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 new file mode 100644 index 00000000000..14569b81924 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,69 @@ +# https://help.github.com/articles/about-codeowners/ + +# Areas are not limited to the filters defined in this file +# First, let's start with areas with no filters or paths + +# Default +* @PowerShell/powershell-maintainers + +# Area: Performance +# @adityapatwardhan + +# Area: Security +src/System.Management.Automation/security/wldpNativeMethods.cs @TravisEz13 @seeminglyscience + +# 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 +# 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: Utility Cmdlets +# src/Microsoft.PowerShell.Commands.Utility/ + +# Area: Console +# src/Microsoft.PowerShell.ConsoleHost/ @daxian-dbw + +# Area: DSC +# src/System.Management.Automation/DscSupport @TravisEz13 @SteveL-MSFT + +# Area: Engine +# src/System.Management.Automation/engine @daxian-dbw + +# Area: Debugging +# Must be below engine to override +# src/System.Management.Automation/engine/debugger/ + +# Area: Help +src/System.Management.Automation/help @adityapatwardhan @daxian-dbw + +# Area: Intellisense +# @daxian-dbw + +# Area: Language +src/System.Management.Automation/engine/parser @daxian-dbw @seeminglyscience + +# Area: Providers +# src/System.Management.Automation/namespaces + +# Area: Remoting +src/System.Management.Automation/engine/remoting @daxian-dbw @TravisEz13 + +# Areas: Build +# Must be last +*.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..797f7003851 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,39 @@ + + +## 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). + +You should receive a response within 24 hours. 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..04c5b6cb58f --- /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@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # 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@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # 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..87b593164a4 --- /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@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # 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 befc0d7a24b..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,44 +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 - -# ignore the version file as it is generated at build time -powershell.version - -# ignore the telemetry semaphore file -DELETE_ME_TO_DISABLE_CONSOLEHOST_TELEMETRY +*.nupkg +*.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..d6ddb53256e --- /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 + + $LTS = $metadata.LTSRelease.PublishToChannels + $Stable = $metadata.StableRelease.PublishToChannels + $isPreview = '$(OutputReleaseTag.releaseTag)' -match '-' + $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\.' + + # 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..b5a15699026 --- /dev/null +++ b/.pipelines/templates/compliance/apiscan.yml @@ -0,0 +1,170 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +jobs: + - job: APIScan + variables: + - 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..0a38ed8f8e6 --- /dev/null +++ b/.pipelines/templates/compliance/generateNotice.yml @@ -0,0 +1,119 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +parameters: + - name: parentJobs + type: jobList + - name: SkipVerifyPackages + type: boolean + +jobs: +- job: generateNotice + variables: + - 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..ea49b9fea5d --- /dev/null +++ b/.pipelines/templates/linux-package-build.yml @@ -0,0 +1,200 @@ +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: 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..97b594830b3 --- /dev/null +++ b/.pipelines/templates/linux.yml @@ -0,0 +1,197 @@ +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: 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: 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..def866173ac --- /dev/null +++ b/.pipelines/templates/mac-package-build.yml @@ -0,0 +1,240 @@ +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: 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..1699207c657 --- /dev/null +++ b/.pipelines/templates/mac.yml @@ -0,0 +1,147 @@ +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: 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..3558c949402 --- /dev/null +++ b/.pipelines/templates/nupkg.yml @@ -0,0 +1,298 @@ +jobs: +- job: build_nupkg + displayName: Package NuPkgs + condition: succeeded() + pool: + type: windows + + variables: + - 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..1f03d65ab21 --- /dev/null +++ b/.pipelines/templates/packaging/windows/package.yml @@ -0,0 +1,224 @@ +parameters: + runtime: x64 + +jobs: +- job: build_win_${{ parameters.runtime }} + displayName: Build Windows Packages ${{ parameters.runtime }} + condition: succeeded() + pool: + type: windows + + variables: + - 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..e644bece68f --- /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.PublishToChannels + $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..a628f4d7127 --- /dev/null +++ b/.pipelines/templates/release-symbols.yml @@ -0,0 +1,89 @@ +parameters: + - name: skipPublish + default: false + type: boolean + +jobs: +- job: PublishSymbols + displayName: Publish Symbols + condition: succeeded() + pool: + type: windows + variables: + - 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..c470af1fd6e --- /dev/null +++ b/.pipelines/templates/release-upload-buildinfo.yml @@ -0,0 +1,149 @@ +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: 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 + $stableReleaseTag = $metadata.StableReleaseTag -Replace 'v','' + + $currentReleaseTag = $buildInfo.ReleaseTag -Replace 'v','' + $stableRelease = $metadata.StableRelease.PublishToChannels + $ltsRelease = $metadata.LTSRelease.PublishToChannels + + Write-Verbose -Verbose "Writing $jsonFile contents:" + $buildInfoJsonContent = Get-Content $jsonFile -Encoding UTF8NoBom -Raw + Write-Verbose -Verbose $buildInfoJsonContent + + $buildInfo = $buildInfoJsonContent | ConvertFrom-Json + $buildInfo.ReleaseDate = $dateTime + $currentReleaseTag = $buildInfo.ReleaseTag -Replace 'v','' + + $targetFile = "$ENV:PIPELINE_WORKSPACE/$fileName" + ConvertTo-Json -InputObject $buildInfo | Out-File $targetFile -Encoding ascii + + if ($fileName -eq "preview.json") { + Set-BuildVariable -Name UploadPreview -Value YES + } else { + Set-BuildVariable -Name UploadPreview -Value NO + } + + Set-BuildVariable -Name PreviewBuildInfoFile -Value $targetFile + + ## Create 'lts.json' if marked as a LTS release. + if ($fileName -eq "stable.json") { + [System.Management.Automation.SemanticVersion] $stableVersion = $stableReleaseTag + [System.Management.Automation.SemanticVersion] $currentVersion = $currentReleaseTag + if ($ltsRelease) { + $ltsFile = "$ENV:PIPELINE_WORKSPACE/lts.json" + Copy-Item -Path $targetFile -Destination $ltsFile -Force + Set-BuildVariable -Name LTSBuildInfoFile -Value $ltsFile + Set-BuildVariable -Name UploadLTS -Value YES + } else { + Set-BuildVariable -Name UploadLTS -Value NO + } + + ## Only update the stable.json if the current version is greater than the stable version. + if ($currentVersion -gt $stableVersion) { + $versionFile = "$ENV:PIPELINE_WORKSPACE/$($currentVersion.Major)-$($currentVersion.Minor).json" + Copy-Item -Path $targetFile -Destination $versionFile -Force + Set-BuildVariable -Name StableBuildInfoFile -Value $versionFile + Set-BuildVariable -Name UploadStable -Value YES + } else { + Set-BuildVariable -Name UploadStable -Value NO + } + + } else { + Set-BuildVariable -Name UploadStable -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 + + #preview + if ($env:UploadPreview -eq 'YES') { + $jsonFile = "$env:PreviewBuildInfoFile" + $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 + } + + #LTS + if ($env:UploadLTS -eq 'YES') { + $jsonFile = "$env:LTSBuildInfoFile" + $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 + } + + #stable + if ($env:UploadStable -eq 'YES') { + $jsonFile = "$env:StableBuildInfoFile" + $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(), or(eq(variables['UploadPreview'], 'YES'), eq(variables['UploadLTS'], 'YES'), eq(variables['UploadStable'], '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..3a6bec4a859 --- /dev/null +++ b/.pipelines/templates/testartifacts.yml @@ -0,0 +1,134 @@ +jobs: +- job: build_testartifacts_win + variables: + - 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: 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..ce7f26131cc --- /dev/null +++ b/.pipelines/templates/uploadToAzure.yml @@ -0,0 +1,431 @@ +jobs: +- job: upload_packages + displayName: Upload packages + condition: succeeded() + pool: + type: windows + variables: + - name: ob_sdl_sbom_enabled + value: true + - 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..70d3dd2df97 --- /dev/null +++ b/.pipelines/templates/variable/release-shared.yml @@ -0,0 +1,40 @@ +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: 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..83c6b32cdfd --- /dev/null +++ b/.pipelines/templates/windows-hosted-build.yml @@ -0,0 +1,323 @@ +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: 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 8c881de6717..cc711f0aa5a 100644 --- a/.spelling +++ b/.spelling @@ -3,854 +3,1755 @@ # 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 +-title +0-powershell-crossplatform +0xfeeddeadbeef +100ms +1redone +1.final +2.x +2ae5d07 +32-bit +4.final +64-bit +AAATechGuy +about_ +about_debuggers +about_jobs +about_Telemetry +about_PSDesiredStateConfiguration +acl +adamdriscoll +add-localgroupmember +add-ons +AddType.cs +adelton +adhoc +aditya +adityapatwardhan +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 alpha.7 alpha.8 alpha.9 -analyzing -AppImage -AppVeyor -artifact -artifacts -AssemblyLoadContext -behavior -behaviors -booleans -catalog -cataloged +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 +beta.3 +beta.4 +beta.5 +beta.6 +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 +checkbox +checksum +chibi +childitem +ChuckieChen945 +ChrisLGardner +chrullrich +cimsession +cimsupport +ci.psm1 +cgmanifest +classlib +clear-itemproperty +cloudydino +cls cmake cmd cmdlet +cmdletproviderclasses cmdlets -color -colors +codebase +codecov.io +codecoverage.zip +codefactor +CodeFormatter +codeowner +codepage +commanddiscovery +CommandInvocationIntrinsics +commandsearch +CommandSearcher +comobject +Compiler.cs +composability +computerinfo +ComRuntimeHelpers.cs config -ConsoleHost -ConvertFrom-Json -CoreCLR -CoreFX +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 -DevOps -DockerFile -DockerFiles -eBook +crossgen'ing +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 -env +enums +Environment.NewLine +ergo3114 +errorrecord +etl +eugenesmlv +EventLogLogProvider +excludeversion exe -favor -favorite +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 +formatfileloading +formatviewbinding +FormatWideCommand +Francisco-Gamino frontload -FullCLR -Get-Acl -Get-AuthenticodeSignature -Get-ChildItem -Get-ComputerInfo -Get-PSSessionConfiguration -Get-WinEvent +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 +GoogleTest +gregsdennis +GUIs +gzip +hackathons +HashData +HashSet hashtable hashtables +hayhay27 +helloworld.ps1 +helpproviderwithcache +helpproviderwithfullcache +helpsystem +hemant +hemantmahawar +Higinbotham +himura2la +hololens homebrew +hostifaces +hostname hotfix -init -Invoke-RestMethod -Invoke-WebRequest +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 +jellyfrog +jen +joandrsn +joeltankam +joeyaiello +jokajak +JohnLBevan +josea +joshuacooper +journalctl +jpsnover json -labeled -lockfile -macOS -Microsoft.PowerShell.Archive -MS-PSRP +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 +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 +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 myget -New-PSSessionOption -New-PSTransportOption -NuGet +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-https +non-nullable +non-r2 +noresume +notcontains nuget -nunit -NUnit +nuget.config +nuget.exe +nugetfeed +Nullable +numberbytes +numberOfPowershellRefAssemblies nupkg -nuspec -OpenSSH -PackageManagement -param -parameterized +oauth +object.ReferenceEquals +offthewoll +oising +omi +omnisharp +OneDrive +oneget.org +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 powershell -PowerShell +powershell-unix +powershell.6 +powershell.com +PowerShell.Common.props +powershell.core.instrumentation powershell.exe -PowerShellGet -ProgramFiles +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 +preview1.22217.1 +preview7 +ProcessorArchitecture +ProductCode +productversion +program.cs +prototyyppi +providername +proxycommand ps1 -PSReadline -Register-EngineEvent -Register-PSSessionConfiguration +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 +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 -ResGen -RFCs -Runspace +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 +rkitover +robo210 +ronn +rpalo +rpolley +runas +runspace +runspaceinit +runspaces +runtime runtimes -SecureString -Set-Acl -Set-AuthenticodeSignature -Set-ExecutionPolicy -ssh -StackOverflow +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 +stevel-msft +StevenLiekens +stevend811 +stknohg +strawgate +streamdescribecifeaturescenariodescribecontextitcontextcontextbeforeallafterallbeforeeachaftereachshould +StrictMode +string.split +stringbuilder +stuntguy3000 +StyleCop +subfolder submodule submodules sudo -TeamCity +superproject +svg +swarfegagit +SwitchParameter +sxs +sydneyhsmith +symlink +symlinks +syscall +syslog +System.IO.Packaging +System.InvalidOperationException +system.manage +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 +timothywlewis +TKey +tobias +tokenizer.cs +tokenizing +tomconte +tommymaynard +toolchain toolset -Unregister-Event -Unregister-PSSessionConfiguration +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 +v141 +v2 +v3 +v4 +v5 +v5.0 +v6 +v6.0. +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 -Win32 -WinRM -WSMan -xUnit - - 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 -hub.docker.com -microsoft - - 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 +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 -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/dev-process/map-json-files.md -build.psm1 -psl-monad -sd -System.Management.Automation.dll - - 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 -DotNet -Interop -MessageAnalyzer -Microsoft.PowerShell.Core -Microsoft.PowerShell.Diagnostics -Microsoft.PowerShell.Management -Microsoft.PowerShell.Security -Microsoft.PowerShell.Utility -NativeExecution -TabCompletion +PSKoans + - docs/testing-guidelines/CodeCoverageAnalysis.md +de5f69c - 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 +_no_ - docs/testing-guidelines/WritingPesterTests.md -FQErrorId -FullyQualifiedErrorId nGet-ContentOut-String nGet-MultiLineString PSDefaultParameterValues-skip ShouldShouldIt -StreamDescribeCIFeatureScenarioDescribeContextItContextContextBeforeAllAfterAllBeforeEachAfterEachshould -TestDrive -throw-testcasesItMockDescribe - - README.md -Gitter -microsoft.com -msi -omnisharp-vscode -opencode -pkg - - 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 --Title -alpha.10 -alpha.11 -alpha.12 -alpha.13 -alpha.14 -alpha.16 -alpha.17 -bool -ContentType -ConvertTo-Html -CoreConsoleHost -crossgen'ing -EXE's -FileCatalog -FilterHashtable -GetParentProcess -hostname -IncludeUserName -InformationRecord -IoT -iSazonov -IsCore -IsCoreCLR -JsonConfigFileAccessor -kittholland -kwiknick -Lee303 -libpsl-native -Microsoft.Management.Infrastructure.Native -Microsoft.PowerShell.LocalAccounts -oneget.org -PetSerAl -powercode -PowerShellProperties -preview1-24530-04 -PSDrive -PseudoParameterBinder -PSReadLine -PVS-Studio -rc2-24027 -rc3-24011 -stdin -StringBuilder -system.manage -TimeZone -TTY's -UserData -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 -XPath -alpha.18 -behavioral -MiaRomero -TheFlyingCorpse -beta.1 -Youtube -globbing -_ -ExecutionPolicy -0xfeeddeadbeef -non-22 -beta.2 -_Jobs -startup -mklement0 -RelationLink -deserialize -beta.3 -shebang -Tadas - - test/tools/CodeCoverageAutomation/README.md -CodeCoverage.zip -Coveralls.exe -Coveralls.io. -Coveralls.net -csmacnz -OpenCover.zip -powershell.version -Start-CodeCoverageRun -tests.zip -v5.0 -v6.0. - - docs/host-powershell/README.md -CorePsAssemblyLoadContext.cs -sample-dotnet1 -NTLM-based -Kerberos-based -preview1-002106-00 -sample-dotnet2 -0-powershell -post-6 + - 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 2dcd41aa546..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,41 +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 - - ./download.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 - -script: - - ulimit -n 4096 - - 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 3c214124503..73ffd17cdb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,480 +1,3 @@ # Changelog -## 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 new file mode 100644 index 00000000000..686e5e7a090 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,10 @@ +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +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 new file mode 100644 index 00000000000..dfc16f830d7 --- /dev/null +++ b/PowerShell.Common.props @@ -0,0 +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 + Microsoft Corporation + (c) Microsoft Corporation. + PowerShell 7 + + 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 110f9783142..a7b31c475f8 100644 --- a/README.md +++ b/README.md @@ -1,190 +1,135 @@ # ![logo][] PowerShell Welcome to the PowerShell GitHub Community! -PowerShell 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. +[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. -[logo]: assets/Powershell_64.png +[logo]: assets/ps_black_64.svg?sanitize=true + +## Windows PowerShell vs. PowerShell 7+ + +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. + +[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.3/PowerShell-6.0.0-beta.3-win10-win2016-x64.msi -[rl-windows81]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.3/PowerShell-6.0.0-beta.3-win81-win2012r2-x64.msi -[rl-windows7-64]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-alpha.18/PowerShell-6.0.0-alpha.18-win7-win2008r2-x64.msi -[rl-windows7-86]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-alpha.18/PowerShell-6.0.0-alpha.18-win7-x86.msi -[rl-ubuntu16]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.3/powershell_6.0.0-beta.3-1ubuntu1.16.04.1_amd64.deb -[rl-ubuntu14]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.3/powershell_6.0.0-beta.3-1ubuntu1.14.04.1_amd64.deb -[rl-centos]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.3/powershell-6.0.0_beta.3-1.el7.x86_64.rpm -[rl-ai]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.3/PowerShell-6.0.0-beta.3-x86_64.AppImage -[rl-macos]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.3/powershell-6.0.0-beta.3-osx.10.12-x64.pkg -[rl-opensuse421]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.3/powershell-6.0.0_beta.3-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 have any problems building, please consult the developer [FAQ][]. +If you are developing .NET Core C# applications targeting PowerShell Core, [check out our FAQ][] to learn more about the PowerShell SDK NuGet package. -### Build status of master branches +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. -| AppVeyor (Windows) | Travis CI (Linux / macOS) | -|--------------------------|--------------------------| -| [![av-image][]][av-site] | [![tv-image][]][tv-site] | +[Contribution Guide]: .github/CONTRIBUTING.md +[check out our FAQ]: docs/FAQ.md#where-do-i-get-the-powershell-core-sdk-package -### Build status of nightly builds +## Building PowerShell -| AppVeyor (Windows) | Travis CI (Linux / macOS) | Code Coverage Status | -|--------------------------|---------------------------|----------------------| -| [![av-nightly-image][]][av-nightly-site] | [![tv-nightly-image][]][tv-site] | [![cc-image][]][cc-site] | +| Linux | Windows | macOS | +|--------------------------|----------------------------|------------------------| +| [Instructions][bd-linux] | [Instructions][bd-windows] | [Instructions][bd-macOS] | + +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://coveralls.io/github/PowerShell/PowerShell?branch=master -[cc-image]: https://coveralls.io/repos/github/PowerShell/PowerShell/badge.svg?branch=master - ## 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: +You can clone the repository: ```sh -git clone --recursive https://github.com/PowerShell/PowerShell.git +git clone https://github.com/PowerShell/PowerShell.git ``` -If you already cloned but forgot to use `--recursive`, you can update submodules manually: +For more information, see [working with the PowerShell repository](https://github.com/PowerShell/PowerShell/tree/master/docs/git). -```sh -git submodule update --init -``` +## Support -See [working with the PowerShell repository](docs/git) for more information. - -## Developing and Contributing +For support, see the [Support Section][]. -Please see the [Contribution Guide][] for how to develop and contribute. - -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. - -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 +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 +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 3cbdb96bfac..00000000000 --- a/appveyor.yml +++ /dev/null @@ -1,28 +0,0 @@ -version: 6.0.0-beta.3-{build} - -image: Visual Studio 2015 - -# cache version - netcoreapp.2.0.0-preview1-002106-00 -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 e4bea5860da..00000000000 --- a/assets/Product.wxs +++ /dev/null @@ -1,90 +0,0 @@ - - - - = 601" ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 5d6488920d8..12121b31dfe 100644 --- a/docs/dev-process/breaking-change-contract.md +++ b/docs/dev-process/breaking-change-contract.md @@ -1,37 +1,41 @@ -#Breaking Changes +# Breaking Changes We have a serious commitment to backwards compatibility with all earlier versions of PowerShell – including the language, cmdlets, APIs and the various protocols (e.g. PowerShell Remoting Protocol) and data formats (e.g. cdxml). 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/map-json-files.md b/docs/dev-process/map-json-files.md deleted file mode 100644 index 89ed47a006c..00000000000 --- a/docs/dev-process/map-json-files.md +++ /dev/null @@ -1,102 +0,0 @@ -# Mapping - -PowerShell/PowerShell utilizes `dotnet cli` project model. -Source code for a library (executable) is located under `src/`. -I.e. System.Management.Automation.dll sources are located under `src/System.Management.Automation` - -In the windows source tree, the files are organized differently. -That's why we use `map.json` files in `src/`. -This file is a simple json hashtable that describes mapping between files in source depot and GitHub. - -* Keys are relative file paths from `psl-monad` (that has the same layout as admin sd enlistment). -* Values are file paths, relative to the corresponding `map.json` folder. - -#### Example - -There is an entry - -``` -"monad/src/engine/COM/ComMethod.cs": "engine/COM/ComMethod.cs", -``` - -in `.\src\System.Management.Automation\map.json`. -It tells us that file `ComMethod.cs` located at `monad/src/engine/COM/ComMethod.cs` in psl-monad (and sd enlistment) is mapped to `src/System.Management.Automation/engine/COM/ComMethod.cs` in PowerShell/PowerShell. - -### build.psm1 - -Our dev module contains a number of functions that can be used to work with this mapping file. - -* `Copy-MappedFiles` -- copies files from psl-monad into PowerShell/PowerShell. Used for "sd -> github" integration. -* `Send-GitDiffToSd` -- applies patch from git to **admin** enlistment with respect to all `map.json` files. - It supports `-WhatIf` switch. - -``` -> Send-GitDiffToSd -diffArg1 32b90c048aa0c5bc8e67f96a98ea01c728c4a5be~1 -diffArg2 32b90c048aa0c5bc8e67f96a98ea01c728c4a5be -AdminRoot d:\e\ps_dev\admin -> cd d:\e\ps_dev\admin -> sd online ... -> # move files to new change list (i.e. with sdb) -> sd submit -c - -``` - -## Updating `map.json` - -If you are bringing new (that are not yet included) files from source-depot, you need to update `map.json` in the corresponding folder to include them. - -This way, we can keep track of changes and have ability to integrate changes back to Source Depot. - -Use this approach for any files from source-depot (including test files) - -## Creating `map.json` - -If you are creating new folder for that, create `map.json` with all mapped files in it. - -* Make a separate commit with update/creation for `map.json`. - Separate commit will help to manage this change. - -``` -> mkdir .\src\My.New.Module -> notepad .\src\My.New.Module\map.json -# add mappings into the file -``` - -* Find current baseline SD change-list in tags: - -``` -> git tag -SD-692351 -SD-693793 -SD/688741 -SD/692351 -SD/693793 -SD/695331 -SD/700586 -SD/704605 -SD/706766 -SD/709766 <--- the last changelist -v0.1.0 -v0.2.0 -v0.3.0 -v0.4.0 -``` - -* Find corresponding commit in psl-monad and check it out. - -``` -> Push-Location ..\psl-monad -> git checkout 85e2ecd -> Pop-Location -``` - -* Use `Copy-MappedFiles` function to copy files on disk. - -``` -> Copy-MappedFiles -PslMonadRoot ..\psl-monad -Path .\src\My.New.Module -``` - -* Make a separate commit with mapped files. - Use `--author="PowerShell Team "` switch to indicate that it's a collective work. - -``` -git commit --author="PowerShell Team " -``` 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 25029327d1e..00000000000 --- a/docs/installation/linux.md +++ /dev/null @@ -1,470 +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.3-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.3-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.3-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.3-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.3-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.3-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.3-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.3-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.3/powershell-6.0.0_beta.3-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.3-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.3-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.3/powershell-6.0.0_beta.3-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.3-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.3-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.3/powershell-6.0.0_beta.3-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.3-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.3-x86_64.AppImage -./PowerShell-6.0.0-beta.3-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 - -Using macOS 10.12, download the PKG package -`powershell-6.0.0-beta.3-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.3-osx.10.12-x64.pkg -target / -``` - -### Uninstallation - macOS 10.12 - -PowerShell on MacOS must be removed manually. - -To remove the installed package: - -```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`. - -[paths]:#paths - -## OpenSSL - -Also install [Homebrew's OpenSSL][openssl]: - -```bash -brew install openssl -brew install curl --with-openssl -``` - -[Homebrew][brew] is the missing package manager for macOS. -If the `brew` command was not found, -you need to install Homebrew following [their instructions][brew]. - -.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. - -**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 -[brew]: http://brew.sh/ -[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.3-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-alpha.18/PowerShell-6.0.0-alpha.18-x86_64.AppImage - -# Make executable -chmod a+x PowerShell-6.0.0-alpha.18-x86_64.AppImage - -# Start PowerShell -./PowerShell-6.0.0-alpha.18-x86_64.AppImage -``` - -### Uninstallation - Kali - -```sh -dpkg -r powershell_6.0.0-beta.3-1ubuntu1.16.04.1_amd64 -``` - -## Paths - -* `$PSHOME` is `/opt/microsoft/powershell/6.0.0-beta.3/` -* 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.3/`, -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 325b7619196..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/en-us/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://my.visualstudio.com/Downloads?pid=2082) 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/en-us/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 and FullCLR 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 d63e995b906..ebba4b02258 100644 --- a/docs/maintainers/README.md +++ b/docs/maintainers/README.md @@ -3,28 +3,39 @@ 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 -2. Merge pull requests -3. 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 + - [Current Repository Maintainers](#current-repository-maintainers) - [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)) +- 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 @@ -32,35 +43,36 @@ 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 1. **MUST** validate that tests and documentation have been written before merging a pull request that contains new functionality 1. **SHOULD** add [the correct labels][issue-management] to issues and pull requests 1. **SHOULD** make sure the correct [Area Experts](../community/governance.md#area-experts) are assigned to relevant pull requests and issues. -This includes adding extra reviewers when it makes sense -(e.g. a pull request that adds remoting capabilities might require a security expert) + This includes adding extra reviewers when it makes sense + (e.g. a pull request that adds remoting capabilities might require a security expert) 1. **SHOULD** validate that the names and email addresses in the git commits reasonably match identity of the person submitting the pull request 1. **SHOULD** make sure contributors are following the [contributor guidelines][CONTRIBUTING] 1. **SHOULD** ask people to resend a pull request, if it [doesn't target `master`](../../.github/CONTRIBUTING.md#lifecycle-of-a-pull-request) 1. **SHOULD** wait for the [CI system][ci-system] build to pass for pull requests -(unless, for instance, the pull request is being submitted to fix broken CI) + (unless, for instance, the pull request is being submitted to fix broken CI) 1. **SHOULD** encourage contributors to refer to issues in their pull request description (e.g. `Resolves issue #123`). -If a user did not create an issue prior to submitting their pull request, their pull request should not be rejected. -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. + If a user did not create an issue prior to submitting their pull request, their pull request should not be rejected. + 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 + Edit the title if necessary to provide clarity on the problem +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 -(unless the CLA bot is broken, and CLA signing can be confirmed through other means) + (unless, for instance, the pull request is being submitted to fix broken CI) +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 -(unless the pull request is particularly urgent for some reason) + Even if the pull request meets all the requirements, people should have time to give their input + (unless the pull request is particularly urgent for some reason) 1. **SHOULD NOT** merge your own pull requests. -If a Repository Maintainer opens a pull request, another Maintainer should merge it unless there are extreme, short-term circumstances requiring a merge or another Maintainer has given explicit sign-off without merging + If a Repository Maintainer opens a pull request, another Maintainer should merge it unless there are extreme, short-term circumstances requiring a merge or another Maintainer has given explicit sign-off without merging ## Issue Management Process @@ -68,43 +80,27 @@ 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. +Please see [Contributing][CONTRIBUTING] -In these cases: +## Maintainer Best Practices -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. -2. 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 [Best Practices][best-practice] ## Becoming a Repository Maintainer -Repository Maintainers currently consist entirely of Microsoft employees. +Repository Maintainers currently consist mostly of Microsoft employees. It is expected that over time, regular trusted contributors to the PowerShell repository will be made Repository Maintainers. -Eligibility is heavily dependent on the level of contribution and expertise: individuals who contribute in meaningful ways to the project will be recognized accordingly. +Eligibility is heavily dependent on the level of contribution and expertise: individuals who contribute consistently in meaningful ways to the project will be recognized accordingly. -At any point in time, a Repository Maintainers can nominate a strong community member to become a Repository Maintainer. -Nominations should be submitted in the form of [RFCs][RFC-repo] detailing why that individual is qualified and how they will contribute. -After the RFC has been discussed, a unanimous vote by the PowerShell Committee will be required for the new Repository Maintainer to be confirmed. +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. +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 \ No newline at end of file +[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 0f7e3ff8f8f..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 him 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 \ No newline at end of file diff --git a/docs/maintainers/releasing.md b/docs/maintainers/releasing.md index b3a5d5c9dd5..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, @@ -57,8 +52,8 @@ not `/home/username/src/PowerShell/...`. ### Packaging Overview -The `build.psm1` module contains a `Start-PSPackage` function to build packages. -It **requires** that PowerShell Core has been built via `Start-PSBuild`. +The `tools/packaging` module contains a `Start-PSPackage` function to build packages. +It **requires** that PowerShell Core has been built via `Start-PSBuild` from the `build.psm1` module. #### Windows @@ -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 @@ -160,37 +162,22 @@ On Windows, the `-Runtime` parameter should be specified for `Start-PSBuild` to # Install dependencies Start-PSBootstrap -Package -# Build for v6.0.0-beta.1 release targeting Windows 10 and Server 2016 -Start-PSBuild -Clean -CrossGen -PSModuleRestore -Runtime win10-x64 -Configuration Release -ReleaseTag v6.0.0-beta.1 +# Build for v6.0.0-beta.1 release targeting Windows universal package, set -Runtime to win7-x64 +Start-PSBuild -Clean -CrossGen -PSModuleRestore -Runtime win7-x64 -Configuration Release -ReleaseTag v6.0.0-beta.1 ``` -If the package is targeting a downlevel Windows (not Windows 10 or Server 2016), -the `-WindowsDownLevel` parameter should be specified for `Start-PSPackage`. -Otherwise, the `-WindowsDownLevel` parameter should be left out. - ```powershell -# Create packages for v6.0.0-beta.1 release targeting Windows 10 and Server 2016. -# When creating packages for downlevel Windows, such as Windows 8.1 or Server 2012R2, -# the parameter '-WindowsDownLevel' must be specified. -Start-PSPackage -Type msi -ReleaseTag v6.0.0-beta.1 <# -WindowsDownLevel win81-x64 #> -Start-PSPackage -Type zip -ReleaseTag v6.0.0-beta.1 <# -WindowsDownLevel win81-x64 #> +# Create packages for v6.0.0-beta.1 release targeting Windows universal package. +# 'win7-x64' / 'win7-x86' should be used for -WindowsRuntime. +Start-PSPackage -Type msi -ReleaseTag v6.0.0-beta.1 -WindowsRuntime 'win7-x64' +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 @@ -205,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'. @@ -215,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/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 2e6d7608c17..5225dd94a22 100755 --- a/docs/testing-guidelines/WritingPesterTests.md +++ b/docs/testing-guidelines/WritingPesterTests.md @@ -1,124 +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, do that in a `try/catch`, and then check `FullyQualifiedErrorId`. 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" { - try - { - get-item "ThisFileCannotPossiblyExist" -ErrorAction Stop - throw "No Exception!" - } - catch - { - $_.FullyQualifiedErrorId | should be "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 because the FQErrorId is wrong. 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 + +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, the build process will 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. + +### 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 -### 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 `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 -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 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. -#### 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 . +The name of the `It` block should expressively state the expectation of the test. -#### 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. +## Admin privileges in tests -### 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: +Tests that require admin privileges **on Windows** must be additionally marked with `RequireAdminOnWindows` Pester tag. -- The pass with exclusion of tests with 'RequireAdminOnWindows' tag -- The pass where we run only 'RequireAdminOnWindows' tests +In the Azure DevOps Windows CI, we run two different passes: + +- 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. + +`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. -#### 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. +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 = @( @@ -129,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 @@ -172,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" } @@ -183,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 @@ -209,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 { @@ -227,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 @@ -253,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 @@ -263,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. @@ -308,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 '@) @@ -318,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 4197f324f90..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,22 +55,46 @@ 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: * `SLOW` indicates that the test takes a somewhat longer time to execute (97% of our `CI` tests take 100ms or less), a test which takes longer than 1 second should be considered as a candidate for being tagged `Slow` +#### 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: + +- 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 @@ -96,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 ``` @@ -120,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..c2af57a3fe4 --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "10.0.102" + } +} 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 c745d8b813a..2e914a2299a 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/AssemblyInfo.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/AssemblyInfo.cs @@ -1,16 +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:AssemblyFileVersionAttribute("1.0.0.0")] -[assembly:AssemblyVersion("1.0.0.0")] - -[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 98098e0f3fd..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,15 +239,14 @@ private void NewSubscriptionResultHandler(object src, CimSubscriptionEventArgs a /// If set EnableRaisingEvents to false, which will be ignored /// /// -#if !CORECLR - [BrowsableAttribute(false)] -#endif + [Browsable(false)] public bool EnableRaisingEvents { get { return enableRaisingEvents; } + set { DebugHelper.WriteLogEx(); @@ -277,6 +257,7 @@ public bool EnableRaisingEvents } } } + private bool enableRaisingEvents; /// @@ -288,7 +269,7 @@ public void Start() { DebugHelper.WriteLogEx(); - lock(myLock) + lock (myLock) { if (status == Status.Default) { @@ -310,6 +291,7 @@ public void Start() this.queryExpression, this.operationTimeout); } + status = Status.Started; } } @@ -333,6 +315,7 @@ public void Stop() DebugHelper.WriteLog("Dispose CimRegisterCimIndication object", 4); this.cimRegisterCimIndication.Dispose(); } + status = Status.Stopped; } } @@ -341,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) @@ -362,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"; @@ -392,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 81d02a3dab5..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,24 +1,13 @@ - - + + - 6.0.0 - netcoreapp2.0 - true - true + PowerShell's Microsoft.Management.Infrastructure.CimCmdlets project + $(NoWarn);CS1570;CS1572;CS1573;CS1574;CS1584;CS1587;CS1591 Microsoft.Management.Infrastructure.CimCmdlets - ../signing/visualstudiopublic.snk - true - false - false - - $(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 5482e4bbb4b..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,57 +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) { -#if CORECLR - //return a dummy string as StackFrame won't be available on CoreCLR - return string.Format(CultureInfo.CurrentUICulture, "{0}::{1} ", "Type", "Method"); -#else - 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); - -#endif + + 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) @@ -246,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() @@ -255,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. /// /// /// @@ -276,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. /// /// /// @@ -299,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. /// /// /// @@ -310,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); } /// @@ -342,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); } - } } } @@ -385,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); } @@ -412,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(); @@ -426,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)); } /// @@ -444,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/map.json b/src/Microsoft.Management.Infrastructure.CimCmdlets/map.json deleted file mode 100644 index b6a1acc7bec..00000000000 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/map.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "wmi/WMIv2/client/CIMCmdlets/CimAsyncOperation.cs" : "CimAsyncOperation.cs", - "wmi/WMIv2/client/CIMCmdlets/CimBaseAction.cs" : "CimBaseAction.cs", - "wmi/WMIv2/client/CIMCmdlets/CimCmdletModuleInitialize.cs" : "CimCmdletModuleInitialize.cs", - "wmi/WMIv2/client/CIMCmdlets/CimCommandBase.cs" : "CimCommandBase.cs", - "wmi/WMIv2/client/CIMCmdlets/CimGetAssociatedInstance.cs" : "CimGetAssociatedInstance.cs", - "wmi/WMIv2/client/CIMCmdlets/CimGetCimClass.cs" : "CimGetCimClass.cs", - "wmi/WMIv2/client/CIMCmdlets/CimGetInstance.cs" : "CimGetInstance.cs", - "wmi/WMIv2/client/CIMCmdlets/CimIndicationWatcher.cs" : "CimIndicationWatcher.cs", - "wmi/WMIv2/client/CIMCmdlets/CimInvokeCimMethod.cs" : "CimInvokeCimMethod.cs", - "wmi/WMIv2/client/CIMCmdlets/CimNewCimInstance.cs" : "CimNewCimInstance.cs", - "wmi/WMIv2/client/CIMCmdlets/CimPromptUser.cs" : "CimPromptUser.cs", - "wmi/WMIv2/client/CIMCmdlets/CimRegisterCimIndication.cs" : "CimRegisterCimIndication.cs", - "wmi/WMIv2/client/CIMCmdlets/CimRemoveCimInstance.cs" : "CimRemoveCimInstance.cs", - "wmi/WMIv2/client/CIMCmdlets/CimResultObserver.cs" : "CimResultObserver.cs", - "wmi/WMIv2/client/CIMCmdlets/CimSessionOperations.cs" : "CimSessionOperations.cs", - "wmi/WMIv2/client/CIMCmdlets/CimSessionProxy.cs" : "CimSessionProxy.cs", - "wmi/WMIv2/client/CIMCmdlets/CimSetCimInstance.cs" : "CimSetCimInstance.cs", - "wmi/WMIv2/client/CIMCmdlets/CimWriteError.cs" : "CimWriteError.cs", - "wmi/WMIv2/client/CIMCmdlets/CimWriteMessage.cs" : "CimWriteMessage.cs", - "wmi/WMIv2/client/CIMCmdlets/CimWriteProgress.cs" : "CimWriteProgress.cs", - "wmi/WMIv2/client/CIMCmdlets/CimWriteResultObject.cs" : "CimWriteResultObject.cs", - "wmi/WMIv2/client/CIMCmdlets/CmdletOperation.cs" : "CmdletOperation.cs", - "wmi/WMIv2/client/CIMCmdlets/GetCimAssociatedInstanceCommand.cs" : "GetCimAssociatedInstanceCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/GetCimClassCommand.cs" : "GetCimClassCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/GetCimInstanceCommand.cs" : "GetCimInstanceCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/GetCimSessionCommand.cs" : "GetCimSessionCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/InvokeCimMethodCommand.cs" : "InvokeCimMethodCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/NewCimInstanceCommand.cs" : "NewCimInstanceCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/NewCimSessionCommand.cs" : "NewCimSessionCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/NewCimSessionOptionCommand.cs" : "NewCimSessionOptionCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/RegisterCimIndicationCommand.cs" : "RegisterCimIndicationCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/RemoveCimInstanceCommand.cs" : "RemoveCimInstanceCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/RemoveCimSessionCommand.cs" : "RemoveCimSessionCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/SetCimInstanceCommand.cs" : "SetCimInstanceCommand.cs", - "wmi/WMIv2/client/CIMCmdlets/Utils.cs" : "Utils.cs", - "wmi/WMIv2/client/CIMCmdlets/Strings.resx" : "resources/Microsoft.Management.Infrastructure.CimCmdlets.Strings.resx" -} \ No newline at end of file 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.PowerShell.GraphicalHost/HelpWindow/HelpParagraphBuilder.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpParagraphBuilder.cs similarity index 75% rename from src/Microsoft.PowerShell.GraphicalHost/HelpWindow/HelpParagraphBuilder.cs rename to src/Microsoft.Management.UI.Internal/HelpWindow/HelpParagraphBuilder.cs index 8947cf62b0e..fdb81a6d416 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/HelpWindow/HelpParagraphBuilder.cs +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpParagraphBuilder.cs @@ -1,46 +1,40 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements HelpParagraphBuilder. -// -//----------------------------------------------------------------------- +// 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 { - using System; - using System.Diagnostics; - using System.Globalization; - using System.Management.Automation; - using System.Text; - using System.Windows.Documents; - /// - /// Builds a help paragraph for a cmdlet + /// Builds a help paragraph for a cmdlet. /// internal class HelpParagraphBuilder : ParagraphBuilder { /// - /// Indentation size + /// Indentation size. /// internal const int IndentSize = 4; /// - /// new line separators + /// new line separators. /// - private static readonly string[] Separators = new string[] { "\r\n", "\n" }; + private static readonly string[] Separators = new[] { "\r\n", "\n" }; /// - /// Object with the cmdelt + /// Object with the cmdelt. /// - private PSObject psObj; + private readonly PSObject psObj; /// - /// Initializes a new instance of the HelpParagraphBuilder class + /// Initializes a new instance of the HelpParagraphBuilder class. /// - /// paragraph being built - /// object with help information + /// Paragraph being built. + /// Object with help information. internal HelpParagraphBuilder(Paragraph paragraph, PSObject psObj) : base(paragraph) { @@ -51,14 +45,19 @@ internal HelpParagraphBuilder(Paragraph paragraph, PSObject psObj) /// /// Enum for category of Help. /// - private enum HelpCategory { Default, DscResource, Class }; + private enum HelpCategory + { + Default, + DscResource, + Class + } /// - /// Gets the string value of a property or null if it could not be retrieved + /// 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 + /// 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"); @@ -73,28 +72,28 @@ internal static string GetPropertyString(PSObject psObj, string propertyName) } /// - /// Adds the help text to the paragraph + /// Adds the help text to the paragraph. /// internal void AddTextToParagraphBuilder() { this.ResetAllText(); - string strCategory = (HelpParagraphBuilder.GetProperty(this.psObj, "Category")).Value.ToString(); + string strCategory = HelpParagraphBuilder.GetProperty(this.psObj, "Category").Value.ToString(); HelpCategory category = HelpCategory.Default; - if (String.Compare(strCategory, "DscResource", StringComparison.OrdinalIgnoreCase) == 0) + if (string.Equals(strCategory, "DscResource", StringComparison.OrdinalIgnoreCase)) { - category = HelpCategory.DscResource; + category = HelpCategory.DscResource; } - else if (String.Compare(strCategory, "Class", StringComparison.OrdinalIgnoreCase) == 0) + else if (string.Equals(strCategory, "Class", StringComparison.OrdinalIgnoreCase)) { category = HelpCategory.Class; } if (HelpParagraphBuilder.GetProperty(this.psObj, "Syntax") == null) { - if(category == HelpCategory.Default) + if (category == HelpCategory.Default) { // if there is no syntax, this is not the standard help // it might be an about page @@ -106,17 +105,17 @@ internal void AddTextToParagraphBuilder() switch (category) { case HelpCategory.Class: - this.AddDescription(HelpWindowSettings.Default.HelpSynopsisDisplayed, HelpWindowResources.SynopsisTitle, "Introduction"); + 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.HelpSynopsisDisplayed, "Synopsis", HelpWindowResources.SynopsisTitle); + 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.HelpSynopsisDisplayed, "Synopsis", HelpWindowResources.SynopsisTitle); + 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); @@ -132,11 +131,11 @@ internal void AddTextToParagraphBuilder() } /// - /// Gets the object property or null if it could not be retrieved + /// 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 + /// 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"); @@ -144,12 +143,12 @@ private static PSPropertyInfo GetProperty(PSObject psObj, string propertyName) } /// - /// Gets a PSObject and then a value from it or null if the value could not be retrieved + /// 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 + /// 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"); @@ -171,11 +170,11 @@ private static string GetInnerPSObjectPropertyString(PSObject psObj, string psOb } /// - /// Gets the value of a property or null if the value could not be retrieved + /// 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 + /// 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"); @@ -192,17 +191,18 @@ private static object GetPropertyObject(PSObject psObj, string propertyName) } catch (ExtendedTypeSystemException) { + // ignore this exception } return value; } /// - /// Gets the text from a property of type PSObject[] where the first object has a text property + /// Gets the text from a property of type PSObject[] where the first object has a text property. /// - /// object 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 + /// 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[]; @@ -215,10 +215,10 @@ private static string GetTextFromArray(PSObject psObj, string propertyText) } /// - /// Returns the largest size of a group of strings + /// Returns the largest size of a group of strings. /// - /// strings to evaluate the largest size from - /// 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; @@ -235,39 +235,39 @@ private static int LargestSize(params string[] strs) } /// - /// Splits the string adding indentation before each line + /// Splits the string adding indentation before each line. /// - /// string to add indentation to - /// the string indented + /// 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 + /// 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 numberOfIndents) + /// 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(' ', numberOfIndents * HelpParagraphBuilder.IndentSize); + indent.Append(' ', numberOfIdents * HelpParagraphBuilder.IndentSize); return HelpParagraphBuilder.AddIndent(str, indent.ToString()); } /// - /// Splits the string adding indentation before each line + /// Splits the string adding indentation before each line. /// - /// string to add indentation to - /// indentation string - /// the string indented + /// String to add indentation to. + /// Indentation string. + /// The string indented. private static string AddIndent(string str, string indentString) { if (str == null) { - return String.Empty; + return string.Empty; } string[] lines = str.Split(Separators, StringSplitOptions.None); @@ -276,7 +276,7 @@ private static string AddIndent(string str, string indentString) foreach (string line in lines) { // Indentation is not localized - returnValue.AppendFormat("{0}{1}\r\n", indentString, line); + returnValue.Append($"{indentString}{line}\r\n"); } if (returnValue.Length > 2) @@ -289,11 +289,11 @@ private static string AddIndent(string str, string indentString) } /// - /// Get the object array value of a property + /// Get the object array value of a property. /// - /// object containing the property - /// property with the array value - /// 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; @@ -304,25 +304,19 @@ private static object[] GetPropertyObjectArray(PSObject obj, string propertyName if (innerObject is PSObject) { - return new object[] { innerObject }; + return new[] { innerObject }; } object[] innerObjectArray = innerObject as object[]; - if (innerObject == null) - { - return null; - } - return innerObjectArray; } - /// - /// Adds a section that contains only a string + /// 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 + /// 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; @@ -338,10 +332,10 @@ private void AddStringSection(bool setting, string sectionName, string sectionTi } /// - /// Adds the help syntax segment + /// Adds the help syntax segment. /// - /// true if it should add the segment - /// title of the section + /// True if it should add the segment. + /// Title of the section. private void AddSyntax(bool setting, string sectionTitle) { PSObject syntaxObject; @@ -375,7 +369,7 @@ private void AddSyntax(bool setting, string sectionTitle) continue; } - string commandStart = String.Format(CultureInfo.CurrentCulture, "{0} ", commandName); + string commandStart = string.Create(CultureInfo.CurrentCulture, $"{commandName} "); this.AddText(HelpParagraphBuilder.AddIndent(commandStart), false); foreach (object parameterObj in parameterObjs) @@ -395,13 +389,14 @@ private void AddSyntax(bool setting, string sectionTitle) continue; } - string parameterType = parameterValue == null ? String.Empty : String.Format(CultureInfo.CurrentCulture, "<{0}>", parameterValue); + string parameterType = parameterValue == null ? string.Empty : string.Create(CultureInfo.CurrentCulture, $"<{parameterValue}>"); string parameterOptionalOpenBrace, parameterOptionalCloseBrace; - if (String.Equals(required, "true", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(required, "true", StringComparison.OrdinalIgnoreCase)) { - parameterOptionalOpenBrace = parameterOptionalCloseBrace = String.Empty; + parameterOptionalOpenBrace = string.Empty; + parameterOptionalCloseBrace = string.Empty; } else { @@ -411,9 +406,9 @@ private void AddSyntax(bool setting, string sectionTitle) string parameterNameOptionalOpenBrace, parameterNameOptionalCloseBrace; - if (String.Equals(position, "named", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(position, "named", StringComparison.OrdinalIgnoreCase)) { - parameterNameOptionalOpenBrace = parameterNameOptionalCloseBrace = String.Empty; + parameterNameOptionalOpenBrace = parameterNameOptionalCloseBrace = string.Empty; } else { @@ -421,25 +416,25 @@ private void AddSyntax(bool setting, string sectionTitle) parameterNameOptionalCloseBrace = "]"; } - string parameterPrefix = String.Format( + string paramterPrefix = string.Format( CultureInfo.CurrentCulture, "{0}{1}-", parameterOptionalOpenBrace, parameterNameOptionalOpenBrace); - this.AddText(parameterPrefix, false); + this.AddText(paramterPrefix, false); this.AddText(parameterName, true); - string parameterSuffix = String.Format( + string paramterSuffix = string.Format( CultureInfo.CurrentCulture, "{0} {1}{2} ", parameterNameOptionalCloseBrace, parameterType, parameterOptionalCloseBrace); - this.AddText(parameterSuffix, false); + this.AddText(paramterSuffix, false); } - string commonParametersText = String.Format( + string commonParametersText = string.Format( CultureInfo.CurrentCulture, "[<{0}>]\r\n\r\n", HelpWindowResources.CommonParameters); @@ -451,11 +446,11 @@ private void AddSyntax(bool setting, string sectionTitle) } /// - /// Adds the help description segment + /// Adds the help description segment. /// - /// true if it should add the segment - /// title of the section - /// propertyName that has description + /// 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; @@ -480,10 +475,10 @@ private void AddDescription(bool setting, string sectionTitle, string propertyNa } /// - /// Adds the help examples segment + /// Adds the help examples segment. /// - /// true if it should add the segment - /// title of the section + /// True if it should add the segment. + /// Title of the section. private void AddExamples(bool setting, string sectionTitle) { if (!setting) @@ -531,7 +526,7 @@ private void AddExamples(bool setting, string sectionTitle) this.AddText("\r\n", false); } - string codeLine = String.Format( + string codeLine = string.Format( CultureInfo.CurrentCulture, "{0}{1}\r\n\r\n", introductionText, @@ -563,17 +558,23 @@ private void AddExamples(bool setting, string sectionTitle) private void AddMembers(bool setting, string sectionTitle) { - if(!setting || String.IsNullOrEmpty(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); @@ -585,13 +586,15 @@ private void AddMembers(bool setting, string sectionTitle) PSObject member = memberObj as PSObject; if (member == null) + { continue; + } string name = GetPropertyString(member, "title"); string type = GetPropertyString(member, "type"); string propertyType = null; - if (String.Compare("field", type, StringComparison.OrdinalIgnoreCase) == 0) + if (string.Equals("field", type, StringComparison.OrdinalIgnoreCase)) { PSObject fieldData = HelpParagraphBuilder.GetPropertyObject(member, "fieldData") as PSObject; @@ -604,17 +607,17 @@ private void AddMembers(bool setting, string sectionTitle) description = GetPropertyString(propertyTypeObject, "description"); } - memberText = String.Format(CultureInfo.CurrentCulture, " [{0}] {1}\r\n", propertyType, name); + memberText = string.Create(CultureInfo.CurrentCulture, $" [{propertyType}] {name}\r\n"); } } - else if (String.Compare("method", type, StringComparison.OrdinalIgnoreCase) == 0) + else if (string.Equals("method", type, StringComparison.OrdinalIgnoreCase)) { FormatMethodData(member, name, out memberText, out description); } - if (!String.IsNullOrEmpty(memberText)) + if (!string.IsNullOrEmpty(memberText)) { - this.AddText(HelpParagraphBuilder.AddIndent(""), false); + this.AddText(HelpParagraphBuilder.AddIndent(string.Empty), false); this.AddText(memberText, true); if (description != null) @@ -628,12 +631,12 @@ private void AddMembers(bool setting, string sectionTitle) } } - private void FormatMethodData(PSObject member, string name, out string memberText, out string description) + private static void FormatMethodData(PSObject member, string name, out string memberText, out string description) { memberText = null; description = null; - if (member == null || String.IsNullOrEmpty(name)) + if (member == null || string.IsNullOrEmpty(name)) { return; } @@ -641,16 +644,18 @@ private void FormatMethodData(PSObject member, string name, out string memberTex string returnType = null; StringBuilder parameterText = new StringBuilder(); - //Get method return type + // 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. + // Get method description. PSObject[] methodDescriptions = HelpParagraphBuilder.GetPropertyObject(member, "introduction") as PSObject[]; if (methodDescriptions != null) { @@ -658,13 +663,15 @@ private void FormatMethodData(PSObject member, string name, out string memberTex { description = GetPropertyString(methodDescription, "Text"); - //If we get an text we do not need to iterate more. - if (!String.IsNullOrEmpty(description)) + // If we get an text we do not need to iterate more. + if (!string.IsNullOrEmpty(description)) + { break; + } } } - //Get method parameters. + // Get method parameters. PSObject parametersObject = HelpParagraphBuilder.GetPropertyObject(member, "parameters") as PSObject; if (parametersObject != null) { @@ -683,33 +690,35 @@ private void FormatMethodData(PSObject member, string name, out string memberTex { parameterType = GetPropertyString(parameterTypeData, "name"); - //If there is no type for the parameter, we expect it is System.Object - if (String.IsNullOrEmpty(parameterType)) + // If there is no type for the parameter, we expect it is System.Object + if (string.IsNullOrEmpty(parameterType)) + { parameterType = "object"; + } } - string paramString = String.Format(CultureInfo.CurrentCulture, "[{0}] ${1},", parameterType, parameterName); + string paramString = string.Create(CultureInfo.CurrentCulture, $"[{parameterType}] ${parameterName},"); parameterText.Append(paramString); } - if (String.Compare(parameterText[parameterText.Length - 1].ToString(), ",", StringComparison.OrdinalIgnoreCase) == 0) + if (string.Equals(parameterText[parameterText.Length - 1].ToString(), ",", StringComparison.OrdinalIgnoreCase)) { parameterText = parameterText.Remove(parameterText.Length - 1, 1); } } } - memberText = String.Format(CultureInfo.CurrentCulture, " [{0}] {1}({2})\r\n", returnType, name, parameterText.ToString()); + memberText = string.Create(CultureInfo.CurrentCulture, $" [{returnType}] {name}({parameterText})\r\n"); } /// - /// Adds the help parameters segment + /// 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 + /// 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) @@ -725,7 +734,7 @@ private void AddParameters(bool setting, string sectionTitle, string paramProper object[] parameterObjects = null; - //Root object for Class has members not parameters. + // Root object for Class has members not parameters. if (helpCategory != HelpCategory.Class) { parameterObjects = HelpParagraphBuilder.GetPropertyObjectArray(parameterRootObject, "parameter"); @@ -756,20 +765,22 @@ private void AddParameters(bool setting, string sectionTitle, string paramProper string defaultValue = GetPropertyString(parameter, "defaultValue"); string acceptWildcard = GetPropertyString(parameter, "globbing"); - if (String.IsNullOrEmpty(name)) + if (string.IsNullOrEmpty(name)) { continue; } - // This syntax string is not localized - if (helpCategory == HelpCategory.DscResource) - this.AddText(HelpParagraphBuilder.AddIndent(""), false); + { + this.AddText(HelpParagraphBuilder.AddIndent(string.Empty), false); + } else + { this.AddText(HelpParagraphBuilder.AddIndent("-"), false); + } this.AddText(name, true); - string parameterText = String.Format( + string parameterText = string.Format( CultureInfo.CurrentCulture, " <{0}>\r\n", parameterValue); @@ -787,19 +798,19 @@ private void AddParameters(bool setting, string sectionTitle, string paramProper int largestSize = HelpParagraphBuilder.LargestSize( HelpWindowResources.ParameterRequired, HelpWindowResources.ParameterPosition, - HelpWindowResources.ParameterDefautValue, + HelpWindowResources.ParameterDefaultValue, HelpWindowResources.ParameterPipelineInput, HelpWindowResources.ParameterAcceptWildcard); // justification of parameter values is not localized - string formatString = String.Format( + string formatString = string.Format( CultureInfo.CurrentCulture, "{{0,-{0}}}{{1}}", largestSize + 2); string tableLine; - tableLine = String.Format( + tableLine = string.Format( CultureInfo.CurrentCulture, formatString, HelpWindowResources.ParameterRequired, @@ -807,10 +818,10 @@ private void AddParameters(bool setting, string sectionTitle, string paramProper this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); this.AddText("\r\n", false); - //these are not applicable for Dsc Resource help + // these are not applicable for Dsc Resource help if (helpCategory != HelpCategory.DscResource) { - tableLine = String.Format( + tableLine = string.Format( CultureInfo.CurrentCulture, formatString, HelpWindowResources.ParameterPosition, @@ -818,15 +829,15 @@ private void AddParameters(bool setting, string sectionTitle, string paramProper this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); this.AddText("\r\n", false); - tableLine = String.Format( + tableLine = string.Format( CultureInfo.CurrentCulture, formatString, - HelpWindowResources.ParameterDefautValue, + HelpWindowResources.ParameterDefaultValue, defaultValue); this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); this.AddText("\r\n", false); - tableLine = String.Format( + tableLine = string.Format( CultureInfo.CurrentCulture, formatString, HelpWindowResources.ParameterPipelineInput, @@ -834,7 +845,7 @@ private void AddParameters(bool setting, string sectionTitle, string paramProper this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); this.AddText("\r\n", false); - tableLine = String.Format( + tableLine = string.Format( CultureInfo.CurrentCulture, formatString, HelpWindowResources.ParameterAcceptWildcard, @@ -849,10 +860,10 @@ private void AddParameters(bool setting, string sectionTitle, string paramProper } /// - /// Adds the help navigation links segment + /// Adds the help navigation links segment. /// - /// true if it should add the segment - /// title of the section + /// True if it should add the segment. + /// Title of the section. private void AddNavigationLink(bool setting, string sectionTitle) { if (!setting) @@ -882,7 +893,7 @@ private void AddNavigationLink(bool setting, string sectionTitle) string text = GetPropertyString(linkObject, "linkText"); string uri = GetPropertyString(linkObject, "uri"); - string linkLine = String.IsNullOrEmpty(uri) ? text : String.Format( + string linkLine = string.IsNullOrEmpty(uri) ? text : string.Format( CultureInfo.CurrentCulture, HelpWindowResources.LinkTextFormat, text, @@ -896,12 +907,12 @@ private void AddNavigationLink(bool setting, string sectionTitle) } /// - /// Adds the help input or output segment + /// Adds the help input or output segment. /// - /// true if it should add the segment - /// title of the section - /// property with the outer object - /// property with the inner object + /// 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) @@ -950,10 +961,10 @@ private void AddInputOrOutputEntries(bool setting, string sectionTitle, string i } /// - /// Adds the help notes segment + /// Adds the help notes segment. /// - /// true if it should add the segment - /// title of the section + /// True if it should add the segment. + /// Title of the section. private void AddNotes(bool setting, string sectionTitle) { if (!setting) diff --git a/src/Microsoft.PowerShell.GraphicalHost/HelpWindow/HelpViewModel.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpViewModel.cs similarity index 75% rename from src/Microsoft.PowerShell.GraphicalHost/HelpWindow/HelpViewModel.cs rename to src/Microsoft.Management.UI.Internal/HelpWindow/HelpViewModel.cs index ab9f6391b8b..7ab8681fef4 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/HelpWindow/HelpViewModel.cs +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpViewModel.cs @@ -1,64 +1,58 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements HelpViewModel. -// -//----------------------------------------------------------------------- +// 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 { - using System; - using System.ComponentModel; - using System.Diagnostics; - using System.Globalization; - using System.Management.Automation; - using System.Windows.Documents; - /// /// ViewModel for the Help Dialog used to: /// build the help document /// search the help document - /// offer text for labels + /// offer text for labels. /// internal class HelpViewModel : INotifyPropertyChanged { /// - /// The builder for the help FlowDocument Paragraph used in a RichEditText control + /// The builder for the help FlowDocument Paragraph used in a RichEditText control. /// - private HelpParagraphBuilder helpBuilder; + private readonly HelpParagraphBuilder helpBuilder; /// - /// Searcher for selecting current matches in paragraph text + /// Searcher for selecting current matches in paragraph text. /// - private ParagraphSearcher searcher; + private readonly ParagraphSearcher searcher; /// - /// Title of the help window + /// Title of the help window. /// - private string helpTitle; + private readonly string helpTitle; /// - /// the zoom bound to the zoom slider value + /// the zoom bound to the zoom slider value. /// private double zoom = 100; /// - /// Text to be found. This is bound to the find TextBox + /// Text to be found. This is bound to the find TextBox. /// private string findText; /// - /// text for the number of matches found + /// text for the number of matches found. /// private string matchesLabel; /// - /// Initializes a new instance of the HelpViewModel class + /// Initializes a new instance of the HelpViewModel class. /// - /// object containing help - /// paragraph in which help text is built/searched + /// Object containing help. + /// Paragraph in which help text is built/searched. internal HelpViewModel(PSObject psObj, Paragraph documentParagraph) { Debug.Assert(psObj != null, "ensured by caller"); @@ -67,8 +61,8 @@ internal HelpViewModel(PSObject psObj, Paragraph documentParagraph) this.helpBuilder = new HelpParagraphBuilder(documentParagraph, psObj); this.helpBuilder.BuildParagraph(); this.searcher = new ParagraphSearcher(); - this.helpBuilder.PropertyChanged += new PropertyChangedEventHandler(this.HelpBuilder_PropertyChanged); - this.helpTitle = String.Format( + this.helpBuilder.PropertyChanged += this.HelpBuilder_PropertyChanged; + this.helpTitle = string.Format( CultureInfo.CurrentCulture, HelpWindowResources.HelpTitleFormat, HelpParagraphBuilder.GetPropertyString(psObj, "name")); @@ -76,13 +70,13 @@ internal HelpViewModel(PSObject psObj, Paragraph documentParagraph) #region INotifyPropertyChanged Members /// - /// Used to notify of property changes + /// Used to notify of property changes. /// public event PropertyChangedEventHandler PropertyChanged; #endregion /// - /// Gets or sets the Zoom bound to the zoom slider value + /// Gets or sets the Zoom bound to the zoom slider value. /// public double Zoom { @@ -101,7 +95,7 @@ public double Zoom } /// - /// Gets the value bound to the RichTextEdit zoom, which is calculated based on the zoom + /// Gets the value bound to the RichTextEdit zoom, which is calculated based on the zoom. /// public double ZoomLevel { @@ -112,18 +106,18 @@ public double ZoomLevel } /// - /// Gets the label to be displayed for the zoom + /// Gets the label to be displayed for the zoom. /// public string ZoomLabel { get { - return String.Format(CultureInfo.CurrentCulture, HelpWindowResources.ZoomLabelTextFormat, this.zoom); + return string.Format(CultureInfo.CurrentCulture, HelpWindowResources.ZoomLabelTextFormat, this.zoom); } } /// - /// Gets or sets the text to be found + /// Gets or sets the text to be found. /// public string FindText { @@ -141,7 +135,7 @@ public string FindText } /// - /// Gets the title of the window + /// Gets the title of the window. /// public string HelpTitle { @@ -152,7 +146,7 @@ public string HelpTitle } /// - /// Gets or sets the label for current matches + /// Gets or sets the label for current matches. /// public string MatchesLabel { @@ -169,7 +163,7 @@ public string MatchesLabel } /// - /// Gets a value indicating whether there are matches to go to + /// Gets a value indicating whether there are matches to go to. /// public bool CanGoToNextOrPrevious { @@ -180,7 +174,7 @@ public bool CanGoToNextOrPrevious } /// - /// Gets the searcher for selecting current matches in paragraph text + /// Gets the searcher for selecting current matches in paragraph text. /// internal ParagraphSearcher Searcher { @@ -188,7 +182,7 @@ internal ParagraphSearcher Searcher } /// - /// Gets the paragraph builder used to write help content + /// Gets the paragraph builder used to write help content. /// internal HelpParagraphBuilder HelpBuilder { @@ -206,7 +200,7 @@ internal void Search() } /// - /// Increases Zoom if not above maximum + /// Increases Zoom if not above maximum. /// internal void ZoomIn() { @@ -217,7 +211,7 @@ internal void ZoomIn() } /// - /// Decreases Zoom if not below minimum + /// Decreases Zoom if not below minimum. /// internal void ZoomOut() { @@ -228,10 +222,10 @@ internal void ZoomOut() } /// - /// Called to update the matches label + /// Called to update the matches label. /// - /// event sender - /// event arguments + /// Event sender. + /// Event arguments. private void HelpBuilder_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "HighlightCount") @@ -242,13 +236,13 @@ private void HelpBuilder_PropertyChanged(object sender, PropertyChangedEventArgs } /// - /// Sets the current matches label + /// Sets the current matches label. /// private void SetMatchesLabel() { if (this.findText == null || this.findText.Trim().Length == 0) { - this.MatchesLabel = String.Empty; + this.MatchesLabel = string.Empty; } else { @@ -264,7 +258,7 @@ private void SetMatchesLabel() } else { - this.MatchesLabel = String.Format( + this.MatchesLabel = string.Format( CultureInfo.CurrentCulture, HelpWindowResources.SomeMatchesFormat, this.HelpBuilder.HighlightCount); @@ -274,9 +268,9 @@ private void SetMatchesLabel() } /// - /// Called internally to notify when a property changed + /// Called internally to notify when a property changed. /// - /// property name + /// Property name. private void OnNotifyPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = this.PropertyChanged; diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/HelpWindow.xaml b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml similarity index 77% rename from src/Microsoft.PowerShell.GraphicalHost/xamls/HelpWindow.xaml rename to src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml index 2d79201a13f..2b2a8fe60b1 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/HelpWindow.xaml +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml @@ -1,7 +1,13 @@ - + + - - + + + Content="{x:Static default:HelpWindowResources.PreviousText}" Click="PreviousMatch_Click"> + Content="{x:Static default:HelpWindowResources.NextText}" Click="NextMatch_Click"> - + - + @@ -63,7 +69,7 @@ - + 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.PowerShell.GraphicalHost/HelpWindow/HelpWindowSettings.Designer.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.Designer.cs similarity index 94% rename from src/Microsoft.PowerShell.GraphicalHost/HelpWindow/HelpWindowSettings.Designer.cs rename to src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.Designer.cs index 0c85dd16838..937429ebf09 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/HelpWindow/HelpWindowSettings.Designer.cs +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.Designer.cs @@ -1,7 +1,6 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -//------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.16598 @@ -11,15 +10,15 @@ // //------------------------------------------------------------------------------ -namespace Microsoft.Management.UI.Internal { - - +// 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; @@ -40,7 +39,7 @@ public bool HelpRemarksDisplayed this["HelpRemarksDisplayed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -52,7 +51,7 @@ public bool HelpSyntaxDisplayed { this["HelpSyntaxDisplayed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -64,19 +63,19 @@ public bool HelpExamplesDisplayed { this["HelpExamplesDisplayed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool HelpSynopsisDisplayed { + public bool HelpSynopsysDisplayed { get { - return ((bool)(this["HelpSynopsisDisplayed"])); + return ((bool)(this["HelpSynopsysDisplayed"])); } set { - this["HelpSynopsisDisplayed"] = value; + this["HelpSynopsysDisplayed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -88,7 +87,7 @@ public bool HelpDescriptionDisplayed { this["HelpDescriptionDisplayed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -100,7 +99,7 @@ public bool HelpParametersDisplayed { this["HelpParametersDisplayed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -112,7 +111,7 @@ public bool HelpInputsDisplayed { this["HelpInputsDisplayed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -124,7 +123,7 @@ public bool HelpOutputsDisplayed { this["HelpOutputsDisplayed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -136,7 +135,7 @@ public bool HelpNotesDisplayed { this["HelpNotesDisplayed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -148,7 +147,7 @@ public bool HelpRelatedLinksDisplayed { this["HelpRelatedLinksDisplayed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -160,7 +159,7 @@ public bool HelpSearchMatchCase { this["HelpSearchMatchCase"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -172,7 +171,7 @@ public bool HelpSearchWholeWord { this["HelpSearchWholeWord"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("400")] @@ -184,7 +183,7 @@ public double HelpWindowHeight { this["HelpWindowHeight"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("600")] @@ -196,7 +195,7 @@ public double HelpWindowWidth { this["HelpWindowWidth"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("-1")] @@ -208,7 +207,7 @@ public double HelpWindowTop { this["HelpWindowTop"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("-1")] @@ -220,7 +219,7 @@ public double HelpWindowLeft { this["HelpWindowLeft"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -232,7 +231,7 @@ public bool HelpWindowMaximized { this["HelpWindowMaximized"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("100")] @@ -245,4 +244,4 @@ public double HelpZoom { } } } -} +//} 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.PowerShell.GraphicalHost/HelpWindow/SettingsDialog.xaml.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml.cs similarity index 78% rename from src/Microsoft.PowerShell.GraphicalHost/HelpWindow/SettingsDialog.xaml.cs rename to src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml.cs index d6930987f1f..5d499d47f71 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/HelpWindow/SettingsDialog.xaml.cs +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml.cs @@ -1,11 +1,5 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements SettingsDialog. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. namespace Microsoft.Management.UI { @@ -13,12 +7,12 @@ namespace Microsoft.Management.UI using Microsoft.Management.UI.Internal; /// - /// Dialog with settings for the help dialog + /// Dialog with settings for the help dialog. /// public partial class SettingsDialog : Window { /// - /// Initializes a new instance of the SettingsDialog class + /// Initializes a new instance of the SettingsDialog class. /// public SettingsDialog() { @@ -31,17 +25,17 @@ public SettingsDialog() this.Parameters.IsChecked = HelpWindowSettings.Default.HelpParametersDisplayed; this.RelatedLinks.IsChecked = HelpWindowSettings.Default.HelpRelatedLinksDisplayed; this.Remarks.IsChecked = HelpWindowSettings.Default.HelpRemarksDisplayed; - this.Synopsis.IsChecked = HelpWindowSettings.Default.HelpSynopsisDisplayed; + 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 + /// Called when the OK button has been clicked. /// - /// event sender - /// event arguments + /// Event sender. + /// Event arguments. private void OK_Click(object sender, RoutedEventArgs e) { HelpWindowSettings.Default.HelpDescriptionDisplayed = this.Description.IsChecked == true; @@ -52,7 +46,7 @@ private void OK_Click(object sender, RoutedEventArgs e) HelpWindowSettings.Default.HelpParametersDisplayed = this.Parameters.IsChecked == true; HelpWindowSettings.Default.HelpRelatedLinksDisplayed = this.RelatedLinks.IsChecked == true; HelpWindowSettings.Default.HelpRemarksDisplayed = this.Remarks.IsChecked == true; - HelpWindowSettings.Default.HelpSynopsisDisplayed = this.Synopsis.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; 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.PowerShell.GraphicalHost/ManagementList/Common/CustomTypeComparer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/CustomTypeComparer.cs similarity index 77% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/CustomTypeComparer.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/CustomTypeComparer.cs index 13a238550e9..6b0db6d5f95 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/CustomTypeComparer.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/CustomTypeComparer.cs @@ -1,20 +1,17 @@ -//----------------------------------------------------------------------- -// -// 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; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Diagnostics; - /// /// 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 + /// with a custom comparer. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public static class CustomTypeComparer @@ -50,7 +47,7 @@ static CustomTypeComparer() public static int Compare(T value1, T value2) where T : IComparable { IComparer comparer; - if (false == TryGetCustomComparer(out comparer)) + if (TryGetCustomComparer(out comparer) == false) { return value1.CompareTo(value2); } @@ -63,12 +60,12 @@ private static bool TryGetCustomComparer(out IComparer comparer) where T : comparer = null; object uncastComparer = null; - if (false == comparers.TryGetValue(typeof(T), out uncastComparer)) + if (comparers.TryGetValue(typeof(T), out uncastComparer) == false) { return false; } - Debug.Assert(uncastComparer is IComparer); + 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.PowerShell.GraphicalHost/ManagementList/Common/DateTimeApproximationComparer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DateTimeApproximationComparer.cs similarity index 79% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/DateTimeApproximationComparer.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/DateTimeApproximationComparer.cs index 636144165da..affbe8ca4ab 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/DateTimeApproximationComparer.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DateTimeApproximationComparer.cs @@ -1,14 +1,11 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - /// /// The DateTimeApproximationComparer is responsible for comparing two /// DateTime objects at a level of precision determined by @@ -59,13 +56,12 @@ private static void GetRoundedValues(DateTime value1, DateTime value2, out DateT private static bool HasTimeComponent(DateTime value) { - bool hasNoTimeComponent = true - && 0 == value.Hour - && 0 == value.Minute - && 0 == value.Second - && 0 == value.Millisecond; + bool hasNoTimeComponent = value.Hour == 0 + && value.Minute == 0 + && value.Second == 0 + && value.Millisecond == 0; - return (!hasNoTimeComponent); + return !hasNoTimeComponent; } } } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/DismissiblePopup.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.Generated.cs similarity index 95% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/DismissiblePopup.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.Generated.cs index 2ea4222e591..3a2768b17b0 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/DismissiblePopup.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -257,7 +247,7 @@ protected virtual void OnSetFocusOnCloseElementChanged(PropertyChangedEventArgs< /// private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/DismissiblePopup.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.cs similarity index 84% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/DismissiblePopup.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.cs index 4e81ad6aa3f..582be1c7ecd 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/DismissiblePopup.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.cs @@ -1,20 +1,17 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ----------------------------------------------------------------------- +// 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 { - using System; - using System.Diagnostics; - using System.Windows; - using System.Windows.Automation; - using System.Windows.Controls.Primitives; - using System.Windows.Input; - using System.Windows.Media; - using System.Windows.Data; - /// /// Partial class implementation for DismissiblePopup control. /// @@ -49,7 +46,6 @@ protected override void OnOpened(EventArgs e) this.SetupAutomationIdBinding(); } - /// /// Responds when the value of the IsOpen property changes from to true to false. /// @@ -109,14 +105,14 @@ private FrameworkElement FindPopupRoot() { DependencyObject element = this.Child; - while (false == element.GetType().Name.Equals("PopupRoot", StringComparison.Ordinal)) + while (element.GetType().Name.Equals("PopupRoot", StringComparison.Ordinal) == false) { - element = VisualTreeHelper.GetParent( element ); + element = VisualTreeHelper.GetParent(element); } - Debug.Assert(element != null ); + Debug.Assert(element != null, "element not null"); - return (FrameworkElement) element; + return (FrameworkElement)element; } /// diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs similarity index 85% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs index 10ac0f006c9..0f269df5b75 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs @@ -1,23 +1,13 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Provides a base automation peer for FrameworkElement controls. -// -//----------------------------------------------------------------------- +// 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 { - #region Using Directives - - using System.Diagnostics.CodeAnalysis; - using System.Windows; - using System.Windows.Automation.Peers; - using System.Windows.Controls; - - #endregion - /// /// Provides a base automation peer for FrameworkElement controls. /// 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.PowerShell.GraphicalHost/ManagementList/Common/IntegralConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IntegralConverter.cs similarity index 76% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/IntegralConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/IntegralConverter.cs index 7c01b5a0f1c..27c45ef288b 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/IntegralConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IntegralConverter.cs @@ -1,19 +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.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Text; +using System.Windows; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Text; - using System.Windows.Data; - using System.Diagnostics.CodeAnalysis; - using System.Windows; - using System.Globalization; - /// /// Takes a value and returns the largest value which is a integral amount of the second value. /// @@ -34,12 +31,9 @@ public class IntegralConverter : IMultiValueConverter /// public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - if (values == null) - { - throw new ArgumentNullException("values"); - } + ArgumentNullException.ThrowIfNull(values); - if (2 != values.Length) + if (values.Length != 2) { throw new ArgumentException("Two values expected", "values"); } @@ -50,14 +44,14 @@ public object Convert(object[] values, Type targetType, object parameter, System return DependencyProperty.UnsetValue; } - var source = (double) values[0]; + var source = (double)values[0]; var factor = (double)values[1]; double padding = 0; if (parameter != null) { - padding = Double.Parse((string)parameter, CultureInfo.InvariantCulture); + padding = double.Parse((string)parameter, CultureInfo.InvariantCulture); } var newSource = source - padding; @@ -71,7 +65,6 @@ public object Convert(object[] values, Type targetType, object parameter, System var result = newSource - remainder; return result; - } /// diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/InverseBooleanConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/InverseBooleanConverter.cs similarity index 77% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/InverseBooleanConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/InverseBooleanConverter.cs index accc2215e84..efefb08bae9 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/InverseBooleanConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/InverseBooleanConverter.cs @@ -1,14 +1,11 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Windows.Data; - /// /// Takes a bool value and returns the inverse. /// @@ -25,10 +22,7 @@ public class InverseBooleanConverter : IValueConverter /// The inverted boolean value. public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - if (value == null) - { - throw new ArgumentNullException("value"); - } + ArgumentNullException.ThrowIfNull(value); var boolValue = (bool)value; 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.PowerShell.GraphicalHost/ManagementList/Common/IsNotNullConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsNotNullConverter.cs similarity index 81% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/IsNotNullConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/IsNotNullConverter.cs index ba8825426d0..265e0266c53 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/IsNotNullConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsNotNullConverter.cs @@ -1,13 +1,11 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Windows.Data; + namespace Microsoft.Management.UI.Internal { - using System; - using System.Windows.Data; - /// /// The IsNotNullConverter is responsible for converting a value into /// a boolean indicting whether the value is not null. @@ -27,7 +25,7 @@ public class IsNotNullConverter : IValueConverter /// Returns true if value is not null, false otherwise. public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - return (null != value); + return value != null; } /// diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/KeyboardHelp.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs similarity index 84% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/KeyboardHelp.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs index dded7361de9..386b33996c1 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/KeyboardHelp.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.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.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Windows; +using System.Windows.Input; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Text; - using System.Windows.Input; - using System.Diagnostics; - using System.Windows; - internal enum LogicalDirection { None, @@ -30,7 +27,7 @@ internal static class KeyboardHelp /// The logical direction. public static LogicalDirection GetLogicalDirection(DependencyObject element, Key key) { - Debug.Assert(element != null); + Debug.Assert(element != null, "element not null"); bool rightToLeft = IsElementRightToLeft(element); @@ -69,7 +66,7 @@ public static LogicalDirection GetLogicalDirection(DependencyObject element, Key /// The focus direction. public static FocusNavigationDirection GetNavigationDirection(DependencyObject element, Key key) { - Debug.Assert(element != null); + Debug.Assert(element != null, "element not null"); Debug.Assert(IsFlowDirectionKey(key)); bool rightToLeft = IsElementRightToLeft(element); @@ -109,10 +106,10 @@ public static FocusNavigationDirection GetNavigationDirection(DependencyObject e /// /// Determines if the control key is pressed. /// - /// True if a control is is pressed. + /// True if a control is pressed. public static bool IsControlPressed() { - if (ModifierKeys.Control == (Keyboard.Modifiers & ModifierKeys.Control)) + if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) { return true; } @@ -145,7 +142,7 @@ private static bool IsFlowDirectionKey(Key key) private static bool IsElementRightToLeft(DependencyObject element) { FlowDirection flowDirection = FrameworkElement.GetFlowDirection(element); - bool rightToLeft = (flowDirection == FlowDirection.RightToLeft); + bool rightToLeft = flowDirection == FlowDirection.RightToLeft; return rightToLeft; } } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizer.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.Generated.cs similarity index 97% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizer.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.Generated.cs index dcda8d26ee3..799cd260e12 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizer.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.Generated.cs @@ -1,7 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. // // // This code was generated by a tool. DO NOT EDIT @@ -9,7 +7,6 @@ // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // -// ----------------------------------------------------------------------- #region StyleCop Suppression - generated code using System; @@ -346,7 +343,7 @@ protected virtual void OnItemsSourceChanged(PropertyChangedEventArgs /// Identifies the NoItemsText dependency property. /// - public static readonly DependencyProperty NoItemsTextProperty = DependencyProperty.Register( "NoItemsText", typeof(string), typeof(ListOrganizer), new PropertyMetadata( String.Empty, NoItemsTextProperty_PropertyChanged) ); + public static readonly DependencyProperty NoItemsTextProperty = DependencyProperty.Register( "NoItemsText", typeof(string), typeof(ListOrganizer), new PropertyMetadata( string.Empty, NoItemsTextProperty_PropertyChanged) ); /// /// Gets or sets a value that appears to inform the user that there are no items in the list. @@ -395,7 +392,7 @@ protected virtual void OnNoItemsTextChanged(PropertyChangedEventArgs e) /// /// Identifies the TextContentPropertyName dependency property. /// - public static readonly DependencyProperty TextContentPropertyNameProperty = DependencyProperty.Register( "TextContentPropertyName", typeof(string), typeof(ListOrganizer), new PropertyMetadata( String.Empty, TextContentPropertyNameProperty_PropertyChanged) ); + public static readonly DependencyProperty TextContentPropertyNameProperty = DependencyProperty.Register( "TextContentPropertyName", typeof(string), typeof(ListOrganizer), new PropertyMetadata( string.Empty, TextContentPropertyNameProperty_PropertyChanged) ); /// /// Gets or sets a value which dictates what binding is used to provide content for the items in the list. @@ -443,7 +440,7 @@ protected virtual void OnTextContentPropertyNameChanged(PropertyChangedEventArgs /// private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.cs similarity index 75% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizer.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.cs index 5ee026ec5bd..7f8a0f8bfc4 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizer.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.cs @@ -1,17 +1,14 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// 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; namespace Microsoft.Management.UI.Internal { - using System.Windows; - using System.Windows.Controls; - using System.Windows.Input; - using System; - using System.Diagnostics.CodeAnalysis; - /// /// Picker control that displays a list with basic editing functionality. /// @@ -45,7 +42,7 @@ protected override void OnKeyDown(KeyEventArgs e) partial void OnSelectItemExecutedImplementation(ExecutedRoutedEventArgs e) { - if (null == e.Parameter) + if (e.Parameter == null) { throw new ArgumentException("e.Parameter is null", "e"); } @@ -56,7 +53,7 @@ partial void OnSelectItemExecutedImplementation(ExecutedRoutedEventArgs e) partial void OnDeleteItemExecutedImplementation(ExecutedRoutedEventArgs e) { - if (null == e.Parameter) + if (e.Parameter == null) { throw new ArgumentException("e.Parameter is null", "e"); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizerItem.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizerItem.Generated.cs similarity index 93% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizerItem.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizerItem.Generated.cs index 6b0d5d8abd6..e51172095a1 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizerItem.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizerItem.Generated.cs @@ -1,7 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. // // // This code was generated by a tool. DO NOT EDIT @@ -9,7 +7,6 @@ // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // -// ----------------------------------------------------------------------- #region StyleCop Suppression - generated code using System; @@ -58,7 +55,7 @@ partial class ListOrganizerItem /// /// Identifies the TextContentPropertyName dependency property. /// - public static readonly DependencyProperty TextContentPropertyNameProperty = DependencyProperty.Register( "TextContentPropertyName", typeof(string), typeof(ListOrganizerItem), new PropertyMetadata( String.Empty, TextContentPropertyNameProperty_PropertyChanged) ); + public static readonly DependencyProperty TextContentPropertyNameProperty = DependencyProperty.Register( "TextContentPropertyName", typeof(string), typeof(ListOrganizerItem), new PropertyMetadata( string.Empty, TextContentPropertyNameProperty_PropertyChanged) ); /// /// Gets or sets a value which dictates what binding is used to provide content for the items in the list. @@ -106,7 +103,7 @@ protected virtual void OnTextContentPropertyNameChanged(PropertyChangedEventArgs /// private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizerItem.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizerItem.cs similarity index 83% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizerItem.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizerItem.cs index 9456a457cbd..24ff69bc35b 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ListOrganizerItem.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizerItem.cs @@ -1,19 +1,16 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; namespace Microsoft.Management.UI.Internal { - using System; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Data; - using System.Windows.Input; - using System.ComponentModel; - using System.Diagnostics.CodeAnalysis; - /// /// Partial class implementation for ListOrganizerItem control. /// @@ -38,7 +35,7 @@ public bool IsInEditMode { get { - return (null != this.renameButton) ? this.renameButton.IsChecked.Value : false; + return (this.renameButton != null) ? this.renameButton.IsChecked.Value : false; } } @@ -49,7 +46,7 @@ public void Select() { if (!this.IsLoaded) { - this.Loaded += new RoutedEventHandler(this.ListOrganizerItem_Loaded_SelectItem); + this.Loaded += this.ListOrganizerItem_Loaded_SelectItem; this.ApplyTemplate(); return; } @@ -156,10 +153,10 @@ private void RevertTextAndChangeFromEditToDisplayMode() private void ChangeFromEditToDisplayMode() { // NOTE : This is to resolve a race condition where clicking - // on the rename button causes the the edit box to change and + // on the rename button causes the edit box to change and // then have re-toggle. DependencyObject d = Mouse.DirectlyOver as DependencyObject; - if (null == d || !(this.renameButton.IsAncestorOf(d) && Mouse.LeftButton == MouseButtonState.Pressed)) + if (d == null || !(this.renameButton.IsAncestorOf(d) && Mouse.LeftButton == MouseButtonState.Pressed)) { this.renameButton.IsChecked = false; } @@ -192,12 +189,12 @@ private void UpdateTextContentBindings() { if (!this.IsLoaded) { - this.Loaded += new RoutedEventHandler(this.ListOrganizerItem_Loaded_UpdateTextContentBindings); + this.Loaded += this.ListOrganizerItem_Loaded_UpdateTextContentBindings; this.ApplyTemplate(); return; } - if (!String.IsNullOrEmpty(this.TextContentPropertyName)) + if (!string.IsNullOrEmpty(this.TextContentPropertyName)) { Binding b = new Binding(this.TextContentPropertyName); b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; @@ -233,13 +230,13 @@ partial void PostOnApplyTemplate() private void AttachToVisualTree() { this.editBox.IsVisibleChanged += new DependencyPropertyChangedEventHandler(this.EditBox_IsVisibleChanged); - this.editBox.KeyDown += new KeyEventHandler(this.EditBox_KeyDown); - this.editBox.LostFocus += new RoutedEventHandler(this.EditBox_LostFocus); + this.editBox.KeyDown += this.EditBox_KeyDown; + this.editBox.LostFocus += this.EditBox_LostFocus; this.templatedParent = this.TemplatedParent as FrameworkElement; - if (null != this.templatedParent) + if (this.templatedParent != null) { - this.templatedParent.KeyDown += new KeyEventHandler(this.TemplatedParent_OnKeyDown); + this.templatedParent.KeyDown += this.TemplatedParent_OnKeyDown; } } @@ -252,7 +249,7 @@ private void DetachFromVisualTree() this.editBox.LostFocus -= this.EditBox_LostFocus; } - if (null != this.templatedParent) + if (this.templatedParent != null) { this.templatedParent.KeyDown -= this.TemplatedParent_OnKeyDown; } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/MessageTextBox.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/MessageTextBox.Generated.cs similarity index 89% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/MessageTextBox.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/MessageTextBox.Generated.cs index e0d91c95ff7..e204886880a 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/MessageTextBox.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/MessageTextBox.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -20,7 +10,7 @@ namespace Microsoft.Management.UI.Internal { /// - /// A TextBox which shows a user provided text when it's empty. + /// A TextBox which shows a user provided text when its empty. /// [Localizability(LocalizationCategory.None)] partial class MessageTextBox @@ -132,7 +122,7 @@ protected virtual void OnIsBackgroundTextShownChanged(PropertyChangedEventArgs private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/MessageTextBox.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/MessageTextBox.cs new file mode 100644 index 00000000000..741d93a07d3 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/MessageTextBox.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Partial class implementation for MessageTextBox control. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public partial class MessageTextBox : TextBox + { + static partial void StaticConstructorImplementation() + { + TextProperty.OverrideMetadata( + typeof(MessageTextBox), + new FrameworkPropertyMetadata( + string.Empty, + null, + new CoerceValueCallback(OnTextBoxTextCoerce))); + } + + #region Non-Public Methods + + private void UpdateIsBackgroundTextShown(string text) + { + if (string.IsNullOrEmpty(text) == false && this.IsBackgroundTextShown) + { + this.IsBackgroundTextShown = false; + } + else if (string.IsNullOrEmpty(text) && this.IsBackgroundTextShown == false) + { + this.IsBackgroundTextShown = true; + } + } + + private static object OnTextBoxTextCoerce(DependencyObject o, object baseValue) + { + MessageTextBox mtb = (MessageTextBox)o; + + mtb.UpdateIsBackgroundTextShown((string)baseValue); + + if (baseValue == null) + { + return string.Empty; + } + + return baseValue; + } + + #endregion Non-Public Methods + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PickerBase.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/PickerBase.Generated.cs similarity index 94% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PickerBase.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/PickerBase.Generated.cs index d11b427537c..f8b8bec0a5a 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PickerBase.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/PickerBase.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -224,7 +214,7 @@ protected virtual void OnIsOpenChanged(PropertyChangedEventArgs e) /// private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/PickerBase.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/PickerBase.cs new file mode 100644 index 00000000000..bb4b6c61511 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/PickerBase.cs @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Threading; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Implements a re-usable base component useful for showing + /// Picker-like controls. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public partial class PickerBase : HeaderedContentControl + { + /// + /// Creates a new instance of the PickerBase class. + /// + public PickerBase() + { + // empty + } + + partial void OnCloseDropDownExecutedImplementation(ExecutedRoutedEventArgs e) + { + this.IsOpen = false; + } + + #region DropDownButtonTemplate Changed + + partial void OnDropDownButtonTemplateChangedImplementation(PropertyChangedEventArgs e) + { + this.ApplyDropDownButtonTemplate(); + } + + private void ApplyDropDownButtonTemplate() + { + if (!this.IsLoaded) + { + this.ApplyTemplate(); + this.Loaded += this.PickerBase_Loaded_ApplyDropDownButtonTemplate; + return; + } + + if (this.DropDownButtonTemplate != null && !ReferenceEquals(this.dropDownButton.Template, this.DropDownButtonTemplate)) + { + this.dropDownButton.Template = this.DropDownButtonTemplate; + } + } + + private void PickerBase_Loaded_ApplyDropDownButtonTemplate(object sender, RoutedEventArgs e) + { + this.Loaded -= this.PickerBase_Loaded_ApplyDropDownButtonTemplate; + this.ApplyDropDownButtonTemplate(); + } + + #endregion DropDownButtonTemplate Changed + + #region DropDown IsOpen Handlers + + private void DropDown_Opened(object sender, EventArgs e) + { + this.FocusDropDown(); + } + + private void FocusDropDown() + { + if (!this.dropDown.IsLoaded) + { + this.dropDown.Loaded += this.DropDown_Loaded_FocusDropDown; + } + + if (this.dropDown.Child != null && !this.dropDown.IsAncestorOf((DependencyObject)Keyboard.FocusedElement)) + { + this.dropDown.Child.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); + } + } + + private void DropDown_Loaded_FocusDropDown(object sender, RoutedEventArgs e) + { + this.Loaded -= this.DropDown_Loaded_FocusDropDown; + this.FocusDropDown(); + } + + private void DropDown_Closed(object sender, EventArgs e) + { + if (this.dropDown.IsKeyboardFocusWithin || Keyboard.FocusedElement == null) + { + this.dropDownButton.Focus(); + } + } + + #endregion DropDown IsOpen Handlers + + #region Apply Template + + partial void PostOnApplyTemplate() + { + this.AttachToVisualTree(); + this.ApplyDropDownButtonTemplate(); + } + + partial void PreOnApplyTemplate() + { + this.DetachFromVisualTree(); + } + + private void AttachToVisualTree() + { + this.dropDown.Opened += this.DropDown_Opened; + this.dropDown.Closed += this.DropDown_Closed; + } + + private void DetachFromVisualTree() + { + if (this.dropDown != null) + { + this.dropDown.Opened -= this.DropDown_Opened; + this.dropDown.Closed -= this.DropDown_Closed; + } + } + + #endregion Apply Template + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PopupControlButton.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/PopupControlButton.Generated.cs similarity index 83% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PopupControlButton.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/PopupControlButton.Generated.cs index 1096c7a5e88..c358ce69985 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PopupControlButton.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/PopupControlButton.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -82,7 +72,7 @@ protected virtual void OnIsPopupOpenChanged(PropertyChangedEventArgs e) /// private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PopupControlButton.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/PopupControlButton.cs similarity index 86% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PopupControlButton.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/PopupControlButton.cs index f759f35fee8..b02ea20e07a 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PopupControlButton.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/PopupControlButton.cs @@ -1,26 +1,31 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Threading; namespace Microsoft.Management.UI.Internal { - using System; - using System.ComponentModel; - using System.Windows; - using System.Windows.Controls.Primitives; - using System.Diagnostics; - using System.Windows.Threading; - using System.Windows.Input; - /// /// Partial class implementation for PopupControlButton control. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public partial class PopupControlButton : ExpanderButton { - bool isClickInProgress = false; + private bool isClickInProgress = false; + + /// + /// Tooltip to show to expand. + /// + protected override string ExpandToolTip + { + get { return XamlLocalizableResources.AutoResXGen_ManagementList2_ToolTip_132; } + } /// /// Constructs an instance of PopupControlButton. @@ -100,7 +105,7 @@ partial void OnIsPopupOpenChangedImplementation(PropertyChangedEventArgs e } } - if (false == this.isClickInProgress) + if (this.isClickInProgress == false) { this.UpdateIsChecked(); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PropertyChangedEventArgs.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/PropertyChangedEventArgs.cs similarity index 79% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PropertyChangedEventArgs.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/PropertyChangedEventArgs.cs index bb6fe1e108d..7d601bf8e26 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PropertyChangedEventArgs.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/PropertyChangedEventArgs.cs @@ -1,8 +1,6 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + namespace Microsoft.Management.UI.Internal { using System; diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ReadOnlyObservableAsyncCollection.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ReadOnlyObservableAsyncCollection.cs similarity index 84% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ReadOnlyObservableAsyncCollection.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/ReadOnlyObservableAsyncCollection.cs index 0134973e4a4..99f08931aa4 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ReadOnlyObservableAsyncCollection.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ReadOnlyObservableAsyncCollection.cs @@ -1,17 +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.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Collections.Specialized; - using System.ComponentModel; - /// /// Represents a read-only ObservableCollection which also implement IAsyncProgress. /// @@ -37,8 +34,8 @@ public ReadOnlyObservableAsyncCollection(IList list) { this.asyncProgress = list as IAsyncProgress; - ((INotifyCollectionChanged)this.Items).CollectionChanged += new NotifyCollectionChangedEventHandler(this.HandleCollectionChanged); - ((INotifyPropertyChanged)this.Items).PropertyChanged += new PropertyChangedEventHandler(this.HandlePropertyChanged); + ((INotifyCollectionChanged)this.Items).CollectionChanged += this.HandleCollectionChanged; + ((INotifyPropertyChanged)this.Items).PropertyChanged += this.HandlePropertyChanged; } #endregion Constructors @@ -47,7 +44,7 @@ public ReadOnlyObservableAsyncCollection(IList list) /// Occurs when the collection changes, either by adding or removing an item. /// /// - /// see + /// see /// public event NotifyCollectionChangedEventHandler CollectionChanged; @@ -55,7 +52,7 @@ public ReadOnlyObservableAsyncCollection(IList list) /// Occurs when a property changes. /// /// - /// see + /// see /// public event PropertyChangedEventHandler PropertyChanged; #endregion Events diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ScalableImage.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ScalableImage.Generated.cs similarity index 82% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ScalableImage.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/ScalableImage.Generated.cs index 458bd71df05..6d682160e49 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ScalableImage.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ScalableImage.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -80,7 +70,7 @@ protected virtual void OnSourceChanged(PropertyChangedEventArgs private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } @@ -97,7 +87,7 @@ private void RaisePropertyChangedEvent(EventHandler protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() { - return new ExtendedFrameworkElementAutomationPeer(this,AutomationControlType.Image); + return new ExtendedFrameworkElementAutomationPeer(owner: this, controlType: AutomationControlType.Image, isControlElement: false); } } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ScalableImage.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ScalableImage.cs similarity index 81% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ScalableImage.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/ScalableImage.cs index 9b6e30e73d4..ea5f91adc87 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ScalableImage.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ScalableImage.cs @@ -1,27 +1,16 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Represents an image that can render as a vector or as a bitmap. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Automation; +using System.Windows.Automation.Peers; +using System.Windows.Data; +using System.Windows.Media; namespace Microsoft.Management.UI.Internal { - #region Using Directives - - using System; - using System.Diagnostics.CodeAnalysis; - using System.Windows; - using System.Windows.Automation; - using System.Windows.Automation.Peers; - using System.Windows.Data; - using System.Windows.Media; - - - #endregion - /// /// Partial class implementation for ScalableImage control. /// @@ -86,11 +75,11 @@ protected override void OnRender(DrawingContext drawingContext) } /// - /// Override of . + /// Override of . /// Make this control to respect the ClipToBounds attribute value. /// /// An instance of used for calculating an additional clip. - /// Geometry to use as an additional clip in case when element is larger than available space + /// Geometry to use as an additional clip in case when element is larger than available space. protected override Geometry GetLayoutClip(Size layoutSlotSize) { return ClipToBounds ? base.GetLayoutClip(layoutSlotSize) : null; @@ -105,13 +94,13 @@ partial void OnSourceChangedImplementation(PropertyChangedEventArgs -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -179,7 +169,7 @@ protected virtual void OnImageChanged(PropertyChangedEventArgs e) /// /// Identifies the Size dependency property. /// - public static readonly DependencyProperty SizeProperty = DependencyProperty.Register( "Size", typeof(Size), typeof(ScalableImageSource), new PropertyMetadata( new Size(Double.NaN, Double.NaN), SizeProperty_PropertyChanged) ); + public static readonly DependencyProperty SizeProperty = DependencyProperty.Register( "Size", typeof(Size), typeof(ScalableImageSource), new PropertyMetadata( new Size(double.NaN, double.NaN), SizeProperty_PropertyChanged) ); /// /// Gets or sets the suggested size of the image. @@ -227,7 +217,7 @@ protected virtual void OnSizeChanged(PropertyChangedEventArgs e) /// private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/ScalableImageSource.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ScalableImageSource.cs new file mode 100644 index 00000000000..5f88400d63b --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ScalableImageSource.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Partial class implementation for SeparatedList control. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public partial class ScalableImageSource : Freezable + { + #region Structors + + /// + /// Initializes a new instance of the class. + /// + public ScalableImageSource() + { + // This constructor intentionally left blank + } + + #endregion + + #region Overrides + + /// + /// Creates a new instance of the Freezable derived class. + /// + /// The new instance of the Freezable derived class. + protected override Freezable CreateInstanceCore() + { + return new ScalableImageSource(); + } + + #endregion Overrides + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/StateDescriptor.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/StateDescriptor.cs similarity index 82% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/StateDescriptor.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/StateDescriptor.cs index d7cbc19d139..b1a82c0d079 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/StateDescriptor.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/StateDescriptor.cs @@ -1,21 +1,17 @@ -//----------------------------------------------------------------------- -// -// 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.Text; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Text; - using System.Diagnostics.CodeAnalysis; - /// /// Base proxy class for other classes which wish to have save and restore functionality. /// /// There are no restrictions on T. - [Serializable] [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public abstract class StateDescriptor { diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/StringFormatConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/StringFormatConverter.cs similarity index 75% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/StringFormatConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/StringFormatConverter.cs index 48d9f6ffb46..d8cf0b253aa 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/StringFormatConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/StringFormatConverter.cs @@ -1,15 +1,12 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Globalization; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Globalization; - using System.Windows.Data; - /// /// Formatting string with a given format. /// @@ -26,19 +23,16 @@ public class StringFormatConverter : IValueConverter /// The formatted string. public object Convert(object value, Type targetType, Object parameter, CultureInfo culture) { - if (parameter == null) - { - throw new ArgumentNullException("parameter"); - } + ArgumentNullException.ThrowIfNull(parameter); string str = (string)value; string formatString = (string)parameter; - if (String.IsNullOrEmpty(str)) + if (string.IsNullOrEmpty(str)) { return null; } - return String.Format(culture, formatString, str); + return string.Format(culture, formatString, str); } /// diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/TextBlockService.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/TextBlockService.Generated.cs similarity index 93% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/TextBlockService.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/TextBlockService.Generated.cs index cf1d88e2382..9270c8f7914 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/TextBlockService.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/TextBlockService.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -146,7 +136,7 @@ static private void IsTextTrimmedMonitoringEnabledProperty_PropertyChanged(Depen /// /// Identifies the UntrimmedText dependency property. /// - public static readonly DependencyProperty UntrimmedTextProperty = DependencyProperty.RegisterAttached( "UntrimmedText", typeof(string), typeof(TextBlockService), new PropertyMetadata( String.Empty, UntrimmedTextProperty_PropertyChanged) ); + public static readonly DependencyProperty UntrimmedTextProperty = DependencyProperty.RegisterAttached( "UntrimmedText", typeof(string), typeof(TextBlockService), new PropertyMetadata( string.Empty, UntrimmedTextProperty_PropertyChanged) ); /// /// Gets the untrimmed text. diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/TextBlockService.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/TextBlockService.cs new file mode 100644 index 00000000000..888c03944a2 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/TextBlockService.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Attached property provider to control. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public static partial class TextBlockService + { + static partial void IsTextTrimmedMonitoringEnabledProperty_PropertyChangedImplementation(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + TextBlock tb = o as TextBlock; + if (tb == null) + { + return; + } + + if ((bool)e.OldValue) + { + tb.SizeChanged -= OnTextBlockSizeChanged; + } + else + { + tb.SizeChanged += OnTextBlockSizeChanged; + } + } + + private static void OnTextBlockSizeChanged(object sender, SizeChangedEventArgs e) + { + var textBlock = (TextBlock)sender; + UpdateIsTextTrimmed(textBlock); + } + + private static void OnTextBlockPropertyChanged(object sender, EventArgs e) + { + var textBlock = (TextBlock)sender; + UpdateIsTextTrimmed(textBlock); + } + + private static void UpdateIsTextTrimmed(TextBlock textBlock) + { + Debug.Assert(textBlock != null, "textblock not null"); + + if (textBlock.TextWrapping != TextWrapping.NoWrap || textBlock.TextTrimming == TextTrimming.None) + { + SetIsTextTrimmed(textBlock, false); + } + else + { + SetIsTextTrimmed(textBlock, CalculateIsTextTrimmed(textBlock)); + } + } + + private static bool CalculateIsTextTrimmed(TextBlock textBlock) + { + if (!textBlock.IsArrangeValid) + { + return GetIsTextTrimmed(textBlock); + } + + Typeface typeface = new Typeface( + textBlock.FontFamily, + textBlock.FontStyle, + textBlock.FontWeight, + textBlock.FontStretch); + +#pragma warning disable 612, 618 + // FormattedText is used to measure the whole width of the text held up by TextBlock container + FormattedText formattedText = new FormattedText( + textBlock.Text, + System.Threading.Thread.CurrentThread.CurrentCulture, + textBlock.FlowDirection, + typeface, + textBlock.FontSize, + textBlock.Foreground); +#pragma warning restore 612, 618 + + return formattedText.Width > textBlock.ActualWidth; + } + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/TextTrimConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/TextTrimConverter.cs similarity index 75% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/TextTrimConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/TextTrimConverter.cs index 6ee99a18a47..1ea4bf3b96f 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/TextTrimConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/TextTrimConverter.cs @@ -1,20 +1,16 @@ -//----------------------------------------------------------------------- -// -// 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.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Windows.Data; - using System.Collections; - using System.Diagnostics.CodeAnalysis; - /// /// Removes whitespace at beginning and end of a string. /// @@ -33,7 +29,7 @@ public TextTrimConverter() /// /// Trims excess whitespace from the given string. /// - /// original string + /// Original string. /// The parameter is not used. /// The parameter is not used. /// The parameter is not used. @@ -57,7 +53,7 @@ private static object TrimValue(object value) /// /// Trims extra whitespace from the given string during backward conversion. /// - /// original string + /// Original string. /// The parameter is not used. /// The parameter is not used. /// The parameter is not used. diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/Utilities.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/Utilities.cs similarity index 82% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/Utilities.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/Utilities.cs index 6e1baf7b0b3..9cc60c8411c 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/Utilities.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/Utilities.cs @@ -1,29 +1,19 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Provides common methods for use in the library. -// -//----------------------------------------------------------------------- +// 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.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; namespace Microsoft.Management.UI.Internal { - #region Using Directives - - using System; - using System.Collections; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using System.Text; - using System.Windows; - using System.Windows.Media; - using System.Windows.Controls; - - #endregion - #region UserActionState enum /// @@ -93,14 +83,11 @@ public static class Utilities /// The specified value is a null reference. public static bool AreAllItemsOfType(IEnumerable items) { - if (items == null) - { - throw new ArgumentNullException("items"); - } + ArgumentNullException.ThrowIfNull(items); foreach (object item in items) { - if (!(item is T)) + if (item is not T) { return false; } @@ -118,10 +105,7 @@ public static bool AreAllItemsOfType(IEnumerable items) /// The specified value is a null reference. public static T Find(this IEnumerable items) { - if (items == null) - { - throw new ArgumentNullException("items"); - } + ArgumentNullException.ThrowIfNull(items); foreach (object item in items) { @@ -169,8 +153,8 @@ public static string NullCheckTrim(string value) /// /// Parameter is not generic to type T /// since it may be a collection of a subclass of type T, - /// and IEnumerable's subclass is not compatible with - /// IEnumerable's baseclass. + /// and IEnumerable'subclass is not compatible with + /// IEnumerable'baseclass. /// public static void ResortObservableCollection( ObservableCollection modify, @@ -181,7 +165,7 @@ public static void ResortObservableCollection( { T sortedObject = (T)obj; int foundIndex = modify.IndexOf(sortedObject); - if (0 <= foundIndex) + if (foundIndex >= 0) { modify.Move(foundIndex, orderedPosition); orderedPosition++; @@ -196,4 +180,4 @@ public static void ResortObservableCollection( } #endregion -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/VisualToAncestorDataConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/VisualToAncestorDataConverter.cs similarity index 76% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/VisualToAncestorDataConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/VisualToAncestorDataConverter.cs index 2b327990dee..868e9a9b25b 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/VisualToAncestorDataConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/VisualToAncestorDataConverter.cs @@ -1,17 +1,14 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Globalization; +using System.Reflection; +using System.Windows; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Globalization; - using System.Reflection; - using System.Windows; - using System.Windows.Data; - /// /// Provides a way to get the of a visual ancestor. /// @@ -30,19 +27,13 @@ public class VisualToAncestorDataConverter : IValueConverter /// The specified value is a null reference. public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (value == null) - { - throw new ArgumentNullException("value"); - } + ArgumentNullException.ThrowIfNull(value); - if (parameter == null) - { - throw new ArgumentNullException("parameter"); - } + ArgumentNullException.ThrowIfNull(parameter); Type dataType = (Type)parameter; - if (false == dataType.IsClass) + if (dataType.IsClass == false) { throw new ArgumentException("The specified value is not a class type.", "parameter"); } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/WeakEventListener.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/WeakEventListener.cs new file mode 100644 index 00000000000..d005dd909ee --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/WeakEventListener.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Windows; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// A common weak event listener which can be used for different kinds of events. + /// + /// The EventArgs type for the event. + internal class WeakEventListener : IWeakEventListener where TEventArgs : EventArgs + { + private EventHandler realHander; + + /// + /// Constructs an instance of WeakEventListener. + /// + /// The handler for the event. + public WeakEventListener(EventHandler handler) + { + ArgumentNullException.ThrowIfNull(handler); + + this.realHander = handler; + } + + /// + /// Receives events from the centralized event manager. + /// + /// The type of the WeakEventManager calling this method. + /// Object that originated the event. + /// Event data. + /// + /// true if the listener handled the event. It is considered an error by the WeakEventManager handling in WPF to register a listener for an event that the listener does not handle. Regardless, the method should return false if it receives an event that it does not recognize or handle. + /// + public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) + { + TEventArgs realArgs = (TEventArgs)e; + + this.realHander(sender, realArgs); + + return true; + } + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/WpfHelp.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/WpfHelp.cs similarity index 89% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/WpfHelp.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/Common/WpfHelp.cs index 46732db0b59..628723b089a 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/WpfHelp.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/WpfHelp.cs @@ -1,30 +1,24 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Provides common WPF methods for use in the library. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Windows; +using System.Windows.Automation; +using System.Windows.Automation.Peers; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Threading; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.Windows; - using System.Windows.Controls; - using System.Diagnostics; - using System.Windows.Media; - using System.Windows.Data; - using System.ComponentModel; - using System.Windows.Controls.Primitives; - using System.Windows.Threading; - using System.Windows.Automation; - using System.Windows.Automation.Peers; - using System.Windows.Input; - /// /// Defines a method which will be called when /// a condition is met. @@ -71,7 +65,7 @@ public static bool RetryActionAfterLoaded(FrameworkElement element, RetryActi data.Enqueue(callback, parameter); - element.Loaded += new RoutedEventHandler(Element_Loaded); + element.Loaded += Element_Loaded; element.ApplyTemplate(); return true; @@ -144,7 +138,7 @@ public bool IsEmpty { get { - return (0 == this.callbacks.Count); + return this.callbacks.Count == 0; } } } @@ -159,10 +153,7 @@ public bool IsEmpty /// The specified value does not have a parent that supports removal. public static void RemoveFromParent(FrameworkElement element) { - if (element == null) - { - throw new ArgumentNullException("element"); - } + ArgumentNullException.ThrowIfNull(element); // If the element has already been detached, do nothing \\ if (element.Parent == null) @@ -221,15 +212,9 @@ public static void RemoveFromParent(FrameworkElement element) /// The specified value does not have a parent that supports removal. public static void AddChild(FrameworkElement parent, FrameworkElement element) { - if (element == null) - { - throw new ArgumentNullException("element"); - } + ArgumentNullException.ThrowIfNull(element); - if (parent == null) - { - throw new ArgumentNullException("element"); - } + ArgumentNullException.ThrowIfNull(parent, nameof(element)); ContentControl parentContentControl = parent as ContentControl; @@ -277,7 +262,7 @@ public static void AddChild(FrameworkElement parent, FrameworkElement element) /// Returns an object of type T if found, otherwise null. public static T GetVisualChild(DependencyObject obj) where T : DependencyObject { - if (null == obj) + if (obj == null) { return null; } @@ -290,7 +275,7 @@ public static T GetVisualChild(DependencyObject obj) where T : DependencyObje var element = elementQueue.Dequeue(); T item = element as T; - if (null != item) + if (item != null) { return item; } @@ -305,7 +290,6 @@ public static T GetVisualChild(DependencyObject obj) where T : DependencyObje return null; } - /// /// Finds all children of type within the specified object's visual tree. /// @@ -317,10 +301,8 @@ public static List FindVisualChildren(DependencyObject obj) where T : DependencyObject { Debug.Assert(obj != null, "obj is null"); - if (obj == null) - { - throw new ArgumentNullException("obj"); - } + + ArgumentNullException.ThrowIfNull(obj); List childrenOfType = new List(); @@ -355,10 +337,7 @@ public static List FindVisualChildren(DependencyObject obj) public static T FindVisualAncestorData(this DependencyObject obj) where T : class { - if (obj == null) - { - throw new ArgumentNullException("obj"); - } + ArgumentNullException.ThrowIfNull(obj); FrameworkElement parent = obj.FindVisualAncestor(); @@ -388,10 +367,7 @@ public static T FindVisualAncestorData(this DependencyObject obj) /// The specified value is a null reference. public static T FindVisualAncestor(this DependencyObject @object) where T : class { - if (@object == null) - { - throw new ArgumentNullException("object"); - } + ArgumentNullException.ThrowIfNull(@object, nameof(@object)); DependencyObject parent = VisualTreeHelper.GetParent(@object); @@ -420,10 +396,7 @@ public static T FindVisualAncestor(this DependencyObject @object) where T : c /// The specified value is a null reference. public static bool TryExecute(this RoutedCommand command, object parameter, IInputElement target) { - if (command == null) - { - throw new ArgumentNullException("command"); - } + ArgumentNullException.ThrowIfNull(command); if (command.CanExecute(parameter, target)) { @@ -444,15 +417,8 @@ public static bool TryExecute(this RoutedCommand command, object parameter, IInp /// The reference to the child, or null if the template part wasn't found. public static T GetOptionalTemplateChild(Control templateParent, string childName) where T : FrameworkElement { - if (templateParent == null) - { - throw new ArgumentNullException("templateParent"); - } - - if (String.IsNullOrEmpty(childName)) - { - throw new ArgumentNullException("childName"); - } + ArgumentNullException.ThrowIfNull(templateParent); + ArgumentException.ThrowIfNullOrEmpty(childName); object templatePart = templateParent.Template.FindName(childName, templateParent); T item = templatePart as T; @@ -474,7 +440,7 @@ public static T GetOptionalTemplateChild(Control templateParent, string child /// The reference to the child. public static T GetTemplateChild(Control templateParent, string childName) where T : FrameworkElement { - T item = GetOptionalTemplateChild( templateParent, childName ); + T item = GetOptionalTemplateChild(templateParent, childName); if (item == null) { @@ -491,7 +457,7 @@ public static T GetTemplateChild(Control templateParent, string childName) wh /// The name of the expected template part. private static void HandleWrongTemplatePartType(string name) { - throw new ApplicationException(String.Format( + throw new ApplicationException(string.Format( CultureInfo.CurrentCulture, "A template part with the name of '{0}' is not of type {1}.", name, @@ -505,7 +471,7 @@ private static void HandleWrongTemplatePartType(string name) /// The name of the expected template part. public static void HandleMissingTemplatePart(string name) { - throw new ApplicationException(String.Format( + throw new ApplicationException(string.Format( CultureInfo.CurrentCulture, "A template part with the name of '{0}' and type of {1} was not found.", name, @@ -573,10 +539,7 @@ public static RoutedPropertyChangedEventArgs CreateRoutedPropertyChangedEvent /// The specified index is not valid for the specified collection. public static void ChangeIndex(ItemCollection items, object item, int newIndex) { - if (items == null) - { - throw new ArgumentNullException("items"); - } + ArgumentNullException.ThrowIfNull(items); if (!items.Contains(item)) { diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/AutomationGroup.cs b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/AutomationGroup.cs new file mode 100644 index 00000000000..1341c12a25c --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/AutomationGroup.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Represents a decorator that is always visible in the automation tree, indicating that its descendents belong to a logical group. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class AutomationGroup : ContentControl + { + /// + /// Returns the implementations for this control. + /// + /// The implementations for this control. + protected override AutomationPeer OnCreateAutomationPeer() + { + return new ExtendedFrameworkElementAutomationPeer(this, AutomationControlType.Group, true); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ExpanderButton.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ExpanderButton.Generated.cs new file mode 100644 index 00000000000..2429db7b734 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ExpanderButton.Generated.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#region StyleCop Suppression - generated code +using System; +using System.ComponentModel; +using System.Windows; + +namespace Microsoft.Management.UI.Internal +{ + + /// + /// Represents a toggle button used to expand or collapse elements. + /// + [Localizability(LocalizationCategory.None)] + partial class ExpanderButton + { + // + // CreateAutomationPeer + // + /// + /// Create an instance of the AutomationPeer. + /// + /// + /// An instance of the AutomationPeer. + /// + protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() + { + return new ExpanderButtonAutomationPeer(this); + } + + } +} +#endregion diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ExpanderButton.cs b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ExpanderButton.cs new file mode 100644 index 00000000000..b641811444e --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ExpanderButton.cs @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Windows; +using System.Windows.Automation; +using System.Windows.Automation.Peers; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Represents a toggle button used to expand or collapse elements. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public partial class ExpanderButton : ToggleButton + { + /// + /// Tooltip to show to expand. + /// + protected virtual string ExpandToolTip + { + get { return XamlLocalizableResources.AutoResXGen_ManagementList2_ToolTip_32; } + } + + /// + /// Tooltip to show to collapse. + /// + protected virtual string CollapseToolTip + { + get { return XamlLocalizableResources.CollapsingTabControl_ExpandButton_AutomationName; } + } + + /// + /// Initializes a new instance of the class. + /// + public ExpanderButton() + { + // This constructor intentionally left blank + } + + /// + /// Invoked whenever the effective value of any dependency property on this has been updated. The specific dependency property that changed is reported in the arguments parameter. Overrides . + /// + /// The event data that describes the property that changed, as well as old and new values. + protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + + if (e.Property == ExpanderButton.IsCheckedProperty) + { + this.OnIsCheckedChanged(e); + } + } + + /// + /// Called when the property changes. + /// + /// The event data that describes the property that changed, as well as old and new values. + protected void OnIsCheckedChanged(DependencyPropertyChangedEventArgs args) + { + if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged)) + { + var peer = UIElementAutomationPeer.CreatePeerForElement(this); + + if (peer != null) + { + var oldValue = (bool?)args.OldValue; + var newValue = (bool?)args.NewValue; + + peer.RaisePropertyChangedEvent( + ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, + (oldValue == true) ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed, + (newValue == true) ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed); + } + } + + SetToolTip(); + ToolTip toolTip = (ToolTip)this.ToolTip; + if (toolTip.IsOpen) + { + // need to reset so content changes if already open + toolTip.IsOpen = false; + toolTip.IsOpen = true; + } + } + + /// + /// Called when it has keyboard focus. + /// + /// The event data that describes getting keyboard focus. + protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs args) + { + SetToolTip(); + ((ToolTip)this.ToolTip).IsOpen = true; + } + + /// + /// Called when it lost keyboard focus. + /// + /// The event data that describes losing keyboard focus. + protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs args) + { + if (this.ToolTip is ToolTip toolTip) + { + toolTip.IsOpen = false; + } + } + + private void SetToolTip() + { + ToolTip toolTip; + if (this.ToolTip is ToolTip) + { + toolTip = (ToolTip)this.ToolTip; + } + else + { + toolTip = new ToolTip(); + } + + toolTip.Content = (this.IsChecked == true) ? CollapseToolTip : ExpandToolTip; + toolTip.PlacementTarget = this; + toolTip.Placement = PlacementMode.Bottom; + this.ToolTip = toolTip; + } + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ExpanderButtonAutomationPeer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ExpanderButtonAutomationPeer.cs similarity index 84% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ExpanderButtonAutomationPeer.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ExpanderButtonAutomationPeer.cs index 28445aecca0..3f6f2a3888b 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ExpanderButtonAutomationPeer.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ExpanderButtonAutomationPeer.cs @@ -1,20 +1,13 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// 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.Automation.Provider; namespace Microsoft.Management.UI.Internal { - #region Using Directives - - using System.Diagnostics.CodeAnalysis; - using System.Windows.Automation; - using System.Windows.Automation.Peers; - using System.Windows.Automation.Provider; - - #endregion - /// /// Provides an automation peer for . /// @@ -79,7 +72,7 @@ ExpandCollapseState IExpandCollapseProvider.ExpandCollapseState { get { - if (true == this.expanderButton.IsChecked) + if (this.expanderButton.IsChecked == true) { return ExpandCollapseState.Expanded; } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/Resizer.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/Resizer.Generated.cs similarity index 96% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/Resizer.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/Resizer.Generated.cs index ae7b063a66c..5e2c18129e6 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/Resizer.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/Resizer.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -380,7 +370,7 @@ protected virtual void OnVisibleGripWidthChanged(PropertyChangedEventArgs private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/Resizer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/Resizer.cs similarity index 89% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/Resizer.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/Resizer.cs index 6247d7b2cf0..19c0671bf21 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/Resizer.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/Resizer.cs @@ -1,19 +1,16 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Documents; namespace Microsoft.Management.UI.Internal { - using System; - using System.ComponentModel; - using System.Diagnostics; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Controls.Primitives; - using System.Windows.Documents; - /// /// The resize grip possibilities. /// @@ -54,12 +51,12 @@ internal static Thickness CreateGripThickness(double visibleGripWidth, ResizeGri { Thickness thickness; - if (visibleGripWidth < 0.0 || Double.IsNaN(visibleGripWidth)) + if (visibleGripWidth < 0.0 || double.IsNaN(visibleGripWidth)) { - throw new ArgumentOutOfRangeException("visibleGripWidth", "The value must be greater than or equal to 0." ); + throw new ArgumentOutOfRangeException("visibleGripWidth", "The value must be greater than or equal to 0."); } - if (Double.IsInfinity(visibleGripWidth)) + if (double.IsInfinity(visibleGripWidth)) { throw new ArgumentOutOfRangeException("visibleGripWidth", "The value must be less than infinity."); } @@ -73,13 +70,12 @@ internal static Thickness CreateGripThickness(double visibleGripWidth, ResizeGri thickness = new Thickness(visibleGripWidth, 0, 0, 0); break; default: - throw new InvalidEnumArgumentException("gripLocation", (int) gripLocation, typeof(ResizeGripLocation)); + throw new InvalidEnumArgumentException("gripLocation", (int)gripLocation, typeof(ResizeGripLocation)); } return thickness; } - partial void PreOnApplyTemplate() { if (this.rightGrip != null) @@ -173,7 +169,7 @@ private void PerformDrag(ResizeGripLocation location, DragDeltaEventArgs e) private void StartDragging(ResizeGripLocation location) { - if (false == this.ResizeWhileDragging) + if (this.ResizeWhileDragging == false) { if (this.adornerLayer == null) { @@ -189,7 +185,7 @@ private void StartDragging(ResizeGripLocation location) private void StopDragging(ResizeGripLocation location, DragCompletedEventArgs e) { - if (false == this.ResizeWhileDragging) + if (this.ResizeWhileDragging == false) { this.RemoveAdorner(); double newWidth = this.GetNewWidth(location, e.HorizontalChange); @@ -216,7 +212,7 @@ private double GetHorizontalDelta(ResizeGripLocation location, double horzDelta) } else { - Debug.Assert(location == ResizeGripLocation.Left); + Debug.Assert(location == ResizeGripLocation.Left, "location is left"); realDelta = -horzDelta; } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ResizerGripThicknessConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ResizerGripThicknessConverter.cs similarity index 77% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ResizerGripThicknessConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ResizerGripThicknessConverter.cs index 23ab8bba2be..c371a6391b5 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ResizerGripThicknessConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/ResizerGripThicknessConverter.cs @@ -1,16 +1,13 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Globalization; - using System.Windows.Data; - using System.Windows; - /// /// A converter which creates the proper thickness for the content of the Resizer, depending on the grip visual size /// and grip position. @@ -41,13 +38,10 @@ public ResizerGripThicknessConverter() /// A converted value. If the method returns nullNothingnullptra null reference (Nothing in Visual Basic), the valid null value is used. public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - if (values == null) - { - throw new ArgumentNullException("values"); - } + ArgumentNullException.ThrowIfNull(values); - if (Object.ReferenceEquals(values[0], DependencyProperty.UnsetValue) || - Object.ReferenceEquals(values[1], DependencyProperty.UnsetValue)) + if (object.ReferenceEquals(values[0], DependencyProperty.UnsetValue) || + object.ReferenceEquals(values[1], DependencyProperty.UnsetValue)) { return DependencyProperty.UnsetValue; } @@ -67,7 +61,7 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur /// The converter parameter to use. /// The culture to use in the converter. /// A converted values. If the method returns nullNothingnullptra null reference (Nothing in Visual Basic), the valid null value is used. - public object[] ConvertBack( object value, Type[] targetTypes, object parameter, CultureInfo culture) + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/UIElementAdorner.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/UIElementAdorner.Generated.cs similarity index 81% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/UIElementAdorner.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/UIElementAdorner.Generated.cs index 2e99380195e..7e60d4c6ffc 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/UIElementAdorner.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/UIElementAdorner.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -69,7 +59,7 @@ static private void ChildProperty_PropertyChanged(DependencyObject o, Dependency private void RaiseChildChanged(PropertyChangedEventArgs e) { var eh = this.ChildChanged; - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/UIElementAdorner.cs b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/UIElementAdorner.cs similarity index 88% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/UIElementAdorner.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/UIElementAdorner.cs index f3e7a652d0b..987a1170890 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/UIElementAdorner.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/CommonControls/UIElementAdorner.cs @@ -1,24 +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.Linq; using System.Text; using System.Windows; -using System.Windows.Media; using System.Windows.Documents; +using System.Windows.Media; namespace Microsoft.Management.UI.Internal { /// /// Partial class implementation for UIElementAdorner. /// - partial class UIElementAdorner : Adorner + internal partial class UIElementAdorner : Adorner { - VisualCollection children; + private VisualCollection children; /// /// Constructs an instance of UIElementAdorner. @@ -33,7 +31,7 @@ public UIElementAdorner(UIElement adornedElement) /// /// Overrides Visual.GetVisualChild, and returns a child at the specified index from a collection of child elements. /// - /// The zero-based index of the requested child element in the collection. + /// The zero-based index of the requested child element in the collection.. /// The requested child element. This should not return null; if the provided index is out of range, an exception is thrown. protected override Visual GetVisualChild(int index) { @@ -54,7 +52,7 @@ protected override int VisualChildrenCount /// /// Implements any custom measuring behavior for the popupAdorner. /// - /// A size to constrain the popupAdorner to. + /// A size to constrain the popupAdorner to.. /// A Size object representing the amount of layout space needed by the popupAdorner. protected override Size MeasureOverride(Size constraint) { @@ -87,7 +85,6 @@ protected override Size ArrangeOverride(Size finalSize) { return base.ArrangeOverride(finalSize); } - } partial void OnChildChangedImplementation(PropertyChangedEventArgs e) diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/DefaultFilterRuleCustomizationFactory.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/DefaultFilterRuleCustomizationFactory.cs similarity index 79% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/DefaultFilterRuleCustomizationFactory.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/DefaultFilterRuleCustomizationFactory.cs index cf45c559010..cd5e40a8bd9 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/DefaultFilterRuleCustomizationFactory.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/DefaultFilterRuleCustomizationFactory.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.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Globalization; +using System.Reflection; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Diagnostics; - using System.Reflection; - using System.Globalization; - /// /// The BuiltinDataErrorInfoValidationRuleFactory creates default settings for the /// builtin FilterRules. @@ -36,12 +33,10 @@ public override IPropertyValueGetter PropertyValueGetter return this.propertyValueGetter; } + set { - if (null == value) - { - throw new ArgumentNullException("value"); - } + ArgumentNullException.ThrowIfNull(value); this.propertyValueGetter = value; } @@ -74,7 +69,7 @@ public override ICollection CreateDefaultFilterRulesForPropertyValue rules.Add(new IsEmptyFilterRule()); rules.Add(new IsNotEmptyFilterRule()); } - else if (t == typeof(Boolean)) + else if (t == typeof(bool)) { rules.Add(new EqualsFilterRule()); } @@ -108,15 +103,9 @@ public override ICollection CreateDefaultFilterRulesForPropertyValue /// public override void TransferValues(FilterRule oldRule, FilterRule newRule) { - if (null == oldRule) - { - throw new ArgumentNullException("oldRule"); - } + ArgumentNullException.ThrowIfNull(oldRule); - if (null == newRule) - { - throw new ArgumentNullException("newRule"); - } + ArgumentNullException.ThrowIfNull(newRule); if (this.TryTransferValuesAsSingleValueComparableValueFilterRule(oldRule, newRule)) { @@ -132,10 +121,7 @@ public override void TransferValues(FilterRule oldRule, FilterRule newRule) /// public override void ClearValues(FilterRule rule) { - if (null == rule) - { - throw new ArgumentNullException("rule"); - } + ArgumentNullException.ThrowIfNull(rule); if (this.TryClearValueFromSingleValueComparableValueFilterRule(rule)) { @@ -165,34 +151,30 @@ public override void ClearValues(FilterRule rule) /// public override string GetErrorMessageForInvalidValue(string value, Type typeToParseTo) { - if (null == typeToParseTo) - { - throw new ArgumentNullException("typeToParseTo"); - } - - bool isNumericType = false - || typeToParseTo == typeof(Byte) - || typeToParseTo == typeof(SByte) - || typeToParseTo == typeof(Int16) - || typeToParseTo == typeof(UInt16) - || typeToParseTo == typeof(Int32) - || typeToParseTo == typeof(UInt32) - || typeToParseTo == typeof(Int64) - || typeToParseTo == typeof(UInt64) + ArgumentNullException.ThrowIfNull(typeToParseTo); + + bool isNumericType = typeToParseTo == typeof(byte) + || typeToParseTo == typeof(sbyte) + || typeToParseTo == typeof(short) + || typeToParseTo == typeof(ushort) + || typeToParseTo == typeof(int) + || typeToParseTo == typeof(uint) + || typeToParseTo == typeof(long) + || typeToParseTo == typeof(ulong) || typeToParseTo == typeof(Single) - || typeToParseTo == typeof(Double); + || typeToParseTo == typeof(double); if (isNumericType) { - return String.Format(CultureInfo.CurrentCulture, UICultureResources.ErrorMessageForUnparsableNumericType); + return string.Format(CultureInfo.CurrentCulture, UICultureResources.ErrorMessageForUnparsableNumericType); } if (typeToParseTo == typeof(DateTime)) { - return String.Format(CultureInfo.CurrentCulture, UICultureResources.ErrorMessageForUnparsableDateTimeType, CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); + return string.Format(CultureInfo.CurrentCulture, UICultureResources.ErrorMessageForUnparsableDateTimeType, CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); } - return String.Format(CultureInfo.CurrentCulture, UICultureResources.ErrorTextBoxTypeConversionErrorText, typeToParseTo.Name); + return string.Format(CultureInfo.CurrentCulture, UICultureResources.ErrorTextBoxTypeConversionErrorText, typeToParseTo.Name); } #region Private Methods @@ -204,7 +186,7 @@ private bool TryGetGenericParameterForComparableValueFilterRule(FilterRule rule, genericParameter = null; TextFilterRule textRule = rule as TextFilterRule; - if (null != textRule) + if (textRule != null) { genericParameter = typeof(string); return true; @@ -222,13 +204,10 @@ private bool TryGetGenericParameterForComparableValueFilterRule(FilterRule rule, private object GetValueFromValidatingValue(FilterRule rule, string propertyName) { - Debug.Assert(null != rule && !String.IsNullOrEmpty(propertyName)); + Debug.Assert(rule != null && !string.IsNullOrEmpty(propertyName), "rule and propertyname are not null"); // NOTE: This isn't needed but OACR is complaining - if (null == rule) - { - throw new ArgumentNullException("rule"); - } + ArgumentNullException.ThrowIfNull(rule); Type ruleType = rule.GetType(); @@ -241,13 +220,10 @@ private object GetValueFromValidatingValue(FilterRule rule, string propertyName) private void SetValueOnValidatingValue(FilterRule rule, string propertyName, object value) { - Debug.Assert(null != rule && !String.IsNullOrEmpty(propertyName)); + Debug.Assert(rule != null && !string.IsNullOrEmpty(propertyName), "rule and propertyname are not null"); // NOTE: This isn't needed but OACR is complaining - if (null == rule) - { - throw new ArgumentNullException("rule"); - } + ArgumentNullException.ThrowIfNull(rule); Type ruleType = rule.GetType(); @@ -264,7 +240,7 @@ private void SetValueOnValidatingValue(FilterRule rule, string propertyName, obj private bool TryTransferValuesAsSingleValueComparableValueFilterRule(FilterRule oldRule, FilterRule newRule) { - Debug.Assert(null != oldRule && null != newRule); + Debug.Assert(oldRule != null && newRule != null, "oldrule and newrule are not null"); bool areCorrectType = this.IsSingleValueComparableValueFilterRule(oldRule) && this.IsSingleValueComparableValueFilterRule(newRule); @@ -281,7 +257,7 @@ private bool TryTransferValuesAsSingleValueComparableValueFilterRule(FilterRule private bool TryClearValueFromSingleValueComparableValueFilterRule(FilterRule rule) { - Debug.Assert(null != rule); + Debug.Assert(rule != null, "rule is not null"); if (!this.IsSingleValueComparableValueFilterRule(rule)) { @@ -295,7 +271,7 @@ private bool TryClearValueFromSingleValueComparableValueFilterRule(FilterRule ru private bool IsSingleValueComparableValueFilterRule(FilterRule rule) { - Debug.Assert(null != rule); + Debug.Assert(rule != null, "rule is not null"); Type genericParameter; if (!this.TryGetGenericParameterForComparableValueFilterRule(rule, out genericParameter)) @@ -316,7 +292,7 @@ private bool IsSingleValueComparableValueFilterRule(FilterRule rule) private bool TryClearIsBetweenFilterRule(FilterRule rule) { - Debug.Assert(null != rule); + Debug.Assert(rule != null, "rule is not null"); if (!this.IsIsBetweenFilterRule(rule)) { @@ -331,7 +307,7 @@ private bool TryClearIsBetweenFilterRule(FilterRule rule) private bool IsIsBetweenFilterRule(FilterRule rule) { - Debug.Assert(null != rule); + Debug.Assert(rule != null, "rule is not null"); Type genericParameter; if (!this.TryGetGenericParameterForComparableValueFilterRule(rule, out genericParameter)) diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterEvaluator.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterEvaluator.cs similarity index 85% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterEvaluator.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterEvaluator.cs index 7f0b1f65d5b..cf885d813c5 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterEvaluator.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterEvaluator.cs @@ -1,16 +1,13 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.ObjectModel; - using System.ComponentModel; - using System.Diagnostics; - /// /// The FilterEvaluator class is responsible for allowing the registration of /// the FilterExpressionProviders and producing a FilterExpression composed of @@ -21,7 +18,7 @@ public abstract class FilterEvaluator : IFilterExpressionProvider, INotifyProper { #region Properties - Collection filterExpressionProviders = new Collection(); + private Collection filterExpressionProviders = new Collection(); /// /// Gets a readonly collection of the registered FilterExpressionProviders. @@ -64,6 +61,7 @@ public bool StartFilterOnExpressionChanged { return this.startFilterOnExpressionChanged; } + set { this.startFilterOnExpressionChanged = value; @@ -82,6 +80,7 @@ public bool HasFilterExpression { return this.hasFilterExpression; } + protected set { this.hasFilterExpression = value; @@ -146,13 +145,10 @@ public FilterExpressionNode FilterExpression /// public void AddFilterExpressionProvider(IFilterExpressionProvider provider) { - if (null == provider) - { - throw new ArgumentNullException("provider"); - } + ArgumentNullException.ThrowIfNull(provider); this.filterExpressionProviders.Add(provider); - provider.FilterExpressionChanged += new EventHandler(this.FilterProvider_FilterExpressionChanged); + provider.FilterExpressionChanged += this.FilterProvider_FilterExpressionChanged; } /// @@ -163,13 +159,10 @@ public void AddFilterExpressionProvider(IFilterExpressionProvider provider) /// public void RemoveFilterExpressionProvider(IFilterExpressionProvider provider) { - if (null == provider) - { - throw new ArgumentNullException("provider"); - } + ArgumentNullException.ThrowIfNull(provider); this.filterExpressionProviders.Remove(provider); - provider.FilterExpressionChanged -= new EventHandler(this.FilterProvider_FilterExpressionChanged); + provider.FilterExpressionChanged -= this.FilterProvider_FilterExpressionChanged; } #region NotifyPropertyChanged @@ -182,11 +175,11 @@ public void RemoveFilterExpressionProvider(IFilterExpressionProvider provider) /// protected void NotifyPropertyChanged(string propertyName) { - Debug.Assert(!String.IsNullOrEmpty(propertyName)); + Debug.Assert(!string.IsNullOrEmpty(propertyName), "propertyName is not null"); PropertyChangedEventHandler eh = this.PropertyChanged; - if (null != eh) + if (eh != null) { eh(this, new PropertyChangedEventArgs(propertyName)); } @@ -209,7 +202,7 @@ protected void NotifyPropertyChanged(string propertyName) protected virtual void NotifyFilterExpressionChanged() { EventHandler eh = this.FilterExpressionChanged; - if (null != eh) + if (eh != null) { eh(this, new EventArgs()); } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExceptionEventArgs.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExceptionEventArgs.cs new file mode 100644 index 00000000000..77460f61fc9 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExceptionEventArgs.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The EventArgs detailing the exception raised while + /// evaluating the filter. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class FilterExceptionEventArgs : EventArgs + { + /// + /// Gets the Exception that was raised when filtering was + /// evaluated. + /// + public Exception Exception + { + get; + private set; + } + + /// + /// Initializes a new instance of the FilterExceptionEventArgs + /// class. + /// + /// + /// The Exception that was raised when filtering was evaluated. + /// + public FilterExceptionEventArgs(Exception exception) + { + ArgumentNullException.ThrowIfNull(exception); + + this.Exception = exception; + } + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionAndOperatorNode.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionAndOperatorNode.cs similarity index 81% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionAndOperatorNode.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionAndOperatorNode.cs index a3425d194f8..0227362bf28 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionAndOperatorNode.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionAndOperatorNode.cs @@ -1,15 +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.Text; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Text; - /// /// The FilterExpressionAndOperatorNode class is responsible for containing children /// FilterExpressionNodes which will be AND'ed together during evaluation. @@ -55,10 +52,7 @@ public FilterExpressionAndOperatorNode() /// public FilterExpressionAndOperatorNode(IEnumerable children) { - if (null == children) - { - throw new ArgumentNullException("children"); - } + ArgumentNullException.ThrowIfNull(children); this.children.AddRange(children); } @@ -80,7 +74,7 @@ public FilterExpressionAndOperatorNode(IEnumerable childre /// public override bool Evaluate(object item) { - if (0 == this.Children.Count) + if (this.Children.Count == 0) { return false; } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionNode.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionNode.cs similarity index 84% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionNode.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionNode.cs index b0312cfa0c1..e9e41b93f20 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionNode.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionNode.cs @@ -1,15 +1,12 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Management.UI.Internal { - using System.Diagnostics.CodeAnalysis; - using System.Collections.Generic; - using System.Collections.ObjectModel; - /// /// The FilterExpressionNode class is the base class for derived /// FilterExpressionNodes. FilterExpressionNodes are used to diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOperandNode.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOperandNode.cs new file mode 100644 index 00000000000..3161dc30283 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOperandNode.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The FilterExpressionOperandNode class is responsible for holding a + /// FilterRule within the FilterExpression tree. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class FilterExpressionOperandNode : FilterExpressionNode + { + #region Properties + + /// + /// The FilterRule to evaluate. + /// + public FilterRule Rule + { + get; + protected set; + } + + #endregion Properties + + #region Ctor + + /// + /// Initializes a new instance of the FilterExpressionOperandNode + /// class. + /// + /// + /// The FilterRule to hold for evaluation. + /// + public FilterExpressionOperandNode(FilterRule rule) + { + ArgumentNullException.ThrowIfNull(rule); + + this.Rule = rule; + } + + #endregion Ctor + + #region Public Methods + + /// + /// Evaluates the item against the contained FilterRule. + /// + /// + /// The item to pass to the contained FilterRule. + /// + /// + /// Returns true if the contained FilterRule evaluates to + /// true, false otherwise. + /// + public override bool Evaluate(object item) + { + Debug.Assert(this.Rule != null, "rule is not null"); + + return this.Rule.Evaluate(item); + } + + #endregion Public Methods + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOrOperatorNode.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOrOperatorNode.cs similarity index 81% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOrOperatorNode.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOrOperatorNode.cs index 08c6cb3b778..ff92e42cf2d 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOrOperatorNode.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOrOperatorNode.cs @@ -1,15 +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; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - /// /// The FilterExpressionOrOperatorNode class is responsible for containing children /// FilterExpressionNodes which will be OR'ed together during evaluation. @@ -55,10 +52,7 @@ public FilterExpressionOrOperatorNode() /// public FilterExpressionOrOperatorNode(IEnumerable children) { - if (null == children) - { - throw new ArgumentNullException("children"); - } + ArgumentNullException.ThrowIfNull(children); this.children.AddRange(children); } @@ -80,7 +74,7 @@ public FilterExpressionOrOperatorNode(IEnumerable children /// public override bool Evaluate(object item) { - if (0 == this.Children.Count) + if (this.Children.Count == 0) { return false; } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRuleCustomizationFactory.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRuleCustomizationFactory.cs similarity index 84% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRuleCustomizationFactory.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRuleCustomizationFactory.cs index e5ea1191e8a..b61c9933aef 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRuleCustomizationFactory.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRuleCustomizationFactory.cs @@ -1,16 +1,13 @@ -//----------------------------------------------------------------------- -// -// 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; +using System.Reflection; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Reflection; - /// /// The FilterRuleCustomizationFactory class provides a central location /// a return an abstract factory which creates the standard settings and rules @@ -29,16 +26,13 @@ public static FilterRuleCustomizationFactory FactoryInstance { get { - Debug.Assert(null != factoryInstance); + Debug.Assert(factoryInstance != null, "factoryInstance not null"); return factoryInstance; } set { - if (null == value) - { - throw new ArgumentNullException("value"); - } + ArgumentNullException.ThrowIfNull(value); factoryInstance = value; } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs new file mode 100644 index 00000000000..8362a035156 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The ComparableValueFilterRule provides support for derived classes + /// that evaluate against IComparable values. + /// + /// + /// The generic parameter. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public abstract class ComparableValueFilterRule : FilterRule where T : IComparable + { + /// + /// Initializes a new instance of the class. + /// + protected ComparableValueFilterRule() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + protected ComparableValueFilterRule(ComparableValueFilterRule source) + : base(source) + { + this.DefaultNullValueEvaluation = source.DefaultNullValueEvaluation; + } + + #region Properties + + /// + /// Gets or sets a value indicating whether null objects passed to Evaluate will + /// evaluate to true or false. + /// + protected bool DefaultNullValueEvaluation + { + get; + set; + } + + #endregion Properties + + #region Public Methods + + /// + /// Determines if item matches a derived classes criteria. + /// + /// + /// The item to match evaluate. + /// + /// + /// Returns true if the item matches, false otherwise. + /// + public override bool Evaluate(object item) + { + if (item == null) + { + return this.DefaultNullValueEvaluation; + } + + if (!this.IsValid) + { + return false; + } + + T castItem; + if (!FilterUtilities.TryCastItem(item, out castItem)) + { + return false; + } + + return this.Evaluate(castItem); + } + + /// + /// Determines if item matches a derived classes criteria. + /// + /// + /// The item to match evaluate. + /// + /// + /// Returns true if the item matches, false otherwise. + /// + protected abstract bool Evaluate(T data); + + #endregion Public Methods + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs new file mode 100644 index 00000000000..c5d4f36fe55 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The DoesNotEqualFilterRule class evaluates an IComparable item to + /// check if it is not equal to the rule's value. + /// + /// + /// The generic parameter. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class DoesNotEqualFilterRule : EqualsFilterRule where T : IComparable + { + /// + /// Initializes a new instance of the class. + /// + public DoesNotEqualFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_DoesNotEqual; + this.DefaultNullValueEvaluation = true; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public DoesNotEqualFilterRule(DoesNotEqualFilterRule source) + : base(source) + { + } + + /// + /// Determines if item is not equal to Value. + /// + /// + /// The data to compare against. + /// + /// + /// Returns true if data is not equal to Value, false otherwise. + /// + protected override bool Evaluate(T data) + { + return !base.Evaluate(data); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs new file mode 100644 index 00000000000..34a1ecb722d --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The EqualsFilterRule class evaluates an IComparable item to + /// check if it is equal to the rule's value. + /// + /// + /// The generic parameter. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class EqualsFilterRule : SingleValueComparableValueFilterRule where T : IComparable + { + /// + /// Initializes a new instance of the class. + /// + public EqualsFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_Equals; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public EqualsFilterRule(EqualsFilterRule source) + : base(source) + { + } + + /// + /// Determines if item is equal to Value. + /// + /// + /// The data to compare against. + /// + /// + /// Returns true if data is equal to Value. + /// + protected override bool Evaluate(T data) + { + Debug.Assert(this.IsValid, "isValid"); + + int result = CustomTypeComparer.Compare(this.Value.GetCastValue(), data); + return result == 0; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRule.cs new file mode 100644 index 00000000000..f18c89addf9 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRule.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The base class for all filtering rules. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public abstract class FilterRule : IEvaluate, IDeepCloneable + { + /// + /// Gets a value indicating whether the FilterRule can be + /// evaluated in its current state. + /// + public virtual bool IsValid + { + get + { + return true; + } + } + + /// + /// Gets a display friendly name for the FilterRule. + /// + public string DisplayName + { + get; + protected set; + } + + /// + /// Initializes a new instance of the class. + /// + protected FilterRule() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + protected FilterRule(FilterRule source) + { + ArgumentNullException.ThrowIfNull(source); + this.DisplayName = source.DisplayName; + } + + /// + public object DeepClone() + { + return Activator.CreateInstance(this.GetType(), new object[] { this }); + } + + /// + /// Gets a value indicating whether the supplied item meets the + /// criteria specified by this rule. + /// + /// The item to evaluate. + /// Returns true if the item meets the criteria. False otherwise. + public abstract bool Evaluate(object item); + + #region EvaluationResultInvalidated + + /// + /// Occurs when the values of this rule changes. + /// + public event EventHandler EvaluationResultInvalidated; + + /// + /// Fires . + /// + protected void NotifyEvaluationResultInvalidated() + { + var eh = this.EvaluationResultInvalidated; + + if (eh != null) + { + eh(this, new EventArgs()); + } + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs new file mode 100644 index 00000000000..4a3f8dc2975 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The FilterRuleExtensions class provides extension methods + /// for FilterRule classes. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public static class FilterRuleExtensions + { + /// + /// Creates a deep copy of a FilterRule. + /// + /// + /// The FilterRule to clone. + /// + /// + /// Returns a deep copy of the passed in rule. + /// + public static FilterRule DeepCopy(this FilterRule rule) + { + ArgumentNullException.ThrowIfNull(rule); + return (FilterRule)rule.DeepClone(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs new file mode 100644 index 00000000000..f51093510ec --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.Serialization; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The IsBetweenFilterRule class evaluates an item to see if it is between + /// the StartValue and EndValue of the rule. + /// + /// + /// The generic parameter. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class IsBetweenFilterRule : ComparableValueFilterRule where T : IComparable + { + #region Properties + + /// + /// Gets a value indicating whether the FilterRule can be + /// evaluated in its current state. + /// + public override bool IsValid + { + get + { + return this.StartValue.IsValid && this.EndValue.IsValid; + } + } + + /// + /// Gets the start value for the range. + /// + public ValidatingValue StartValue + { + get; + protected set; + } + + /// + /// Gets the end value for the range. + /// + public ValidatingValue EndValue + { + get; + protected set; + } + + #endregion Properties + + #region Ctor + + /// + /// Initializes a new instance of the class. + /// + public IsBetweenFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_IsBetween; + + this.StartValue = new ValidatingValue(); + this.StartValue.PropertyChanged += this.Value_PropertyChanged; + + this.EndValue = new ValidatingValue(); + this.EndValue.PropertyChanged += this.Value_PropertyChanged; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public IsBetweenFilterRule(IsBetweenFilterRule source) + : base(source) + { + this.StartValue = (ValidatingValue)source.StartValue.DeepClone(); + this.StartValue.PropertyChanged += this.Value_PropertyChanged; + + this.EndValue = (ValidatingValue)source.EndValue.DeepClone(); + this.EndValue.PropertyChanged += this.Value_PropertyChanged; + } + + #endregion Ctor + + #region Public Methods + + /// + /// Evaluates data and determines if it is between + /// StartValue and EndValue. + /// + /// + /// The data to evaluate. + /// + /// + /// Returns true if data is between StartValue and EndValue, + /// false otherwise. + /// + protected override bool Evaluate(T data) + { + Debug.Assert(this.IsValid, "is valid"); + int startValueComparedToData = CustomTypeComparer.Compare(this.StartValue.GetCastValue(), data); + int endValueComparedToData = CustomTypeComparer.Compare(this.EndValue.GetCastValue(), data); + + bool isBetweenForward = startValueComparedToData < 0 && endValueComparedToData > 0; + bool isBetweenBackwards = endValueComparedToData < 0 && startValueComparedToData > 0; + + return isBetweenForward || isBetweenBackwards; + } + + #endregion Public Methods + + #region Value Change Handlers + + private void Value_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "Value") + { + this.NotifyEvaluationResultInvalidated(); + } + } + + #endregion Value Change Handlers + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs new file mode 100644 index 00000000000..71bb7e23e7c --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The IsEmptyFilterRule evaluates an item to determine whether it + /// is empty or not. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class IsEmptyFilterRule : FilterRule + { + /// + /// Initializes a new instance of the class. + /// + public IsEmptyFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_IsEmpty; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public IsEmptyFilterRule(IsEmptyFilterRule source) + : base(source) + { + } + + /// + /// Gets a values indicating whether the supplied item is empty. + /// + /// The item to evaluate. + /// + /// Returns true if the item is null or if the item is a string + /// composed of whitespace. False otherwise. + /// + public override bool Evaluate(object item) + { + if (item == null) + { + return true; + } + + Type type = item.GetType(); + + if (typeof(string) == type) + { + return ((string)item).Trim().Length == 0; + } + + return false; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs new file mode 100644 index 00000000000..6c7d16f312a --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The IsGreaterThanFilterRule class evaluates an IComparable item to + /// check if it is greater than its value. + /// + /// + /// The generic parameter. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class IsGreaterThanFilterRule : SingleValueComparableValueFilterRule where T : IComparable + { + /// + /// Initializes a new instance of the class. + /// + public IsGreaterThanFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_GreaterThanOrEqual; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public IsGreaterThanFilterRule(IsGreaterThanFilterRule source) + : base(source) + { + } + + /// + /// Determines if item is greater than Value. + /// + /// + /// The data to compare against. + /// + /// + /// Returns true if data is greater than Value. + /// + protected override bool Evaluate(T data) + { + Debug.Assert(this.IsValid, "is valid"); + + int result = CustomTypeComparer.Compare(this.Value.GetCastValue(), data); + return result <= 0; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs new file mode 100644 index 00000000000..e1dc3268cc5 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The IsLessThanFilterRule class evaluates an IComparable item to + /// check if it is less than the rule's value. + /// + /// + /// The generic parameter. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class IsLessThanFilterRule : SingleValueComparableValueFilterRule where T : IComparable + { + /// + /// Initializes a new instance of the class. + /// + public IsLessThanFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_LessThanOrEqual; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public IsLessThanFilterRule(IsLessThanFilterRule source) + : base(source) + { + } + + /// + /// Determines if item is less than Value. + /// + /// + /// The data to compare against. + /// + /// + /// Returns true if data is less than Value. + /// + protected override bool Evaluate(T item) + { + Debug.Assert(this.IsValid, "is valid"); + + int result = CustomTypeComparer.Compare(this.Value.GetCastValue(), item); + return result >= 0; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs new file mode 100644 index 00000000000..711caee9874 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The IsNotEmptyFilterRule evaluates an item to determine whether it + /// is empty or not. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class IsNotEmptyFilterRule : IsEmptyFilterRule + { + /// + /// Initializes a new instance of the class. + /// + public IsNotEmptyFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_IsNotEmpty; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public IsNotEmptyFilterRule(IsNotEmptyFilterRule source) + : base(source) + { + } + + /// + /// Gets a values indicating whether the supplied item is not empty. + /// + /// The item to evaluate. + /// + /// Returns false if the item is null or if the item is a string + /// composed of whitespace. True otherwise. + /// + public override bool Evaluate(object item) + { + return !base.Evaluate(item); + } + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs similarity index 77% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs index 25331b6e6ba..cb6eacaaff3 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs @@ -1,23 +1,19 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; namespace Microsoft.Management.UI.Internal { - using System; - /// /// The IsNotEmptyValidationRule checks a value to see if a value is not empty. /// - [Serializable] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public class IsNotEmptyValidationRule : DataErrorInfoValidationRule { #region Properties - private static readonly DataErrorInfoValidationResult EmptyValueResult = new DataErrorInfoValidationResult(false, null, String.Empty); + private static readonly DataErrorInfoValidationResult EmptyValueResult = new DataErrorInfoValidationResult(false, null, string.Empty); #endregion Properties @@ -37,7 +33,7 @@ public class IsNotEmptyValidationRule : DataErrorInfoValidationRule /// public override DataErrorInfoValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) { - if (null == value) + if (value == null) { return EmptyValueResult; } @@ -54,12 +50,19 @@ public override DataErrorInfoValidationResult Validate(object value, System.Glob } } + /// + public override object DeepClone() + { + // Instance is stateless. + // return this; + return new IsNotEmptyValidationRule(); + } + #endregion Public Methods - //private internal static bool IsStringNotEmpty(string value) { - return !(String.IsNullOrEmpty(value) || 0 == value.Trim().Length); + return !(string.IsNullOrEmpty(value) || value.Trim().Length == 0); } } } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs similarity index 82% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs index 64b4b7952d6..8c32530be8c 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs @@ -1,20 +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.Runtime.Serialization; +using System.Text.RegularExpressions; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Text.RegularExpressions; - using System.Runtime.Serialization; - /// /// Represents a filter rule that searches for text within properties on an object. /// - [Serializable] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public class PropertiesTextContainsFilterRule : TextFilterRule { @@ -29,7 +25,18 @@ public class PropertiesTextContainsFilterRule : TextFilterRule public PropertiesTextContainsFilterRule() { this.PropertyNames = new List(); - this.EvaluationResultInvalidated += new EventHandler(this.PropertiesTextContainsFilterRule_EvaluationResultInvalidated); + this.EvaluationResultInvalidated += this.PropertiesTextContainsFilterRule_EvaluationResultInvalidated; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public PropertiesTextContainsFilterRule(PropertiesTextContainsFilterRule source) + : base(source) + { + this.PropertyNames = new List(source.PropertyNames); + this.EvaluationResultInvalidated += this.PropertiesTextContainsFilterRule_EvaluationResultInvalidated; } /// @@ -123,11 +130,5 @@ private void PropertiesTextContainsFilterRule_EvaluationResultInvalidated(object { this.OnEvaluationResultInvalidated(); } - - [OnDeserialized] - private void Initialize(StreamingContext context) - { - this.EvaluationResultInvalidated += new EventHandler(this.PropertiesTextContainsFilterRule_EvaluationResultInvalidated); - } } } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs similarity index 81% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs index 1a4930d0d71..09c732970b0 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs @@ -1,15 +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; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Diagnostics; - /// /// The PropertyValueSelectorFilterRule class supports filtering against a /// property of an object. Based on the type of the property a collection of @@ -18,7 +15,6 @@ namespace Microsoft.Management.UI.Internal /// /// The generic parameter. /// - [Serializable] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public class PropertyValueSelectorFilterRule : SelectorFilterRule where T : IComparable { @@ -69,27 +65,12 @@ public PropertyValueSelectorFilterRule(string propertyName, string propertyDispl /// public PropertyValueSelectorFilterRule(string propertyName, string propertyDisplayName, IEnumerable rules) { - if (String.IsNullOrEmpty(propertyName)) - { - throw new ArgumentNullException("propertyName"); - } - - if (String.IsNullOrEmpty(propertyDisplayName)) - { - throw new ArgumentNullException("propertyDisplayName"); - } - - if (null == rules) - { - throw new ArgumentNullException("rules"); - } - this.PropertyName = propertyName; this.DisplayName = propertyDisplayName; foreach (FilterRule rule in rules) { - if (null == rule) + if (rule == null) { throw new ArgumentException("A value within rules is null", "rules"); } @@ -100,6 +81,17 @@ public PropertyValueSelectorFilterRule(string propertyName, string propertyDispl this.AvailableRules.DisplayNameConverter = new FilterRuleToDisplayNameConverter(); } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public PropertyValueSelectorFilterRule(PropertyValueSelectorFilterRule source) + : base(source) + { + this.PropertyName = source.PropertyName; + this.AvailableRules.DisplayNameConverter = new FilterRuleToDisplayNameConverter(); + } + #endregion Ctor #region Public Methods @@ -120,7 +112,7 @@ public override bool Evaluate(object item) return false; } - if (null == item) + if (item == null) { return false; } @@ -142,12 +134,11 @@ private bool TryGetPropertyValue(object item, out T propertyValue) { propertyValue = default(T); - Debug.Assert(null != item); + Debug.Assert(item != null, "item not null"); return FilterRuleCustomizationFactory.FactoryInstance.PropertyValueGetter.TryGetPropertyValue(this.PropertyName, item, out propertyValue); } #endregion Private Methods - } } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs new file mode 100644 index 00000000000..d1627ee2281 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.Serialization; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The SelectorFilterRule represents a rule composed of other rules. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class SelectorFilterRule : FilterRule + { + #region Properties + + /// + /// Gets a value indicating whether the rule can be evaluated. + /// + public override bool IsValid + { + get + { + return this.AvailableRules.IsValid && this.AvailableRules.SelectedValue.IsValid; + } + } + + /// + /// Gets the collection of available rules. + /// + public ValidatingSelectorValue AvailableRules + { + get; + protected set; + } + + #endregion Properties + + #region Ctor + + /// + /// Initializes a new instance of the class. + /// + public SelectorFilterRule() + { + this.AvailableRules = new ValidatingSelectorValue(); + this.AvailableRules.SelectedValueChanged += this.AvailableRules_SelectedValueChanged; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public SelectorFilterRule(SelectorFilterRule source) + : base(source) + { + this.AvailableRules = (ValidatingSelectorValue)source.AvailableRules.DeepClone(); + this.AvailableRules.SelectedValueChanged += this.AvailableRules_SelectedValueChanged; + this.AvailableRules.SelectedValue.EvaluationResultInvalidated += this.SelectedValue_EvaluationResultInvalidated; + } + + #endregion Ctor + + #region Public Methods + + /// + /// Evaluates whether the item is inclusive. + /// + /// + /// The item to evaluate. + /// + /// + /// Returns true if the item matches the filtering criteria, false otherwise. + /// + public override bool Evaluate(object item) + { + if (!this.IsValid) + { + return false; + } + + return this.AvailableRules.SelectedValue.Evaluate(item); + } + + /// + /// Called when the SelectedValue within AvailableRules changes. + /// + /// + /// The old FilterRule. + /// + /// + /// The new FilterRule. + /// + protected void OnSelectedValueChanged(FilterRule oldValue, FilterRule newValue) + { + FilterRuleCustomizationFactory.FactoryInstance.ClearValues(newValue); + FilterRuleCustomizationFactory.FactoryInstance.TransferValues(oldValue, newValue); + FilterRuleCustomizationFactory.FactoryInstance.ClearValues(oldValue); + + oldValue.EvaluationResultInvalidated -= this.SelectedValue_EvaluationResultInvalidated; + newValue.EvaluationResultInvalidated += this.SelectedValue_EvaluationResultInvalidated; + + this.NotifyEvaluationResultInvalidated(); + } + + private void SelectedValue_EvaluationResultInvalidated(object sender, EventArgs e) + { + this.NotifyEvaluationResultInvalidated(); + } + + #endregion Public Methods + + #region Private Methods + + private void AvailableRules_SelectedValueChanged(object sender, PropertyChangedEventArgs e) + { + this.OnSelectedValueChanged(e.OldValue, e.NewValue); + } + + #endregion Private Methods + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs new file mode 100644 index 00000000000..b26531943fc --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.Serialization; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The SingleValueComparableValueFilterRule provides support for derived classes + /// that take a single input and evaluate against IComparable values. + /// + /// The generic parameter. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public abstract class SingleValueComparableValueFilterRule : ComparableValueFilterRule where T : IComparable + { + #region Properties + + /// + /// Gets a value that holds user input. + /// + public ValidatingValue Value + { + get; + protected set; + } + + /// + /// Gets a value indicating whether the FilterRule can be + /// evaluated in its current state. + /// + public override bool IsValid + { + get + { + return this.Value.IsValid; + } + } + + #endregion Properties + + #region Ctor + + /// + /// Initializes a new instance of the class. + /// + protected SingleValueComparableValueFilterRule() + { + this.Value = new ValidatingValue(); + this.Value.PropertyChanged += this.Value_PropertyChanged; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + protected SingleValueComparableValueFilterRule(SingleValueComparableValueFilterRule source) + : base(source) + { + this.Value = (ValidatingValue)source.Value.DeepClone(); + this.Value.PropertyChanged += this.Value_PropertyChanged; + } + + #endregion Ctor + + private void Value_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "Value") + { + this.NotifyEvaluationResultInvalidated(); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs new file mode 100644 index 00000000000..beb4a29d23f --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The TextContainsFilterRule class evaluates a string item to + /// check if it is contains the rule's value within it. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class TextContainsFilterRule : TextFilterRule + { + private static readonly string TextContainsCharactersRegexPattern = "{0}"; + private static readonly string TextContainsWordsRegexPattern = WordBoundaryRegexPattern + TextContainsCharactersRegexPattern + WordBoundaryRegexPattern; + + /// + /// Initializes a new instance of the class. + /// + public TextContainsFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_Contains; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextContainsFilterRule(TextContainsFilterRule source) + : base(source) + { + } + + /// + /// Determines if Value is contained within data. + /// + /// + /// The data to compare with. + /// + /// + /// Returns true if data contains Value, false otherwise. + /// + protected override bool Evaluate(string data) + { + Debug.Assert(this.IsValid, "is valid"); + + // True "text contains": \\ + return this.ExactMatchEvaluate(data, TextContainsCharactersRegexPattern, TextContainsWordsRegexPattern); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs new file mode 100644 index 00000000000..2cdbf1efcef --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The TextDoesNotContainFilterRule class evaluates a string item to + /// check if it is does not contain the rule's value within it. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class TextDoesNotContainFilterRule : TextContainsFilterRule + { + /// + /// Initializes a new instance of the class. + /// + public TextDoesNotContainFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_DoesNotContain; + this.DefaultNullValueEvaluation = true; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextDoesNotContainFilterRule(TextDoesNotContainFilterRule source) + : base(source) + { + } + + /// + /// Determines if Value is not contained within data. + /// + /// + /// The data to compare with. + /// + /// + /// Returns true if data does not contain Value, false otherwise. + /// + protected override bool Evaluate(string data) + { + return !base.Evaluate(data); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs new file mode 100644 index 00000000000..e74b371a7a6 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The TextDoesNotEqualFilterRule class evaluates a string item to + /// check if it is not equal to the rule's value. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class TextDoesNotEqualFilterRule : TextEqualsFilterRule + { + /// + /// Initializes a new instance of the class. + /// + public TextDoesNotEqualFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_DoesNotEqual; + this.DefaultNullValueEvaluation = true; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextDoesNotEqualFilterRule(TextDoesNotEqualFilterRule source) + : base(source) + { + } + + /// + /// Determines if data is not equal to Value. + /// + /// + /// The value to compare against. + /// + /// + /// Returns true is data does not equal Value, false otherwise. + /// + protected override bool Evaluate(string data) + { + return !base.Evaluate(data); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs new file mode 100644 index 00000000000..d7f7e05c4b8 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The TextEndsWithFilterRule class evaluates a string item to + /// check if it ends with the rule's value. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class TextEndsWithFilterRule : TextFilterRule + { + private static readonly string TextEndsWithCharactersRegexPattern = "{0}$"; + private static readonly string TextEndsWithWordsRegexPattern = WordBoundaryRegexPattern + TextEndsWithCharactersRegexPattern; + + /// + /// Initializes a new instance of the class. + /// + public TextEndsWithFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_TextEndsWith; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextEndsWithFilterRule(TextEndsWithFilterRule source) + : base(source) + { + } + + /// + /// Determines if data ends with Value. + /// + /// + /// The value to compare with. + /// + /// + /// Returns true is data ends with Value, false otherwise. + /// + protected override bool Evaluate(string data) + { + Debug.Assert(this.IsValid, "is valid"); + + return this.ExactMatchEvaluate(data, TextEndsWithCharactersRegexPattern, TextEndsWithWordsRegexPattern); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs new file mode 100644 index 00000000000..a357575c6ab --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The TextEqualsFilterRule class evaluates a string item to + /// check if it is equal to the rule's value. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class TextEqualsFilterRule : TextFilterRule + { + private static readonly string TextEqualsCharactersRegexPattern = "^{0}$"; + + /// + /// Initializes a new instance of the class. + /// + public TextEqualsFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_Equals; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextEqualsFilterRule(TextEqualsFilterRule source) + : base(source) + { + } + + /// + /// Determines if data is equal to Value. + /// + /// + /// The value to compare against. + /// + /// + /// Returns true is data equals Value, false otherwise. + /// + protected override bool Evaluate(string data) + { + Debug.Assert(this.IsValid, "is valid"); + + return this.ExactMatchEvaluate(data, TextEqualsCharactersRegexPattern, TextEqualsCharactersRegexPattern); + } + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextFilterRule.cs similarity index 84% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextFilterRule.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextFilterRule.cs index cfaeb2fc2e3..eacbcb8d256 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextFilterRule.cs @@ -1,22 +1,18 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Text.RegularExpressions; namespace Microsoft.Management.UI.Internal { - using System; - using System.Globalization; - using System.Text.RegularExpressions; - using System.Diagnostics; - using System.ComponentModel; - /// /// The TextFilterRule class supports derived rules by offering services for /// evaluating string operations. /// - [Serializable] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public abstract class TextFilterRule : SingleValueComparableValueFilterRule { @@ -37,6 +33,7 @@ public bool IgnoreCase { return this.ignoreCase; } + set { this.ignoreCase = value; @@ -54,6 +51,7 @@ public bool CultureInvariant { return this.cultureInvariant; } + set { this.cultureInvariant = value; @@ -63,7 +61,7 @@ public bool CultureInvariant } /// - /// Initializes a new instance of the TextFilterRule class. + /// Initializes a new instance of the class. /// protected TextFilterRule() { @@ -71,6 +69,17 @@ protected TextFilterRule() this.CultureInvariant = false; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + protected TextFilterRule(TextFilterRule source) + : base(source) + { + this.IgnoreCase = source.IgnoreCase; + this.CultureInvariant = source.CultureInvariant; + } + /// /// Gets the current value and determines whether it should be evaluated as an exact match. /// @@ -86,7 +95,7 @@ protected internal string GetParsedValue(out bool evaluateAsExactMatch) // If it's an exact-match value, remove quotes and use the exact-match pattern \\ if (evaluateAsExactMatch) { - parsedValue = parsedValue.Replace("\"", String.Empty); + parsedValue = parsedValue.Replace("\"", string.Empty); } return parsedValue; @@ -102,17 +111,11 @@ protected internal string GetParsedValue(out bool evaluateAsExactMatch) /// The specified value is a null reference. protected internal string GetRegexPattern(string pattern, string exactMatchPattern) { - if (pattern == null) - { - throw new ArgumentNullException("pattern"); - } + ArgumentNullException.ThrowIfNull(pattern); - if (exactMatchPattern == null) - { - throw new ArgumentNullException("exactMatchPattern"); - } + ArgumentNullException.ThrowIfNull(exactMatchPattern); - Debug.Assert(this.IsValid); + Debug.Assert(this.IsValid, "is valid"); bool evaluateAsExactMatch; string value = this.GetParsedValue(out evaluateAsExactMatch); @@ -125,7 +128,7 @@ protected internal string GetRegexPattern(string pattern, string exactMatchPatte value = Regex.Escape(value); // Format the pattern using the specified data \\ - return String.Format(CultureInfo.InvariantCulture, pattern, value); + return string.Format(CultureInfo.InvariantCulture, pattern, value); } /// @@ -159,7 +162,7 @@ protected internal RegexOptions GetRegexOptions() /// true if the specified data matches one of the specified patterns; otherwise, false. protected internal bool ExactMatchEvaluate(string data, string pattern, string exactMatchPattern) { - Debug.Assert(this.IsValid); + Debug.Assert(this.IsValid, "is valid"); var parsedPattern = this.GetRegexPattern(pattern, exactMatchPattern); var options = this.GetRegexOptions(); @@ -167,4 +170,4 @@ protected internal bool ExactMatchEvaluate(string data, string pattern, string e return Regex.IsMatch(data, parsedPattern, options); } } -} \ No newline at end of file +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs new file mode 100644 index 00000000000..98eac2b9a41 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The TextStartsWithFilterRule class evaluates a string item to + /// check if it starts with the rule's value. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class TextStartsWithFilterRule : TextFilterRule + { + private static readonly string TextStartsWithCharactersRegexPattern = "^{0}"; + private static readonly string TextStartsWithWordsRegexPattern = TextStartsWithCharactersRegexPattern + WordBoundaryRegexPattern; + + /// + /// Initializes a new instance of the class. + /// + public TextStartsWithFilterRule() + { + this.DisplayName = UICultureResources.FilterRule_TextStartsWith; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextStartsWithFilterRule(TextStartsWithFilterRule source) + : base(source) + { + } + + /// + /// Determines if data starts with Value. + /// + /// + /// The value to compare with. + /// + /// + /// Returns true is data starts with Value, false otherwise. + /// + protected override bool Evaluate(string data) + { + Debug.Assert(this.IsValid, "is valid"); + + return this.ExactMatchEvaluate(data, TextStartsWithCharactersRegexPattern, TextStartsWithWordsRegexPattern); + } + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterStatus.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterStatus.cs similarity index 75% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterStatus.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterStatus.cs index b06307ea0ff..74cca6e890d 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterStatus.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterStatus.cs @@ -1,8 +1,6 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + namespace Microsoft.Management.UI.Internal { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterUtilities.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterUtilities.cs new file mode 100644 index 00000000000..f4827d1b454 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterUtilities.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides common utilities for filtering. + /// + internal static class FilterUtilities + { + internal static bool TryCastItem(object item, out T castItem) + { + castItem = default(T); + + bool isItemUncastable = item == null && typeof(T).IsValueType; + if (isItemUncastable) + { + return false; + } + + bool shouldCastToString = item != null && typeof(string) == typeof(T); + if (shouldCastToString) + { + // NOTE: string => T doesn't compile. We confuse the type system + // and use string => object => T to make this work. + object stringPropertyValue = item.ToString(); + castItem = (T)stringPropertyValue; + return true; + } + + try + { + castItem = (T)item; + return true; + } + catch (InvalidCastException e) + { + Debug.Print(e.ToString()); + } + + return false; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IDeepCloneable.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IDeepCloneable.cs new file mode 100644 index 00000000000..841a2424b51 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IDeepCloneable.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Defines a generalized method for creating a deep copy of an instance. + /// + internal interface IDeepCloneable + { + /// + /// Creates a deep copy of the current instance. + /// + /// A new object that is a deep copy of the current instance. + object DeepClone(); + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IEvaluate.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IEvaluate.cs new file mode 100644 index 00000000000..161f14d4537 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IEvaluate.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The IEvaluate interface provides the most basic + /// support for the evaluation of an item against + /// criteria defined in a derived class. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public interface IEvaluate + { + /// + /// Gets a values indicating whether the supplied item has meet the + /// criteria rule specified by the rule. + /// + /// + /// The item to evaluate. + /// + /// + /// Returns true if the item meets the criteria. False otherwise. + /// + bool Evaluate(object item); + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/IFilterExpressionProvider.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IFilterExpressionProvider.cs similarity index 77% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/IFilterExpressionProvider.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IFilterExpressionProvider.cs index a09ead4a960..b72fcfe5aef 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/IFilterExpressionProvider.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IFilterExpressionProvider.cs @@ -1,13 +1,10 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; namespace Microsoft.Management.UI.Internal { - using System; - /// /// The IFilterExpressionProvider interface defines the contract between /// providers of FilterExpressions and consumers thereof. diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ItemsControlFilterEvaluator.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ItemsControlFilterEvaluator.cs similarity index 83% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ItemsControlFilterEvaluator.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ItemsControlFilterEvaluator.cs index 51699de4118..d68ead8bc4b 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ItemsControlFilterEvaluator.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ItemsControlFilterEvaluator.cs @@ -1,15 +1,12 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Windows.Controls; namespace Microsoft.Management.UI.Internal { - using System; - using System.Diagnostics; - using System.Windows.Controls; - /// /// The ItemsControlFilterEvaluator class provides functionality to /// apply a filter against an ItemsControl. @@ -34,7 +31,7 @@ public ItemsControl FilterTarget set { - if (null != this.filterTarget) + if (this.filterTarget != null) { this.StopFilter(); } @@ -68,7 +65,7 @@ private FilterExpressionNode CachedFilterExpression /// public override void StartFilter() { - if (null == this.FilterTarget) + if (this.FilterTarget == null) { throw new InvalidOperationException("FilterTarget is null."); } @@ -76,7 +73,7 @@ public override void StartFilter() // Cache the expression for filtering so subsequent changes are ignored \\ this.CachedFilterExpression = this.FilterExpression; - if (null != this.CachedFilterExpression) + if (this.CachedFilterExpression != null) { this.FilterTarget.Items.Filter = this.FilterExpressionAdapter; this.FilterStatus = FilterStatus.Applied; @@ -92,7 +89,7 @@ public override void StartFilter() /// public override void StopFilter() { - if (null == this.FilterTarget) + if (this.FilterTarget == null) { throw new InvalidOperationException("FilterTarget is null."); } @@ -112,7 +109,7 @@ public override void StopFilter() private bool FilterExpressionAdapter(object item) { - Debug.Assert(null != this.CachedFilterExpression); + Debug.Assert(this.CachedFilterExpression != null, "not null"); try { @@ -132,7 +129,7 @@ private bool FilterExpressionAdapter(object item) private bool TryNotifyFilterException(Exception e) { EventHandler eh = this.FilterExceptionOccurred; - if (null != eh) + if (eh != null) { eh(this, new FilterExceptionEventArgs(e)); return true; diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidatingSelectorValue.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingSelectorValue.cs similarity index 80% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidatingSelectorValue.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingSelectorValue.cs index a319cf984a5..0fed0c42e65 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidatingSelectorValue.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingSelectorValue.cs @@ -1,16 +1,13 @@ -//----------------------------------------------------------------------- -// -// 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.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Windows.Data; - /// /// The ValidatingSelectorValue class provides support for selecting /// a value from a collection of available values. @@ -18,10 +15,40 @@ namespace Microsoft.Management.UI.Internal /// /// The generic parameter. /// - [Serializable] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public class ValidatingSelectorValue : ValidatingValueBase { + /// + /// Initializes a new instance of the class. + /// + public ValidatingSelectorValue() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public ValidatingSelectorValue(ValidatingSelectorValue source) + : base(source) + { + availableValues.EnsureCapacity(source.availableValues.Count); + if (typeof(IDeepCloneable).IsAssignableFrom(typeof(T))) + { + foreach (var value in source.availableValues) + { + availableValues.Add((T)((IDeepCloneable)value).DeepClone()); + } + } + else + { + availableValues.AddRange(source.availableValues); + } + + selectedIndex = source.selectedIndex; + displayNameConverter = source.displayNameConverter; + } + #region Properties #region Consts @@ -50,6 +77,7 @@ public IList AvailableValues #region SelectedIndex private const string SelectedIndexPropertyName = "SelectedIndex"; + private int selectedIndex; /// @@ -132,11 +160,6 @@ public IValueConverter DisplayNameConverter set { - if (null != value && !value.GetType().IsSerializable) - { - throw new ArgumentException("The DisplayNameConverter must be serializable.", "value"); - } - this.displayNameConverter = value; } } @@ -150,13 +173,18 @@ public IValueConverter DisplayNameConverter /// /// Notifies listeners that the selected value has changed. /// - [field:NonSerialized] public event EventHandler> SelectedValueChanged; #endregion Events #region Public Methods + /// + public override object DeepClone() + { + return new ValidatingSelectorValue(this); + } + #region Validate /// @@ -189,7 +217,7 @@ protected override DataErrorInfoValidationResult Validate(string columnName) { if (!columnName.Equals(SelectedIndexPropertyName, StringComparison.CurrentCulture)) { - throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "{0} is not a valid column name.", columnName), "columnName"); + throw new ArgumentException(string.Create(CultureInfo.CurrentCulture, $"{columnName} is not a valid column name."), "columnName"); } if (!this.IsIndexWithinBounds(this.SelectedIndex)) @@ -218,7 +246,7 @@ protected void NotifySelectedValueChanged(T oldValue, T newValue) { EventHandler> eh = this.SelectedValueChanged; - if (null != eh) + if (eh != null) { eh(this, new PropertyChangedEventArgs(oldValue, newValue)); } @@ -234,7 +262,7 @@ protected void NotifySelectedValueChanged(T oldValue, T newValue) private bool IsIndexWithinBounds(int value) { - return (value >= 0 && value < this.AvailableValues.Count); + return value >= 0 && value < this.AvailableValues.Count; } #endregion IsIndexWithinBounds diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidatingValue.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValue.cs similarity index 82% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidatingValue.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValue.cs index 4ab73d4ddcc..437cb3be50e 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidatingValue.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValue.cs @@ -1,15 +1,12 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Globalization; namespace Microsoft.Management.UI.Internal { - using System; - using System.Globalization; - using System.Diagnostics; - /// /// The ValidatingValue class supports setting a value and validating the /// value. @@ -17,10 +14,26 @@ namespace Microsoft.Management.UI.Internal /// /// The generic parameter. /// - [Serializable] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public class ValidatingValue : ValidatingValueBase { + /// + /// Initializes a new instance of the class. + /// + public ValidatingValue() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public ValidatingValue(ValidatingValue source) + : base(source) + { + value = source.Value is IDeepCloneable deepClone ? deepClone.DeepClone() : source.Value; + } + #region Properties #region Value @@ -53,6 +66,12 @@ public object Value #region Public Methods + /// + public override object DeepClone() + { + return new ValidatingValue(this); + } + /// /// Gets the raw value cast/transformed into /// type T. @@ -129,7 +148,7 @@ protected override DataErrorInfoValidationResult Validate(string columnName) if (this.IsValueEmpty()) { - return new DataErrorInfoValidationResult(false, null, String.Empty); + return new DataErrorInfoValidationResult(false, null, string.Empty); } T castValue; @@ -150,14 +169,13 @@ protected override DataErrorInfoValidationResult Validate(string columnName) private bool IsValueEmpty() { - if (null == this.Value) + if (this.Value == null) { return true; } - string stringValue = this.Value.ToString(); - if (String.IsNullOrEmpty(stringValue)) + if (string.IsNullOrEmpty(stringValue)) { return true; } @@ -169,10 +187,7 @@ private bool TryGetCastValue(object rawValue, out T castValue) { castValue = default(T); - if (null == rawValue) - { - throw new ArgumentNullException("rawValue"); - } + ArgumentNullException.ThrowIfNull(rawValue); if (typeof(T).IsEnum) { @@ -196,8 +211,8 @@ private bool TryGetCastValue(object rawValue, out T castValue) private bool TryGetEnumValue(object rawValue, out T castValue) { - Debug.Assert(null != rawValue); - Debug.Assert(typeof(T).IsEnum); + Debug.Assert(rawValue != null, "rawValue not null"); + Debug.Assert(typeof(T).IsEnum, "is enum"); castValue = default(T); diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidatingValueBase.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValueBase.cs similarity index 83% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidatingValueBase.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValueBase.cs index 6a404b4b84a..a4ffb1af77c 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidatingValueBase.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValueBase.cs @@ -1,26 +1,43 @@ -//----------------------------------------------------------------------- -// -// 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.ComponentModel; +using System.Diagnostics; +using System.Globalization; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.ComponentModel; - using System.Globalization; - using System.Diagnostics; - /// /// The ValidatingValueBase class provides basic services for base /// classes to support validation via the IDataErrorInfo interface. /// - [Serializable] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public abstract class ValidatingValueBase : IDataErrorInfo, INotifyPropertyChanged + public abstract class ValidatingValueBase : IDataErrorInfo, INotifyPropertyChanged, IDeepCloneable { + /// + /// Initializes a new instance of the class. + /// + protected ValidatingValueBase() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + protected ValidatingValueBase(ValidatingValueBase source) + { + ArgumentNullException.ThrowIfNull(source); + validationRules.EnsureCapacity(source.validationRules.Count); + foreach (var rule in source.validationRules) + { + validationRules.Add((DataErrorInfoValidationRule)rule.DeepClone()); + } + } + #region Properties #region ValidationRules @@ -29,7 +46,6 @@ public abstract class ValidatingValueBase : IDataErrorInfo, INotifyPropertyChang private ReadOnlyCollection readonlyValidationRules; private bool isValidationRulesCollectionDirty = true; - [field: NonSerialized] private DataErrorInfoValidationResult cachedValidationResult; /// @@ -85,10 +101,7 @@ public string this[string columnName] { get { - if (String.IsNullOrEmpty(columnName)) - { - throw new ArgumentNullException("columnName"); - } + ArgumentException.ThrowIfNullOrEmpty(columnName); this.UpdateValidationResult(columnName); return this.GetValidationResult().ErrorMessage; @@ -107,7 +120,7 @@ public string Error get { DataErrorInfoValidationResult result = this.GetValidationResult(); - return (!result.IsValid) ? result.ErrorMessage : String.Empty; + return (!result.IsValid) ? result.ErrorMessage : string.Empty; } } @@ -126,7 +139,6 @@ public string Error /// /// The listeners attached to this event are not serialized. /// - [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged; #endregion PropertyChanged @@ -135,6 +147,9 @@ public string Error #region Public Methods + /// + public abstract object DeepClone(); + #region AddValidationRule /// @@ -143,10 +158,7 @@ public string Error /// The validation rule to add. public void AddValidationRule(DataErrorInfoValidationRule rule) { - if (null == rule) - { - throw new ArgumentNullException("rule"); - } + ArgumentNullException.ThrowIfNull(rule); this.validationRules.Add(rule); @@ -164,10 +176,7 @@ public void AddValidationRule(DataErrorInfoValidationRule rule) /// The rule to remove. public void RemoveValidationRule(DataErrorInfoValidationRule rule) { - if (null == rule) - { - throw new ArgumentNullException("rule"); - } + ArgumentNullException.ThrowIfNull(rule); this.validationRules.Remove(rule); @@ -203,7 +212,6 @@ public void ClearValidationRules() /// protected abstract DataErrorInfoValidationResult Validate(); - /// /// Called to validate the property with the given name. /// @@ -225,9 +233,9 @@ internal DataErrorInfoValidationResult EvaluateValidationRules(object value, Sys foreach (DataErrorInfoValidationRule rule in this.ValidationRules) { DataErrorInfoValidationResult result = rule.Validate(value, cultureInfo); - if (null == result) + if (result == null) { - throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "DataErrorInfoValidationResult not returned by ValidationRule: {0}", rule.ToString())); + throw new InvalidOperationException(string.Create(CultureInfo.CurrentCulture, $"DataErrorInfoValidationResult not returned by ValidationRule: {rule}")); } if (!result.IsValid) @@ -266,7 +274,7 @@ protected void NotifyPropertyChanged(string propertyName) { PropertyChangedEventHandler eh = this.PropertyChanged; - if (null != eh) + if (eh != null) { eh(this, new PropertyChangedEventArgs(propertyName)); } @@ -282,7 +290,7 @@ protected void NotifyPropertyChanged(string propertyName) private DataErrorInfoValidationResult GetValidationResult() { - if (null == this.cachedValidationResult) + if (this.cachedValidationResult == null) { this.UpdateValidationResult(); } @@ -308,7 +316,7 @@ private void UpdateValidationResult(string columnName) private void NotifyValidationResultUpdated() { - Debug.Assert(null != this.cachedValidationResult); + Debug.Assert(this.cachedValidationResult != null, "not null"); this.NotifyPropertyChanged("IsValid"); this.NotifyPropertyChanged("Error"); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationResult.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationResult.cs similarity index 77% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationResult.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationResult.cs index 1e1c94d9d97..39ccd531c38 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationResult.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationResult.cs @@ -1,14 +1,11 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Windows.Controls; namespace Microsoft.Management.UI.Internal { - using System; - using System.Windows.Controls; - /// /// The DataErrorInfoValidationResult supports reporting validation result /// data needed for the IDataErrorInfo interface. @@ -36,13 +33,14 @@ public string ErrorMessage get; private set; } - private static readonly DataErrorInfoValidationResult valid = new DataErrorInfoValidationResult(true, null, String.Empty); + + private static readonly DataErrorInfoValidationResult valid = new DataErrorInfoValidationResult(true, null, string.Empty); /// /// Geta an instance of DataErrorInfoValidationResult that corresponds /// to a valid result. /// - public new static DataErrorInfoValidationResult ValidResult + public static new DataErrorInfoValidationResult ValidResult { get { @@ -72,8 +70,8 @@ public string ErrorMessage public DataErrorInfoValidationResult(bool isValid, object errorContent, string errorMessage) : base(isValid, errorContent) { - this.IsUserVisible = !String.IsNullOrEmpty(errorMessage); - this.ErrorMessage = errorMessage ?? String.Empty; + this.IsUserVisible = !string.IsNullOrEmpty(errorMessage); + this.ErrorMessage = errorMessage ?? string.Empty; } #endregion Ctor diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs new file mode 100644 index 00000000000..a92916c0717 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides a way to create a custom rule in order to check the validity of user input. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public abstract class DataErrorInfoValidationRule : IDeepCloneable + { + /// + /// When overridden in a derived class, performs validation checks on a value. + /// + /// + /// The value to check. + /// + /// + /// The culture to use in this rule. + /// + /// + /// A DataErrorInfoValidationResult object. + /// + public abstract DataErrorInfoValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo); + + /// + public abstract object DeepClone(); + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/AddFilterRulePicker.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/AddFilterRulePicker.Generated.cs similarity index 94% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/AddFilterRulePicker.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/AddFilterRulePicker.Generated.cs index de79cdc074c..e326f6542e0 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/AddFilterRulePicker.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/AddFilterRulePicker.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -244,7 +234,7 @@ protected virtual void OnIsOpenChanged(PropertyChangedEventArgs e) /// private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/AddFilterRulePicker.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/AddFilterRulePicker.cs similarity index 86% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/AddFilterRulePicker.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/AddFilterRulePicker.cs index cd2e803e804..d41b0a3e532 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/AddFilterRulePicker.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/AddFilterRulePicker.cs @@ -1,16 +1,13 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Windows.Controls; namespace Microsoft.Management.UI.Internal { - using System.Collections.ObjectModel; - using System.Windows.Controls; - using System.ComponentModel; - using System; - /// /// The AddFilterRulePicker class is responsible for allowing users to /// add rules to an FilterRulePanel. @@ -52,7 +49,7 @@ public ObservableCollection ColumnFilterRules partial void OnOkAddFilterRulesCanExecuteImplementation(System.Windows.Input.CanExecuteRoutedEventArgs e) { - e.CanExecute = (null != this.AddFilterRulesCommand) + e.CanExecute = (this.AddFilterRulesCommand != null) ? CommandHelper.CanExecuteCommand(this.AddFilterRulesCommand, null, this.AddFilterRulesCommandTarget) : false; } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/AddFilterRulePickerItem.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/AddFilterRulePickerItem.cs similarity index 86% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/AddFilterRulePickerItem.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/AddFilterRulePickerItem.cs index c6f9a53dd91..24c6eec1889 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/AddFilterRulePickerItem.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/AddFilterRulePickerItem.cs @@ -1,13 +1,10 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.ComponentModel; namespace Microsoft.Management.UI.Internal { - using System.ComponentModel; - /// /// The AddFilterRulePicker class is responsible for holding state /// information needed by the AddFilterRulePicker class. @@ -75,7 +72,7 @@ protected void NotifyPropertyChanged(string propertyName) { PropertyChangedEventHandler eh = this.PropertyChanged; - if (null != eh) + if (eh != null) { eh(this, new PropertyChangedEventArgs(propertyName)); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanel.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanel.Generated.cs similarity index 86% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanel.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanel.Generated.cs index 405e6b1f3f5..5a87767b70a 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanel.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanel.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanel.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanel.cs similarity index 82% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanel.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanel.cs index 9273bafcba0..c3bb4042d53 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanel.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanel.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.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Globalization; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Diagnostics; - using System.Globalization; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Documents; - using System.Windows.Input; - /// /// The FilterRulePanel allows users to construct and display a complex query built using s. /// @@ -136,7 +133,7 @@ public FilterRulePanel() { this.InitializeTemplates(); - this.Controller.FilterExpressionChanged += new EventHandler(this.Controller_FilterExpressionChanged); + this.Controller.FilterExpressionChanged += this.Controller_FilterExpressionChanged; } #endregion Ctor @@ -157,15 +154,9 @@ public FilterRulePanel() /// public void AddFilterRulePanelItemContentTemplate(Type type, DataTemplate dataTemplate) { - if (null == type) - { - throw new ArgumentNullException("type"); - } + ArgumentNullException.ThrowIfNull(type); - if (null == dataTemplate) - { - throw new ArgumentNullException("dataTemplate"); - } + ArgumentNullException.ThrowIfNull(dataTemplate); this.filterRuleTemplateSelector.TemplateDictionary.Add(new KeyValuePair(type, dataTemplate)); } @@ -179,10 +170,7 @@ public void AddFilterRulePanelItemContentTemplate(Type type, DataTemplate dataTe /// public void RemoveFilterRulePanelItemContentTemplate(Type type) { - if (null == type) - { - throw new ArgumentNullException("type"); - } + ArgumentNullException.ThrowIfNull(type); this.filterRuleTemplateSelector.TemplateDictionary.Remove(type); } @@ -217,7 +205,7 @@ public void ClearContentTemplates() protected virtual void NotifyFilterExpressionChanged() { EventHandler eh = this.FilterExpressionChanged; - if (null != eh) + if (eh != null) { eh(this, new EventArgs()); } @@ -238,9 +226,9 @@ private void Controller_FilterExpressionChanged(object sender, EventArgs e) partial void OnAddRulesExecutedImplementation(ExecutedRoutedEventArgs e) { - Debug.Assert(null != e); + Debug.Assert(e != null, "not null"); - if (null == e.Parameter) + if (e.Parameter == null) { throw new ArgumentException("e.Parameter is null.", "e"); } @@ -251,7 +239,7 @@ partial void OnAddRulesExecutedImplementation(ExecutedRoutedEventArgs e) foreach (object item in selectedItems) { FilterRulePanelItem newItem = item as FilterRulePanelItem; - if (null == newItem) + if (newItem == null) { throw new ArgumentException( "e.Parameter contains a value which is not a valid FilterRulePanelItem object.", @@ -273,15 +261,15 @@ partial void OnAddRulesExecutedImplementation(ExecutedRoutedEventArgs e) partial void OnRemoveRuleExecutedImplementation(ExecutedRoutedEventArgs e) { - Debug.Assert(null != e); + Debug.Assert(e != null, "not null"); - if (null == e.Parameter) + if (e.Parameter == null) { throw new ArgumentException("e.Parameter is null.", "e"); } FilterRulePanelItem item = e.Parameter as FilterRulePanelItem; - if (null == item) + if (item == null) { throw new ArgumentException("e.Parameter is not a valid FilterRulePanelItem object.", "e"); } @@ -301,8 +289,6 @@ private void InitializeTemplates() List> defaultTemplates = new List>() { - #region Info for Default Templates - new KeyValuePair(typeof(SelectorFilterRule), "CompositeRuleTemplate"), new KeyValuePair(typeof(SingleValueComparableValueFilterRule<>), "ComparableValueRuleTemplate"), new KeyValuePair(typeof(IsEmptyFilterRule), "NoInputTemplate"), @@ -311,9 +297,7 @@ private void InitializeTemplates() new KeyValuePair(typeof(ValidatingValue<>), "ValidatingValueTemplate"), new KeyValuePair(typeof(ValidatingSelectorValue<>), "ValidatingSelectorValueTemplate"), new KeyValuePair(typeof(IsBetweenFilterRule<>), "IsBetweenRuleTemplate"), - new KeyValuePair(typeof(Object), "CatchAllTemplate") - - #endregion Info for Default Templates + new KeyValuePair(typeof(object), "CatchAllTemplate") }; defaultTemplates.ForEach(templateInfo => this.AddFilterRulePanelItemContentTemplate(templateInfo.Key, templateInfo.Value)); @@ -323,27 +307,22 @@ private void InitializeTemplatesForInputTypes() { List inputTypes = new List() { - #region Info For Input Types - - typeof(SByte), - typeof(Byte), - typeof(Int16), - typeof(Int32), - typeof(Int64), - typeof(UInt16), - typeof(UInt32), - typeof(UInt64), - typeof(Char), + typeof(sbyte), + typeof(byte), + typeof(short), + typeof(int), + typeof(long), + typeof(ushort), + typeof(uint), + typeof(ulong), + typeof(char), typeof(Single), - typeof(Double), - typeof(Decimal), - typeof(Boolean), - + typeof(double), + typeof(decimal), + typeof(bool), typeof(Enum), typeof(DateTime), - typeof(String) - - #endregion Info For Input Types + typeof(string) }; inputTypes.ForEach(type => this.AddFilterRulePanelItemContentTemplate(type, "InputValueTemplate")); @@ -351,13 +330,13 @@ private void InitializeTemplatesForInputTypes() private void AddFilterRulePanelItemContentTemplate(Type type, string resourceName) { - Debug.Assert(null != type); - Debug.Assert(!String.IsNullOrEmpty(resourceName)); + Debug.Assert(type != null, "not null"); + Debug.Assert(!string.IsNullOrEmpty(resourceName), "not null"); var templateInfo = new ComponentResourceKey(typeof(FilterRulePanel), resourceName); DataTemplate template = (DataTemplate)this.TryFindResource(templateInfo); - Debug.Assert(null != template); + Debug.Assert(template != null, "not null"); this.AddFilterRulePanelItemContentTemplate(type, template); } @@ -368,7 +347,7 @@ private void AddFilterRulePanelItemContentTemplate(Type type, string resourceNam private void AddFilterRuleInternal(FilterRulePanelItem item) { - Debug.Assert(null != item); + Debug.Assert(item != null, "not null"); FilterRulePanelItem newItem = new FilterRulePanelItem(item.Rule.DeepCopy(), item.GroupId); this.Controller.AddFilterRulePanelItem(newItem); @@ -376,7 +355,7 @@ private void AddFilterRuleInternal(FilterRulePanelItem item) private void RemoveFilterRuleInternal(FilterRulePanelItem item) { - Debug.Assert(null != item); + Debug.Assert(item != null, "not null"); this.Controller.RemoveFilterRulePanelItem(item); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelContentPresenter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelContentPresenter.cs similarity index 77% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelContentPresenter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelContentPresenter.cs index b530cc51ad9..7955a149435 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelContentPresenter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelContentPresenter.cs @@ -1,15 +1,12 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System.Windows; - using System.Windows.Controls; - using System.Windows.Data; - /// /// The FilterRulePanelContentPresenter selects a template based upon the ContentConverter /// provided. @@ -45,7 +42,7 @@ public IValueConverter ContentConverter /// protected override DataTemplate ChooseTemplate() { - if (null == this.ContentTemplateSelector || null == this.ContentConverter) + if (this.ContentTemplateSelector == null || this.ContentConverter == null) { return base.ChooseTemplate(); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelController.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelController.cs similarity index 85% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelController.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelController.cs index a22630db61b..68c22de5af9 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelController.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelController.cs @@ -1,16 +1,13 @@ -//----------------------------------------------------------------------- -// -// 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.Diagnostics; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Diagnostics; - /// /// The FilterRulePanelController is responsible managing the addition and removal of /// s to a . @@ -51,7 +48,7 @@ public bool HasFilterExpression { get { - return (this.FilterExpression != null); + return this.FilterExpression != null; } } @@ -91,15 +88,12 @@ public FilterRulePanelController() /// public void AddFilterRulePanelItem(FilterRulePanelItem item) { - if (null == item) - { - throw new ArgumentNullException("item"); - } + ArgumentNullException.ThrowIfNull(item); int insertionIndex = this.GetInsertionIndex(item); this.filterRulePanelItems.Insert(insertionIndex, item); - item.Rule.EvaluationResultInvalidated += new EventHandler(this.Rule_EvaluationResultInvalidated); + item.Rule.EvaluationResultInvalidated += this.Rule_EvaluationResultInvalidated; this.UpdateFilterRulePanelItemTypes(); @@ -119,12 +113,9 @@ private void Rule_EvaluationResultInvalidated(object sender, EventArgs e) /// public void RemoveFilterRulePanelItem(FilterRulePanelItem item) { - if (null == item) - { - throw new ArgumentNullException("item"); - } + ArgumentNullException.ThrowIfNull(item); - item.Rule.EvaluationResultInvalidated -= new EventHandler(this.Rule_EvaluationResultInvalidated); + item.Rule.EvaluationResultInvalidated -= this.Rule_EvaluationResultInvalidated; this.filterRulePanelItems.Remove(item); this.UpdateFilterRulePanelItemTypes(); @@ -152,7 +143,7 @@ private FilterExpressionNode CreateFilterExpression() { List groupNodes = new List(); - for (int i = 0; i < this.filterRulePanelItems.Count; ) + for (int i = 0; i < this.filterRulePanelItems.Count;) { int endIndex = this.GetExclusiveEndIndexForGroupStartingAt(i); @@ -165,7 +156,7 @@ private FilterExpressionNode CreateFilterExpression() i = endIndex; } - if (0 == groupNodes.Count) + if (groupNodes.Count == 0) { return null; } @@ -175,8 +166,8 @@ private FilterExpressionNode CreateFilterExpression() private int GetExclusiveEndIndexForGroupStartingAt(int startIndex) { - Debug.Assert(this.filterRulePanelItems.Count > 0); - Debug.Assert(startIndex >= 0); + Debug.Assert(this.filterRulePanelItems.Count > 0, "greater than 0"); + Debug.Assert(startIndex >= 0, "greater than or equal to 0"); int i = startIndex; for (; i < this.filterRulePanelItems.Count; i++) @@ -200,9 +191,9 @@ private int GetExclusiveEndIndexForGroupStartingAt(int startIndex) private FilterExpressionOrOperatorNode CreateFilterExpressionForGroup(int startIndex, int endIndex) { - Debug.Assert(this.filterRulePanelItems.Count > 0); - Debug.Assert(startIndex >= 0); - Debug.Assert(this.filterRulePanelItems.Count >= endIndex); + Debug.Assert(this.filterRulePanelItems.Count > 0, "greater than 0"); + Debug.Assert(startIndex >= 0, "greater than or equal to 0"); + Debug.Assert(this.filterRulePanelItems.Count >= endIndex, "greater than or equal to endIndex"); FilterExpressionOrOperatorNode groupNode = new FilterExpressionOrOperatorNode(); for (int i = startIndex; i < endIndex; i++) @@ -223,7 +214,7 @@ private FilterExpressionOrOperatorNode CreateFilterExpressionForGroup(int startI private int GetInsertionIndex(FilterRulePanelItem item) { - Debug.Assert(null != item); + Debug.Assert(item != null, "not null"); for (int i = this.filterRulePanelItems.Count - 1; i >= 0; i--) { @@ -257,7 +248,6 @@ private void UpdateFilterRulePanelItemTypes() { this.filterRulePanelItems[i].ItemType = FilterRulePanelItemType.Item; } - } } @@ -271,7 +261,7 @@ private void UpdateFilterRulePanelItemTypes() protected virtual void NotifyFilterExpressionChanged() { EventHandler eh = this.FilterExpressionChanged; - if (null != eh) + if (eh != null) { eh(this, new EventArgs()); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelItem.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelItem.cs similarity index 79% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelItem.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelItem.cs index c77d5b86ed4..ee6b124562c 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelItem.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelItem.cs @@ -1,14 +1,12 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Diagnostics; + namespace Microsoft.Management.UI.Internal { - using System; - using System.ComponentModel; - using System.Diagnostics; - /// /// The FilterRulePanelItem class maintains the state for a row item within a . /// @@ -81,15 +79,8 @@ protected internal set /// public FilterRulePanelItem(FilterRule rule, string groupId) { - if (null == rule) - { - throw new ArgumentNullException("rule"); - } - - if (String.IsNullOrEmpty(groupId)) - { - throw new ArgumentNullException("groupId"); - } + ArgumentNullException.ThrowIfNull(rule); + ArgumentException.ThrowIfNullOrEmpty(groupId); this.Rule = rule; this.GroupId = groupId; @@ -107,10 +98,10 @@ public FilterRulePanelItem(FilterRule rule, string groupId) /// protected void NotifyPropertyChanged(string propertyName) { - Debug.Assert(!String.IsNullOrEmpty(propertyName)); + Debug.Assert(!string.IsNullOrEmpty(propertyName), "not null"); PropertyChangedEventHandler eh = this.PropertyChanged; - if (null != eh) + if (eh != null) { eh(this, new PropertyChangedEventArgs(propertyName)); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelItemType.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelItemType.cs similarity index 79% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelItemType.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelItemType.cs index 461cfcef991..490d1776ec0 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRulePanelItemType.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRulePanelItemType.cs @@ -1,8 +1,5 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. namespace Microsoft.Management.UI.Internal { diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRuleTemplateSelector.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleTemplateSelector.cs similarity index 75% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRuleTemplateSelector.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleTemplateSelector.cs index 4cadb6b4cf8..0381ef0e63c 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRuleTemplateSelector.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleTemplateSelector.cs @@ -1,16 +1,13 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Windows; - using System.Windows.Controls; - /// /// The FilterRuleTemplateSelector class selects a template based upon the type of /// the item and the corresponding template that is registered in the TemplateDictionary. @@ -29,7 +26,7 @@ public IDictionary TemplateDictionary } /// - /// Selects a template based upon the type of the item and and the + /// Selects a template based upon the type of the item and the /// corresponding template that is registered in the TemplateDictionary. /// /// @@ -43,16 +40,12 @@ public IDictionary TemplateDictionary /// public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) { - if (null == item) + if (item == null) { return base.SelectTemplate(item, container); } - Type type = item as Type; - if (null == type) - { - type = item.GetType(); - } + Type type = item as Type ?? item.GetType(); DataTemplate template; @@ -70,7 +63,7 @@ public override DataTemplate SelectTemplate(object item, System.Windows.Dependen type = type.BaseType; } - while (null != type); + while (type != null); return base.SelectTemplate(item, container); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs similarity index 82% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs index dec5adebba4..972c19080e0 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs @@ -1,20 +1,16 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Windows; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Windows; - using System.Windows.Data; - /// /// The FilterRuleToDisplayNameConverter is responsible for converting /// a FilterRule value to its DisplayName. /// - [Serializable] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public class FilterRuleToDisplayNameConverter : IValueConverter { @@ -38,14 +34,13 @@ public class FilterRuleToDisplayNameConverter : IValueConverter /// public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - if (null == value) + if (value == null) { return DependencyProperty.UnsetValue; } - FilterRule rule = value as FilterRule; - if (null == rule) + if (rule == null) { throw new ArgumentException("value of type FilterRule expected.", "value"); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/InputFieldBackgroundTextConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/InputFieldBackgroundTextConverter.cs similarity index 78% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/InputFieldBackgroundTextConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/InputFieldBackgroundTextConverter.cs index 9d01bcb5e37..1295b933d5b 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/InputFieldBackgroundTextConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/InputFieldBackgroundTextConverter.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.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Text; - using System.Windows.Data; - using System.Diagnostics; - using System.Globalization; - /// /// The InputFieldBackgroundTextConverter is responsible for determining the /// correct background text to display for a particular type of data. @@ -43,10 +40,7 @@ public class InputFieldBackgroundTextConverter : IValueConverter /// public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (null == value) - { - throw new ArgumentNullException("value"); - } + ArgumentNullException.ThrowIfNull(value); Type inputType = null; if (this.IsOfTypeValidatingValue(value)) @@ -74,23 +68,23 @@ public object ConvertBack(object value, Type targetType, object parameter, Syste private bool IsOfTypeValidatingValue(object value) { - Debug.Assert(null != value); + Debug.Assert(value != null, "not null"); Type type = value.GetType(); - if (false == type.IsGenericType) + if (type.IsGenericType == false) { return false; } type = type.GetGenericTypeDefinition(); - return (type == ValidatingValueGenericType); + return type == ValidatingValueGenericType; } private Type GetGenericParameter(object value, CultureInfo culture) { - Debug.Assert(null != value); - Debug.Assert(this.IsOfTypeValidatingValue(value)); + Debug.Assert(value != null, "not null"); + Debug.Assert(this.IsOfTypeValidatingValue(value), "not null"); return value.GetType().GetGenericArguments()[0]; } @@ -109,6 +103,5 @@ private object GetBackgroundTextForType(Type inputType) } #endregion Helpers - } } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/IsValidatingValueValidConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/IsValidatingValueValidConverter.cs similarity index 80% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/IsValidatingValueValidConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/IsValidatingValueValidConverter.cs index 70dfa0addc1..4c07ec10f6d 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/IsValidatingValueValidConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/IsValidatingValueValidConverter.cs @@ -1,15 +1,12 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Globalization; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Windows.Data; - using System.Globalization; - /// /// The IsValidatingValueValidConverter is responsible for determining if /// a ValidatingValueBase object is valid. @@ -39,7 +36,7 @@ public class IsValidatingValueValidConverter : IValueConverter public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { string error = (string)value; - return String.IsNullOrEmpty(error); + return string.IsNullOrEmpty(error); } /// diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchBox.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchBox.Generated.cs similarity index 91% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchBox.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchBox.Generated.cs index a67536b0286..3a6c7fedae8 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchBox.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchBox.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -125,7 +115,7 @@ protected virtual void OnBackgroundTextChanged(PropertyChangedEventArgs /// /// Identifies the Text dependency property. /// - public static readonly DependencyProperty TextProperty = DependencyProperty.Register( "Text", typeof(string), typeof(SearchBox), new PropertyMetadata( String.Empty, TextProperty_PropertyChanged) ); + public static readonly DependencyProperty TextProperty = DependencyProperty.Register( "Text", typeof(string), typeof(SearchBox), new PropertyMetadata( string.Empty, TextProperty_PropertyChanged) ); /// /// Gets or sets the text contents of the search box. @@ -173,7 +163,7 @@ protected virtual void OnTextChanged(PropertyChangedEventArgs e) /// private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchBox.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchBox.cs similarity index 78% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchBox.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchBox.cs index 25f5b614a54..b3345a10cf2 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchBox.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchBox.cs @@ -1,25 +1,15 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Represents a control that parses search text to return a filter expression. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; namespace Microsoft.Management.UI.Internal { - #region Using Directives - - using System; - using System.Collections.Generic; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Input; - using System.ComponentModel; - - #endregion - /// /// Partial class implementation for SearchBox control. /// @@ -56,7 +46,7 @@ public bool HasFilterExpression { get { - return (false == String.IsNullOrEmpty(this.Text)); + return string.IsNullOrEmpty(this.Text) == false; } } @@ -71,7 +61,7 @@ public bool HasFilterExpression protected virtual void NotifyFilterExpressionChanged() { EventHandler eh = this.FilterExpressionChanged; - if (null != eh) + if (eh != null) { eh(this, new EventArgs()); } @@ -93,12 +83,10 @@ public SearchTextParser Parser return this.parser; } + set { - if (value == null) - { - throw new ArgumentNullException("value"); - } + ArgumentNullException.ThrowIfNull(value); this.parser = value; } @@ -116,7 +104,7 @@ partial void OnClearTextCanExecuteImplementation(CanExecuteRoutedEventArgs e) partial void OnClearTextExecutedImplementation(ExecutedRoutedEventArgs e) { - this.Text = String.Empty; + this.Text = string.Empty; } /// @@ -127,10 +115,7 @@ partial void OnClearTextExecutedImplementation(ExecutedRoutedEventArgs e) /// The specified value is a null reference. protected static FilterExpressionNode ConvertToFilterExpression(ICollection searchBoxItems) { - if (searchBoxItems == null) - { - throw new ArgumentNullException("searchBoxItems"); - } + ArgumentNullException.ThrowIfNull(searchBoxItems); if (searchBoxItems.Count == 0) { diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchTextParseResult.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchTextParseResult.cs new file mode 100644 index 00000000000..10843087587 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchTextParseResult.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Represents the result of search text parsing. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class SearchTextParseResult + { + /// + /// Initializes a new instance of with the specified . + /// + /// The rule that resulted from parsing the search text. + /// The specified value is a null reference. + public SearchTextParseResult(FilterRule rule) + { + ArgumentNullException.ThrowIfNull(rule); + + this.FilterRule = rule; + } + + /// + /// Gets the rule that resulted from parsing the search text. + /// + public FilterRule FilterRule + { + get; + private set; + } + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchTextParser.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchTextParser.cs similarity index 83% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchTextParser.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchTextParser.cs index 8d97b5ad286..2e0ac74e076 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchTextParser.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/SearchTextParser.cs @@ -1,24 +1,14 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Provides functionality for parsing search text. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Text.RegularExpressions; namespace Microsoft.Management.UI.Internal { - #region Using Directives - - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Globalization; - using System.Text.RegularExpressions; - - #endregion - /// /// Provides functionality for parsing search text. /// @@ -55,10 +45,7 @@ public TextFilterRule FullTextRule public bool TryAddSearchableRule(SelectorFilterRule selectorRule) where T : TextFilterRule { - if (selectorRule == null) - { - throw new ArgumentNullException("selectorRule"); - } + ArgumentNullException.ThrowIfNull(selectorRule); T textRule = selectorRule.AvailableRules.AvailableValues.Find(); @@ -90,7 +77,7 @@ public void ClearSearchableRules() /// A read-only collection of results. public virtual ReadOnlyCollection Parse(string text) { - if (String.IsNullOrEmpty(text)) + if (string.IsNullOrEmpty(text)) { return new ReadOnlyCollection(new List(0)); } @@ -134,9 +121,9 @@ protected virtual string GetPattern() patterns.Add(rule.Pattern); } - patterns.Add(String.Format(CultureInfo.InvariantCulture, "(?<{0}>){1}", FullTextRuleGroupName, ValuePattern)); + patterns.Add(string.Format(CultureInfo.InvariantCulture, "(?<{0}>){1}", FullTextRuleGroupName, ValuePattern)); - return String.Join("|", patterns.ToArray()); + return string.Join("|", patterns.ToArray()); } /// @@ -203,25 +190,21 @@ protected class SearchableRule /// The specified value is a null reference. public SearchableRule(string uniqueId, SelectorFilterRule selectorFilterRule, TextFilterRule childRule) { - if (uniqueId == null) - { - throw new ArgumentNullException("uniqueId"); - } + ArgumentNullException.ThrowIfNull(uniqueId); - if (selectorFilterRule == null) - { - throw new ArgumentNullException("selectorFilterRule"); - } + ArgumentNullException.ThrowIfNull(selectorFilterRule); - if (childRule == null) - { - throw new ArgumentNullException("childRule"); - } + ArgumentNullException.ThrowIfNull(childRule); this.UniqueId = uniqueId; this.selectorFilterRule = selectorFilterRule; this.childRule = childRule; - this.Pattern = String.Format(CultureInfo.InvariantCulture, "(?<{0}>){1}\\s*:\\s*{2}", uniqueId, Regex.Escape(selectorFilterRule.DisplayName), SearchTextParser.ValuePattern); + this.Pattern = string.Format( + CultureInfo.InvariantCulture, + "(?<{0}>){1}\\s*:\\s*{2}", + uniqueId, + Regex.Escape(selectorFilterRule.DisplayName), + SearchTextParser.ValuePattern); } /// @@ -250,10 +233,7 @@ public string Pattern /// The specified value is a null reference. public SelectorFilterRule GetRuleWithValueSet(string value) { - if (value == null) - { - throw new ArgumentNullException("value"); - } + ArgumentNullException.ThrowIfNull(value); SelectorFilterRule selectorRule = (SelectorFilterRule)this.selectorFilterRule.DeepCopy(); selectorRule.AvailableRules.SelectedIndex = this.selectorFilterRule.AvailableRules.AvailableValues.IndexOf(this.childRule); diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/ValidatingSelectorValueToDisplayNameConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/ValidatingSelectorValueToDisplayNameConverter.cs similarity index 79% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/ValidatingSelectorValueToDisplayNameConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/ValidatingSelectorValueToDisplayNameConverter.cs index dd30ac048d8..010fbbeef75 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/ValidatingSelectorValueToDisplayNameConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/ValidatingSelectorValueToDisplayNameConverter.cs @@ -1,17 +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.Linq; +using System.Text; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Windows.Data; - /// /// The ValidatingSelectorValueToDisplayNameConverterTakes class is responsible for returning a display /// friendly name for the ValidatingSelectorValue class. @@ -41,12 +38,9 @@ public class ValidatingSelectorValueToDisplayNameConverter : IMultiValueConverte /// public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - if (null == values) - { - throw new ArgumentNullException("values"); - } + ArgumentNullException.ThrowIfNull(values); - if (2 != values.Length) + if (values.Length != 2) { throw new ArgumentException("Two values expected", "values"); } @@ -55,12 +49,12 @@ public object Convert(object[] values, Type targetType, object parameter, System object input = values[0]; IValueConverter converter = values[1] as IValueConverter; - if (null == converter) + if (converter == null) { throw new ArgumentException("Second value should be a IValueConverter", "values"); } - if (typeof(string) != targetType) + if (targetType != typeof(string)) { throw new ArgumentException("targetType should be of type string", "targetType"); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/ValidatingValueToGenericParameterTypeConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/ValidatingValueToGenericParameterTypeConverter.cs similarity index 85% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/ValidatingValueToGenericParameterTypeConverter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/ValidatingValueToGenericParameterTypeConverter.cs index cb749bddb12..ccd7447e1de 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/ValidatingValueToGenericParameterTypeConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/ValidatingValueToGenericParameterTypeConverter.cs @@ -1,14 +1,11 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Windows.Data; - /// /// The ValidatingValueToGenericParameterTypeConverter class is responsible for /// converting a to its generic parameter T. @@ -16,7 +13,6 @@ namespace Microsoft.Management.UI.Internal [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public class ValidatingValueToGenericParameterTypeConverter : IValueConverter { - /// /// Gets an instance of the ValidatingValueToGenericParameterTypeConverter class. /// @@ -48,7 +44,7 @@ public static ValidatingValueToGenericParameterTypeConverter Instance /// public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - if (null == value) + if (value == null) { return typeof(string); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/ColumnPicker.xaml b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ColumnPicker.xaml similarity index 82% rename from src/Microsoft.PowerShell.GraphicalHost/xamls/ColumnPicker.xaml rename to src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ColumnPicker.xaml index e036a2d26dd..3c4f7e86157 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/ColumnPicker.xaml +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ColumnPicker.xaml @@ -1,9 +1,15 @@ - + + - /// Target ManagementList. /// RetryActionAfterLoaded callback method. - /// true iff columns restorable + /// True if-and-only-if columns are restorable. /// /// ManagementList.AutoGenerateColumns not supported. /// @@ -161,7 +151,7 @@ private static bool VerifyColumnsRestorable(ManagementList subject, RetryActionC return false; } - if (null == subject.List) + if (subject.List == null) { return false; } @@ -182,17 +172,17 @@ private static bool VerifyRulesSavableAndRestorable(ManagementList subject, Retr return false; } - if (null == subject.AddFilterRulePicker) + if (subject.AddFilterRulePicker == null) { return false; } - if (null == subject.FilterRulePanel) + if (subject.FilterRulePanel == null) { return false; } - if (null == subject.SearchBox) + if (subject.SearchBox == null) { return false; } @@ -285,7 +275,7 @@ private void RestoreColumnsOrder(ManagementList subject) List columnsCopy = new List(subject.List.Columns); InnerListColumnOrderComparer ilcc = new InnerListColumnOrderComparer(this.columns); columnsCopy.Sort(ilcc); - Debug.Assert(columnsCopy.Count == subject.List.Columns.Count); + Debug.Assert(columnsCopy.Count == subject.List.Columns.Count, "match count"); Utilities.ResortObservableCollection( subject.List.Columns, @@ -298,8 +288,9 @@ private void RestoreColumnsOrder(ManagementList subject) { columnsCopy.Add((InnerListColumn)gvc); } + columnsCopy.Sort(ilcc); - Debug.Assert(columnsCopy.Count == subject.List.InnerGrid.Columns.Count); + Debug.Assert(columnsCopy.Count == subject.List.InnerGrid.Columns.Count, "match count"); Utilities.ResortObservableCollection( subject.List.InnerGrid.Columns, @@ -390,7 +381,7 @@ private void SaveSortOrder(ManagementList subject) } else { - this.sortOrderPropertyName = String.Empty; + this.sortOrderPropertyName = string.Empty; } } @@ -403,7 +394,7 @@ private void RestoreSortOrder(ManagementList subject) subject.List.ClearSort(); - if (!String.IsNullOrEmpty(this.sortOrderPropertyName)) + if (!string.IsNullOrEmpty(this.sortOrderPropertyName)) { foreach (InnerListColumn column in subject.List.Columns) { @@ -460,7 +451,7 @@ private static double GetColumnWidth(InnerListColumn ilc) private static void SetColumnWidth(GridViewColumn ilc, double width) { - if (!Double.IsNaN(width)) + if (!double.IsNaN(width)) { ilc.Width = width; } @@ -473,7 +464,6 @@ private static void SetColumnWidth(GridViewColumn ilc, double width) #region Helper Classes - [Serializable] internal class ColumnStateDescriptor { private int index; @@ -518,7 +508,6 @@ public double Width } } - [Serializable] internal class RuleStateDescriptor { /// @@ -570,11 +559,11 @@ public int Compare(InnerListColumn x, InnerListColumn y) { return 0; } - else if (null == x) + else if (x == null) { return -1; } - else if (null == y) + else if (y == null) { return 1; } @@ -584,7 +573,7 @@ public int Compare(InnerListColumn x, InnerListColumn y) this.columns.TryGetValue(x.DataDescription.PropertyName, out csdX); this.columns.TryGetValue(y.DataDescription.PropertyName, out csdY); - if (null == csdX || null == csdY || false == (csdX.IsInUse && csdX.IsInUse)) + if (csdX == null || csdY == null || (csdX.IsInUse && csdX.IsInUse) == false) { return 0; } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListStateDescriptorFactory.cs b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListStateDescriptorFactory.cs new file mode 100644 index 00000000000..07c2cec62fa --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListStateDescriptorFactory.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Management.UI.Internal +{ + using System; + using System.Diagnostics.CodeAnalysis; + + /// + /// Defines a factory which returns ManagementListStateDescriptors. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class ManagementListStateDescriptorFactory : IStateDescriptorFactory + { + /// + /// Factory method that creates a ManagementListStateDescriptor. + /// + /// A new ManagementListStateDescriptor. + public StateDescriptor Create() + { + return new ManagementListStateDescriptor(); + } + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ManagementListTitle.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListTitle.Generated.cs similarity index 93% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ManagementListTitle.Generated.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListTitle.Generated.cs index 7c3ac8cb3bd..6aa6d584e65 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ManagementListTitle.Generated.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListTitle.Generated.cs @@ -1,15 +1,5 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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. -// -// ----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region StyleCop Suppression - generated code using System; @@ -81,7 +71,7 @@ protected virtual void OnListChanged(PropertyChangedEventArgs e) /// /// Identifies the ListStatus dependency property. /// - public static readonly DependencyProperty ListStatusProperty = DependencyProperty.Register( "ListStatus", typeof(string), typeof(ManagementListTitle), new PropertyMetadata( String.Empty, ListStatusProperty_PropertyChanged) ); + public static readonly DependencyProperty ListStatusProperty = DependencyProperty.Register( "ListStatus", typeof(string), typeof(ManagementListTitle), new PropertyMetadata( string.Empty, ListStatusProperty_PropertyChanged) ); /// /// Gets the status of the list. This is a dependency property. @@ -130,7 +120,7 @@ protected virtual void OnListStatusChanged(PropertyChangedEventArgs e) /// /// Identifies the Title dependency property. /// - public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( "Title", typeof(string), typeof(ManagementListTitle), new PropertyMetadata( String.Empty, TitleProperty_PropertyChanged) ); + public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( "Title", typeof(string), typeof(ManagementListTitle), new PropertyMetadata( string.Empty, TitleProperty_PropertyChanged) ); /// /// Gets or sets the title. This is a dependency property. @@ -227,7 +217,7 @@ protected virtual void OnTotalItemCountChanged(PropertyChangedEventArgs e) /// private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) { - if(eh != null) + if (eh != null) { eh(this,e); } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListTitle.cs b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListTitle.cs new file mode 100644 index 00000000000..66be05cdf33 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListTitle.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Partial class implementation for ManagementListTitle control. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public partial class ManagementListTitle : Control + { + /// + /// Initializes a new instance of the class. + /// + public ManagementListTitle() + { + // This constructor intentionally left blank + } + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/PropertyValueComparer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/PropertyValueComparer.cs similarity index 80% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/PropertyValueComparer.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/PropertyValueComparer.cs index 13f800b6e9c..5c9f56daa81 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/PropertyValueComparer.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/PropertyValueComparer.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.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Management.Automation; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections; - using System.Collections.Generic; - using System.ComponentModel; - using System.Diagnostics; - using System.Management.Automation; - /// /// Provides a mechanism for comparing objects based on specific properties. /// @@ -77,7 +74,6 @@ private void GetPropertyValues(string propertyName, object a, object b, out obje { // NOTE : Being unable to retrieve the value is equivalent to getting a null in // this case since they will both be treated as "having no value" for CompareTo. - firstValue = null; secondValue = null; @@ -110,21 +106,12 @@ private int CompareData(object firstValue, object secondValue, StringComparison return -1; } - Type firstType = firstValue.GetType(); Type secondType = secondValue.GetType(); - // If the objects are different types do string comparison or numeric comparison if (firstType != secondType) { - // If both are numeric leverage LanguagePrimitives automatic conversion on comparison - if (LanguagePrimitives.IsNumeric(Type.GetTypeCode(firstType)) && LanguagePrimitives.IsNumeric(Type.GetTypeCode(secondType))) - { - return LanguagePrimitives.Compare(firstValue, secondValue); - } - - // otherwise compare them as strings - return string.Compare(firstValue.ToString(), secondValue.ToString(), stringComparison); + return LanguagePrimitives.Compare(firstValue, secondValue); } // If the values are strings, compare them considering stringComparison @@ -133,7 +120,6 @@ private int CompareData(object firstValue, object secondValue, StringComparison if (firstString != null) { string secondString = secondValue as string; - Diagnostics.Assert(secondString != null, "different types have been handled above"); return string.Compare(firstString, secondString, stringComparison); } else @@ -147,7 +133,7 @@ private int CompareData(object firstValue, object secondValue, StringComparison } else { - return string.Compare(firstValue.ToString(), secondValue.ToString(), stringComparison);; + return string.Compare(firstValue.ToString(), secondValue.ToString(), stringComparison); } } } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/PropertyValueGetter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/PropertyValueGetter.cs similarity index 87% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/PropertyValueGetter.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/PropertyValueGetter.cs index 9c49532c651..2e9326cd909 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/PropertyValueGetter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/PropertyValueGetter.cs @@ -1,22 +1,20 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Data; +using System.Globalization; namespace Microsoft.Management.UI.Internal { - using System; - using System.ComponentModel; - using System.Data; - using System.Globalization; - /// /// Provides methods for retrieving the property values of objects. /// public class PropertyValueGetter : IPropertyValueGetter { private const string PropertyDescriptorColumnId = "PropertyDescriptor"; + private DataTable cachedProperties; /// @@ -45,18 +43,15 @@ public virtual bool TryGetPropertyValue(string propertyName, object value, out o { propertyValue = null; - if (String.IsNullOrEmpty(propertyName)) + if (string.IsNullOrEmpty(propertyName)) { throw new ArgumentException("propertyName is empty", "propertyName"); } - if (null == value) - { - throw new ArgumentNullException("value"); - } + ArgumentNullException.ThrowIfNull(value); PropertyDescriptor descriptor = this.GetPropertyDescriptor(propertyName, value); - if (null == descriptor) + if (descriptor == null) { return false; } @@ -94,12 +89,12 @@ private PropertyDescriptor GetPropertyDescriptor(string propertyName, object val PropertyDescriptor descriptor; - if (null == propertyRow) + if (propertyRow == null) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value); descriptor = properties[propertyName]; - if (null != descriptor) + if (descriptor != null) { this.cachedProperties.Rows.Add(dataType, propertyName, descriptor); } diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/UIPropertyGroupDescription.cs b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/UIPropertyGroupDescription.cs similarity index 88% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/UIPropertyGroupDescription.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/UIPropertyGroupDescription.cs index 638aef65e39..44d869c3272 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/UIPropertyGroupDescription.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/UIPropertyGroupDescription.cs @@ -1,20 +1,14 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// ExpressionEditor definition -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Collections.Specialized; - using System.ComponentModel; - using System.Diagnostics.CodeAnalysis; - using System.Windows.Data; - /// /// Describes a property that has visual representation and can be sorted and grouped. /// @@ -82,6 +76,7 @@ public ListSortDirection SortDirection { return this.sortDirection; } + set { this.sortDirection = value; diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ViewGroupToStringConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ViewGroupToStringConverter.cs new file mode 100644 index 00000000000..22741a7031b --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ViewGroupToStringConverter.cs @@ -0,0 +1,63 @@ +// 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.Controls; +using System.Windows.Data; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Converter from ViewGroup to group title string. + /// + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + internal class ViewGroupToStringConverter : IValueConverter + { + /// + /// Convert each ViewGroup into its name and its count. + /// + /// Value to be converted. + /// Type to convert the value to. + /// The conversion parameter. + /// Conversion culture. + /// The converted string. + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + CollectionViewGroup cvg = value as CollectionViewGroup; + if (cvg == null) + { + throw new ArgumentException("value must be of type CollectionViewGroup", "value"); + } + + string name = (!string.IsNullOrEmpty(cvg.Name.ToString())) ? cvg.Name.ToString() : UICultureResources.GroupTitleNone; + string display = string.Create(CultureInfo.CurrentCulture, $"{name} ({cvg.ItemCount})"); + + return display; + } + + /// + /// ConvertBack is not supported. + /// + /// Value to be converted. + /// Type to convert the value to. + /// The conversion parameter. + /// Conversion culture. + /// This method is not supported. + /// when calling the method. + public object ConvertBack( + object value, + Type targetType, + object parameter, + System.Globalization.CultureInfo culture) + { + // I can't think of nothing that could be added to the exception message + // that would be of further help + throw new NotSupportedException(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/WaitRing.cs b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/WaitRing.cs new file mode 100644 index 00000000000..c65da37d6f0 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/WaitRing.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Windows; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Waiting Ring class. + /// + public class WaitRing : Control + { + /// + /// Static constructor for WaitRing. + /// + static WaitRing() + { + // This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class. + // This style is defined in themes\generic.xaml + DefaultStyleKeyProperty.OverrideMetadata(typeof(WaitRing), new FrameworkPropertyMetadata(typeof(WaitRing))); + } + } +} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/innerlistcolumn.cs b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/innerlistcolumn.cs similarity index 84% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/innerlistcolumn.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/innerlistcolumn.cs index 9d5998f4b50..965a66239d0 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/innerlistcolumn.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/innerlistcolumn.cs @@ -1,26 +1,19 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements the InnerLisColumn control. -// -//----------------------------------------------------------------------- - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Management.Automation.Internal; +using System.Text; +using System.Windows; +using System.Windows.Automation; +using System.Windows.Controls; +using System.Windows.Data; namespace Microsoft.Management.UI.Internal { - using System; - using System.Windows; - using System.Windows.Automation; - using System.Management.Automation.Internal; - using System.Windows.Data; - using System.Collections.Generic; - using System.Text; - using System.Windows.Controls; - using System.ComponentModel; - using System.Globalization; - /// /// InnerList Columns class. /// Derives and extends GridViewColumn to add concepts such as column visibility. @@ -75,10 +68,7 @@ public InnerListColumn(UIPropertyGroupDescription dataDescription, bool isVisibl /// Whether the column should create a default binding using the specified data's property. public InnerListColumn(UIPropertyGroupDescription dataDescription, bool isVisible, bool createDefaultBinding) { - if (dataDescription == null) - { - throw new ArgumentNullException("dataDescription"); - } + ArgumentNullException.ThrowIfNull(dataDescription); GridViewColumnHeader header = new GridViewColumnHeader(); header.Content = dataDescription.DisplayContent; @@ -94,7 +84,7 @@ public InnerListColumn(UIPropertyGroupDescription dataDescription, bool isVisibl if (createDefaultBinding) { - var defaultBinding = new Binding(GraphicalHostReflectionWrapper.EscapeBinding(dataDescription.PropertyName)); + var defaultBinding = new Binding(dataDescription.PropertyName.Replace("/", " ").Replace(".", " ")); defaultBinding.StringFormat = GetDefaultStringFormat(dataDescription.DataType); defaultBinding.ConverterCulture = CultureInfo.CurrentCulture; @@ -103,8 +93,6 @@ public InnerListColumn(UIPropertyGroupDescription dataDescription, bool isVisibl } #endregion constructor - - #region private methods #region private static methods @@ -127,9 +115,9 @@ partial void OnMinWidthChangedImplementation(PropertyChangedEventArgs e) static partial void MinWidthProperty_ValidatePropertyImplementation(double value, ref bool isValid) { - isValid = ((value >= 0.0) - && !Double.IsNaN(value) - && !Double.IsPositiveInfinity(value)); + isValid = (value >= 0.0) + && !double.IsNaN(value) + && !double.IsPositiveInfinity(value); } /// diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/managementlist.cs b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/managementlist.cs similarity index 83% rename from src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/managementlist.cs rename to src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/managementlist.cs index cc1f223bdb2..4ac51c702d8 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/managementlist.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/managementlist.cs @@ -1,22 +1,17 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. 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.Reflection; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; namespace Microsoft.Management.UI.Internal { - #region Using - using System; - using System.Collections; - using System.Collections.ObjectModel; - using System.ComponentModel; - using System.Reflection; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Input; - #endregion Using - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public partial class ManagementList : Control { @@ -24,7 +19,7 @@ public partial class ManagementList : Control private ReadOnlyCollection logicalChildren; - PropertiesTextContainsFilterRule defaultFullTextSearchRule = new PropertiesTextContainsFilterRule(); + private PropertiesTextContainsFilterRule defaultFullTextSearchRule = new PropertiesTextContainsFilterRule(); private ObservableCollection> views = new ObservableCollection>(); @@ -45,7 +40,7 @@ public IStateDescriptorFactory SavedViewFactory { get { - if (null == this.savedViewFactory) + if (this.savedViewFactory == null) { this.savedViewFactory = new ManagementListStateDescriptorFactory(); } @@ -55,10 +50,7 @@ public IStateDescriptorFactory SavedViewFactory set { - if (null == value) - { - throw new ArgumentNullException("value"); - } + ArgumentNullException.ThrowIfNull(value); this.savedViewFactory = value; } @@ -133,16 +125,16 @@ partial void OnEvaluatorChangedImplementation(PropertyChangedEventArgs 0) { @@ -182,10 +174,7 @@ private void Evaluator_PropertyChanged(object sender, PropertyChangedEventArgs e /// The specified value is a null reference. public void AddColumn(InnerListColumn column) { - if (column == null) - { - throw new ArgumentNullException("column"); - } + ArgumentNullException.ThrowIfNull(column); this.AddColumn(column, this.IsFilterShown); } @@ -198,10 +187,7 @@ public void AddColumn(InnerListColumn column) /// The specified value is a null reference. public void AddColumn(InnerListColumn column, bool addDefaultFilterRules) { - if (column == null) - { - throw new ArgumentNullException("column"); - } + ArgumentNullException.ThrowIfNull(column); this.List.Columns.Add(column); @@ -234,10 +220,7 @@ public void AddColumn(InnerListColumn column, bool addDefaultFilterRules) /// The specified value is a null reference. public void AddRule(FilterRule rule) { - if (rule == null) - { - throw new ArgumentNullException("rule"); - } + ArgumentNullException.ThrowIfNull(rule); this.AddFilterRulePicker.ShortcutFilterRules.Add(new AddFilterRulePickerItem(new FilterRulePanelItem(rule, rule.DisplayName))); } @@ -251,7 +234,7 @@ public void ResetView() this.defaultFullTextSearchRule.PropertyNames.Clear(); this.AddFilterRulePicker.ShortcutFilterRules.Clear(); this.AddFilterRulePicker.ColumnFilterRules.Clear(); - this.SearchBox.Text = String.Empty; + this.SearchBox.Text = string.Empty; this.SearchBox.Parser.ClearSearchableRules(); this.SearchBox.Parser.FullTextRule = this.defaultFullTextSearchRule; } @@ -263,8 +246,8 @@ public void ResetView() partial void OnStartFilterCanExecuteImplementation(CanExecuteRoutedEventArgs e) { // Allow filtering if there is a filter expression or filtering has been triggered \\ - e.CanExecute = (this.Evaluator.HasFilterExpression || - this.Evaluator.FilterStatus != FilterStatus.NotApplied); + e.CanExecute = this.Evaluator.HasFilterExpression || + this.Evaluator.FilterStatus != FilterStatus.NotApplied; } partial void OnStartFilterExecutedImplementation(ExecutedRoutedEventArgs e) @@ -278,7 +261,7 @@ partial void OnStartFilterExecutedImplementation(ExecutedRoutedEventArgs e) partial void OnStopFilterCanExecuteImplementation(CanExecuteRoutedEventArgs e) { - e.CanExecute = (this.Evaluator.FilterStatus == FilterStatus.InProgress); + e.CanExecute = this.Evaluator.FilterStatus == FilterStatus.InProgress; } partial void OnStopFilterExecutedImplementation(ExecutedRoutedEventArgs e) @@ -292,7 +275,7 @@ partial void OnStopFilterExecutedImplementation(ExecutedRoutedEventArgs e) partial void OnClearFilterCanExecuteImplementation(CanExecuteRoutedEventArgs e) { - e.CanExecute = (this.FilterRulePanel.FilterRulePanelItems.Count > 0); + e.CanExecute = this.FilterRulePanel.FilterRulePanelItems.Count > 0; } partial void OnClearFilterExecutedImplementation(ExecutedRoutedEventArgs e) @@ -316,7 +299,7 @@ partial void OnClearFilterExecutedImplementation(ExecutedRoutedEventArgs e) partial void OnSaveViewCanExecuteImplementation(CanExecuteRoutedEventArgs e) { string viewName = (string)e.Parameter; - bool isNotEmpty = !String.IsNullOrEmpty(viewName) && (0 != viewName.Trim().Length); + bool isNotEmpty = !string.IsNullOrEmpty(viewName) && (viewName.Trim().Length != 0); e.CanExecute = isNotEmpty; } @@ -328,7 +311,7 @@ partial void OnSaveViewExecutedImplementation(ExecutedRoutedEventArgs e) StateDescriptor sd = null; - if (null == (sd = this.DoesViewAlreadyExist(viewName))) + if ((sd = this.DoesViewAlreadyExist(viewName)) == null) { sd = this.SavedViewFactory.Create(); sd.Name = viewName; @@ -357,7 +340,7 @@ private StateDescriptor DoesViewAlreadyExist(string viewName) private void ViewManager_ItemSelected(object sender, DataRoutedEventArgs e) { - if (null == e.Data) + if (e.Data == null) { throw new ArgumentException("e.Data is null", "e"); } @@ -370,7 +353,7 @@ private void ViewManager_ItemSelected(object sender, DataRoutedEventArgs private void ViewManager_ItemDeleted(object sender, DataRoutedEventArgs e) { - if (null == e.Data) + if (e.Data == null) { throw new ArgumentException("e.Data is null", "e"); } @@ -380,7 +363,7 @@ private void ViewManager_ItemDeleted(object sender, DataRoutedEventArgs this.RaiseEvent(new RoutedEventArgs(ViewsChangedEvent)); - if (Object.ReferenceEquals(sd, this.CurrentView)) + if (object.ReferenceEquals(sd, this.CurrentView)) { this.CurrentView = null; } @@ -414,10 +397,9 @@ private void Evaluator_FilterExpressionChanged(object sender, EventArgs e) // For non-live mode, stop filtering if the user has cleared the filter (rules and search text). // This allows the user to clear search results without having to click the Search button on an empty filter. // This happens automatically in live mode. - - if (false == this.Evaluator.StartFilterOnExpressionChanged && - false == this.FilterRulePanel.HasFilterExpression && - false == this.SearchBox.HasFilterExpression) + if (this.Evaluator.StartFilterOnExpressionChanged == false && + this.FilterRulePanel.HasFilterExpression == false && + this.SearchBox.HasFilterExpression == false) { this.Evaluator.StopFilter(); } @@ -425,4 +407,4 @@ private void Evaluator_FilterExpressionChanged(object sender, EventArgs e) #endregion Hooks For Changing Filter State Due To Events } -} \ No newline at end of file +} diff --git a/src/Microsoft.Management.UI.Internal/Microsoft.PowerShell.GraphicalHost.csproj b/src/Microsoft.Management.UI.Internal/Microsoft.PowerShell.GraphicalHost.csproj new file mode 100644 index 00000000000..acc4eae2c61 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/Microsoft.PowerShell.GraphicalHost.csproj @@ -0,0 +1,38 @@ + + + + Assembly containing WPF code for Out-GridView, HelpWindow, and Show-Command + $(NoWarn);CS1570 + Microsoft.Management.UI.Internal + Microsoft.PowerShell.GraphicalHost + false + Windows + 8.0 + true + True + + + + $(DefineConstants);CORECLR + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/AllModulesControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/AllModulesControl.xaml new file mode 100644 index 00000000000..3729feb4a28 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/AllModulesControl.xaml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.PowerShell.GraphicalHost/xamls/ParameterSetControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml similarity index 76% rename from src/Microsoft.PowerShell.GraphicalHost/xamls/ParameterSetControl.xaml rename to src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml index dfb341e6d66..c59f519d663 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/ParameterSetControl.xaml +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml @@ -1,4 +1,9 @@ - + + + /// 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.PowerShell.GraphicalHost/ShowCommand/ShowCommandSettings.Designer.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.Designer.cs similarity index 97% rename from src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ShowCommandSettings.Designer.cs rename to src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.Designer.cs index 7683bbee26c..18194cec8fb 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ShowCommandSettings.Designer.cs +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.Designer.cs @@ -1,4 +1,6 @@ -//------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.16808 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.PowerShell.GraphicalHost/ShowCommand/ViewModel/AllModulesViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/AllModulesViewModel.cs similarity index 79% rename from src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/AllModulesViewModel.cs rename to src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/AllModulesViewModel.cs index 8576bfa68c7..04fc95e4223 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/AllModulesViewModel.cs +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/AllModulesViewModel.cs @@ -1,20 +1,19 @@ -//----------------------------------------------------------------------- -// -// Copyright © 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.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 { - 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; - /// /// Contains all Commands, Parameters, ParameterSet and Common Parameter. /// @@ -22,17 +21,17 @@ public class AllModulesViewModel : INotifyPropertyChanged { #region Private Fields /// - /// Flag indicating a wait message is being displayed + /// Flag indicating a wait message is being displayed. /// private bool waitMessageDisplayed; /// - /// True if this ViewModel is not supposed to show common parameters + /// True if this ViewModel is not supposed to show common parameters. /// private bool noCommonParameter; /// - /// the filterName of command + /// the filterName of command. /// private string commandNameFilter; @@ -42,45 +41,47 @@ public class AllModulesViewModel : INotifyPropertyChanged private List modules; /// - /// true if a command can be run + /// true if a command can be run. /// private bool canRun; /// - /// true if a command can be copied + /// true if a command can be copied. /// private bool canCopy; /// - /// the selected module being displayed in the GUI + /// the selected module being displayed in the GUI. /// private ModuleViewModel selectedModule; /// - /// the visibility of the refresh button + /// 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 + /// Provides an extra viewModel object that allows callers to control certain aspects of the GUI. /// private object extraViewModel; /// - /// private property for ZoomLevel + /// private property for ZoomLevel. /// private double zoomLevel = 1.0; #endregion #region Construction and Destructor /// - /// Initializes a new instance of the AllModulesViewModel class + /// Initializes a new instance of the AllModulesViewModel class. /// - /// the loaded modules - /// commands to show + /// The loaded modules. + /// Commands to show. public AllModulesViewModel(Dictionary importedModules, IEnumerable commands) { - if (commands == null || !commands.GetEnumerator().MoveNext()) + ArgumentNullException.ThrowIfNull(commands); + + if (!commands.GetEnumerator().MoveNext()) { throw new ArgumentNullException("commands"); } @@ -89,17 +90,14 @@ public AllModulesViewModel(Dictionary importedMod } /// - /// Initializes a new instance of the AllModulesViewModel class + /// Initializes a new instance of the AllModulesViewModel class. /// - /// the loaded modules - /// All PowerShell commands - /// true not to show common parameters + /// The loaded modules. + /// All PowerShell commands. + /// True not to show common parameters. public AllModulesViewModel(Dictionary importedModules, IEnumerable commands, bool noCommonParameter) { - if (commands == null) - { - throw new ArgumentNullException("commands"); - } + ArgumentNullException.ThrowIfNull(commands); this.Initialization(importedModules, commands, noCommonParameter); } @@ -108,35 +106,35 @@ public AllModulesViewModel(Dictionary importedMod #region INotifyPropertyChanged Members /// - /// PropertyChanged Event + /// PropertyChanged Event. /// public event PropertyChangedEventHandler PropertyChanged; #endregion /// - /// Indicates the selected command in the selected module needs to display the help for a command + /// 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 + /// 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 + /// Indicates the selected command in the selected module should be run. /// public event EventHandler RunSelectedCommandInSelectedModule; /// - /// Indicates we want to refresh the viewModel + /// Indicates we want to refresh the viewModel. /// public event EventHandler Refresh; #region Public Properties /// - /// Get or Sets Zoom level + /// Get or Sets Zoom level. /// public double ZoomLevel { @@ -156,15 +154,15 @@ public double ZoomLevel } /// - /// Gets the tooltip for the refresh button + /// Gets the tooltip for the refresh button. /// public static string RefreshTooltip { - get { return String.Format(CultureInfo.CurrentUICulture, ShowCommandResources.RefreshShowCommandTooltipFormat, "import-module"); } + get { return string.Format(CultureInfo.CurrentUICulture, ShowCommandResources.RefreshShowCommandTooltipFormat, "import-module"); } } /// - /// Gets or sets the visibility of the refresh button + /// Gets or sets the visibility of the refresh button. /// public Visibility RefreshVisibility { @@ -186,7 +184,7 @@ public Visibility RefreshVisibility } /// - /// Gets a value indicating whether common parameters are displayed + /// Gets a value indicating whether common parameters are displayed. /// public bool NoCommonParameter { @@ -194,7 +192,7 @@ public bool NoCommonParameter } /// - /// Gets or sets the filterName of command + /// Gets or sets the filterName of command. /// public string CommandNameFilter { @@ -222,7 +220,7 @@ public string CommandNameFilter } /// - /// Gets or sets the selected module being displayed in the GUI + /// Gets or sets the selected module being displayed in the GUI. /// public ModuleViewModel SelectedModule { @@ -240,10 +238,10 @@ public ModuleViewModel SelectedModule if (this.selectedModule != null) { - this.selectedModule.SelectedCommandNeedsImportModule -= new EventHandler(this.SelectedModule_SelectedCommandNeedsImportModule); - this.selectedModule.SelectedCommandNeedsHelp -= new EventHandler(this.SelectedModule_SelectedCommandNeedsHelp); - this.selectedModule.RunSelectedCommand -= new EventHandler(this.SelectedModule_RunSelectedCommand); - this.selectedModule.PropertyChanged -= new PropertyChangedEventHandler(this.SelectedModule_PropertyChanged); + 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; @@ -253,10 +251,10 @@ public ModuleViewModel SelectedModule if (this.selectedModule != null) { this.selectedModule.RefreshFilteredCommands(this.CommandNameFilter); - this.selectedModule.SelectedCommandNeedsImportModule += new EventHandler(this.SelectedModule_SelectedCommandNeedsImportModule); - this.selectedModule.SelectedCommandNeedsHelp += new EventHandler(this.SelectedModule_SelectedCommandNeedsHelp); - this.selectedModule.RunSelectedCommand += new EventHandler(this.SelectedModule_RunSelectedCommand); - this.selectedModule.PropertyChanged += new PropertyChangedEventHandler(this.SelectedModule_PropertyChanged); + 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; } @@ -265,7 +263,7 @@ public ModuleViewModel SelectedModule } /// - /// Gets a value indicating whether we can run a command + /// Gets a value indicating whether we can run a command. /// public bool CanRun { @@ -276,7 +274,7 @@ public bool CanRun } /// - /// Gets a value indicating whether we can copy a command + /// Gets a value indicating whether we can copy a command. /// public bool CanCopy { @@ -295,7 +293,7 @@ public List Modules } /// - /// Gets the visibility of the wait message + /// Gets the visibility of the wait message. /// public Visibility WaitMessageVisibility { @@ -306,7 +304,7 @@ public Visibility WaitMessageVisibility } /// - /// Gets the visibility of the main grid + /// Gets the visibility of the main grid. /// public Visibility MainGridVisibility { @@ -317,7 +315,7 @@ public Visibility MainGridVisibility } /// - /// Gets a value indicating whether the main grid is displayed + /// Gets a value indicating whether the main grid is displayed. /// public bool MainGridDisplayed { @@ -328,7 +326,7 @@ public bool MainGridDisplayed } /// - /// Gets or sets a value indicating whether the wait message is displayed + /// Gets or sets a value indicating whether the wait message is displayed. /// public bool WaitMessageDisplayed { @@ -355,7 +353,7 @@ public bool WaitMessageDisplayed } /// - /// Gets or sets an extra viewModel object that allows callers to control certain aspects of the GUI + /// Gets or sets an extra viewModel object that allows callers to control certain aspects of the GUI. /// public object ExtraViewModel { @@ -378,9 +376,9 @@ public object ExtraViewModel #endregion /// - /// Returns the selected script + /// Returns the selected script. /// - /// the selected script + /// The selected script. public string GetScript() { if (this.SelectedModule == null) @@ -397,7 +395,7 @@ public string GetScript() } /// - /// Triggers Refresh + /// Triggers Refresh. /// internal void OnRefresh() { @@ -412,7 +410,7 @@ internal void OnRefresh() /// /// If current modules name is ALL, then return true. /// - /// The modules name + /// The modules name. /// Return true is the module name is ALLModulesViewModel. private static bool IsAll(string name) { @@ -424,8 +422,8 @@ private static bool IsAll(string name) /// SetCanRun for IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues /// SetCanCopy for SetCanCopy /// - /// event sender - /// event arguments + /// Event sender. + /// Event arguments. private void SelectedModule_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues") @@ -480,9 +478,9 @@ private void SetCanCopy() /// /// Initialize AllModulesViewModel. /// - /// All loaded modules - /// List of commands in all modules - /// Whether showing common parameter + /// 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) @@ -556,9 +554,9 @@ private void Initialization(Dictionary importedMo /// /// Compare two ModuleViewModel target and source. /// - /// The source ModuleViewModel - /// The target ModuleViewModel - /// Compare result + /// The source ModuleViewModel. + /// The target ModuleViewModel. + /// Compare result. private int Compare(ModuleViewModel source, ModuleViewModel target) { if (AllModulesViewModel.IsAll(source.Name) && !AllModulesViewModel.IsAll(target.Name)) @@ -571,33 +569,33 @@ private int Compare(ModuleViewModel source, ModuleViewModel target) return 1; } - return String.Compare(source.Name, target.Name, StringComparison.OrdinalIgnoreCase); + return string.Compare(source.Name, target.Name, StringComparison.OrdinalIgnoreCase); } /// - /// Called when the SelectedCommandNeedsHelp event is triggered in the Selected Module + /// Called when the SelectedCommandNeedsHelp event is triggered in the Selected Module. /// - /// event sender - /// event arguments + /// 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 + /// Called when the SelectedCommandNeedsImportModule event is triggered in the Selected Module. /// - /// event sender - /// event arguments + /// Event sender. + /// Event arguments. private void SelectedModule_SelectedCommandNeedsImportModule(object sender, ImportModuleEventArgs e) { this.OnSelectedCommandInSelectedModuleNeedsImportModule(e); } /// - /// Triggers SelectedCommandInSelectedModuleNeedsHelp + /// Triggers SelectedCommandInSelectedModuleNeedsHelp. /// - /// event arguments + /// Event arguments. private void OnSelectedCommandInSelectedModuleNeedsHelp(HelpNeededEventArgs e) { EventHandler handler = this.SelectedCommandInSelectedModuleNeedsHelp; @@ -608,9 +606,9 @@ private void OnSelectedCommandInSelectedModuleNeedsHelp(HelpNeededEventArgs e) } /// - /// Triggers SelectedCommandInSelectedModuleNeedsImportModule + /// Triggers SelectedCommandInSelectedModuleNeedsImportModule. /// - /// event arguments + /// Event arguments. private void OnSelectedCommandInSelectedModuleNeedsImportModule(ImportModuleEventArgs e) { EventHandler handler = this.SelectedCommandInSelectedModuleNeedsImportModule; @@ -621,19 +619,19 @@ private void OnSelectedCommandInSelectedModuleNeedsImportModule(ImportModuleEven } /// - /// Called when the RunSelectedCommand is triggered in the selected module + /// Called when the RunSelectedCommand is triggered in the selected module. /// - /// event sender - /// event arguments + /// Event sender. + /// Event arguments. private void SelectedModule_RunSelectedCommand(object sender, CommandEventArgs e) { this.OnRunSelectedCommandInSelectedModule(e); } /// - /// Triggers RunSelectedCommandInSelectedModule + /// Triggers RunSelectedCommandInSelectedModule. /// - /// event arguments + /// Event arguments. private void OnRunSelectedCommandInSelectedModule(CommandEventArgs e) { EventHandler handler = this.RunSelectedCommandInSelectedModule; @@ -644,15 +642,15 @@ private void OnRunSelectedCommandInSelectedModule(CommandEventArgs e) } /// - /// If property changed will be notify + /// If property changed will be notify. /// - /// The changed property + /// The changed property. private void OnNotifyPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = this.PropertyChanged; if (handler != null) { - handler (this, new PropertyChangedEventArgs(propertyName)); + 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.PowerShell.GraphicalHost/ShowCommand/ViewModel/CommandViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandViewModel.cs similarity index 82% rename from src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/CommandViewModel.cs rename to src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandViewModel.cs index c9b601f77ad..cfa2798963c 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/CommandViewModel.cs +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandViewModel.cs @@ -1,25 +1,25 @@ -//----------------------------------------------------------------------- -// -// 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.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 { - 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 SMAI = System.Management.Automation.Internal; - using Microsoft.PowerShell.Commands.ShowCommandExtension; - /// /// Contains information about a cmdlet's Shard ParameterSet, /// ParameterSets, Parameters, Common Parameters and error message. @@ -28,22 +28,22 @@ public class CommandViewModel : INotifyPropertyChanged { #region Private Fields /// - /// The name of the AllParameterSets + /// The name of the AllParameterSets. /// private const string SharedParameterSetName = "__AllParameterSets"; /// - /// Grid length constant + /// Grid length constant. /// private static readonly GridLength star = new GridLength(1, GridUnitType.Star); /// - /// The module containing this cmdlet in the gui + /// The module containing this cmdlet in the gui. /// private ModuleViewModel parentModule; /// - /// The name of the default ParameterSet + /// The name of the default ParameterSet. /// private string defaultParameterSetName; @@ -70,31 +70,31 @@ public class CommandViewModel : INotifyPropertyChanged /// /// Field used for the CommonParameters parameter. /// - private ParameterSetViewModel commonParameters; + private ParameterSetViewModel comonParameters; /// - /// The ShowCommandCommandInfo this model is based on + /// The ShowCommandCommandInfo this model is based on. /// private ShowCommandCommandInfo commandInfo; /// - /// value indicating whether the selected parameter set has all mandatory parameters valid + /// 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 + /// 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 + /// 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 + /// Prevents a default instance of the CommandViewModel class from being created. /// private CommandViewModel() { @@ -103,25 +103,25 @@ private CommandViewModel() #region INotifyPropertyChanged Members /// - /// PropertyChanged Event + /// PropertyChanged Event. /// public event PropertyChangedEventHandler PropertyChanged; #endregion /// - /// Indicates the command needs to display the help for a command + /// Indicates the command needs to display the help for a command. /// public event EventHandler HelpNeeded; /// - /// Indicates a module needs to be imported + /// 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 + /// Gets or sets a value indicating whether the command name should be qualified by the module in GetScript. /// public bool ModuleQualifyCommandName { @@ -130,7 +130,7 @@ public bool ModuleQualifyCommandName } /// - /// Gets or sets a value indicating whether the common parameters are expanded + /// Gets or sets a value indicating whether the common parameters are expanded. /// public bool AreCommonParametersExpanded { @@ -168,13 +168,13 @@ public ParameterSetViewModel SelectedParameterSet { if (this.selectedParameterSet != null) { - this.selectedParameterSet.PropertyChanged -= new PropertyChangedEventHandler(this.SelectedParameterSet_PropertyChanged); + this.selectedParameterSet.PropertyChanged -= this.SelectedParameterSet_PropertyChanged; } this.selectedParameterSet = value; if (this.selectedParameterSet != null) { - this.selectedParameterSet.PropertyChanged += new PropertyChangedEventHandler(this.SelectedParameterSet_PropertyChanged); + this.selectedParameterSet.PropertyChanged += this.SelectedParameterSet_PropertyChanged; this.SelectedParameterSetAllMandatoryParametersHaveValues = this.SelectedParameterSet.AllMandatoryParametersHaveValues; } else @@ -227,7 +227,7 @@ public Visibility ParameterSetTabControlVisibility } /// - /// Gets the visibility for the single ParameterSetControl displayed when there is only 1 parameter set + /// Gets the visibility for the single ParameterSetControl displayed when there is only 1 parameter set. /// public Visibility SingleParameterSetControlVisibility { @@ -239,7 +239,7 @@ public Visibility SingleParameterSetControlVisibility /// public ParameterSetViewModel CommonParameters { - get { return this.commonParameters; } + get { return this.comonParameters; } } /// @@ -251,7 +251,7 @@ public Visibility CommonParameterVisibility } /// - /// Gets or sets the height for common parameters that will depend on CommonParameterVisibility + /// Gets or sets the height for common parameters that will depend on CommonParameterVisibility. /// public GridLength CommonParametersHeight { @@ -273,18 +273,18 @@ public GridLength CommonParametersHeight } /// - /// Gets the visibility for the control displayed when the module is not imported + /// Gets the visibility for the control displayed when the module is not imported. /// public Visibility NotImportedVisibility { get { - return this.IsImported ? Visibility.Collapsed : Visibility.Visible; + return this.IsImported ? Visibility.Collapsed : Visibility.Visible; } } /// - /// Gets the visibility for the control displayed when there are no parameters + /// Gets the visibility for the control displayed when there are no parameters. /// public Visibility NoParameterVisibility { @@ -296,7 +296,7 @@ public Visibility NoParameterVisibility } /// - /// Gets a value indicating whether the cmdlet comes from a module which is imported + /// Gets a value indicating whether the cmdlet comes from a module which is imported. /// public bool IsImported { @@ -323,7 +323,7 @@ public string Name } /// - /// Gets the module path if it is not null or empty, or the name otherwise + /// Gets the module path if it is not null or empty, or the name otherwise. /// public string ModuleName { @@ -350,13 +350,13 @@ public ModuleViewModel ParentModule } /// - /// Gets Tooltip string for the cmdlet + /// Gets Tooltip string for the cmdlet. /// public string ToolTip { get { - return String.Format( + return string.Format( CultureInfo.CurrentCulture, ShowCommandResources.CmdletTooltipFormat, this.Name, @@ -366,13 +366,13 @@ public string ToolTip } /// - /// Gets the message to be displayed when the cmdlet belongs to a module that is not imported + /// Gets the message to be displayed when the cmdlet belongs to a module that is not imported. /// public string ImportModuleMessage { get { - return String.Format( + return string.Format( CultureInfo.CurrentCulture, ShowCommandResources.NotImportedFormat, this.ModuleName, @@ -382,7 +382,7 @@ public string ImportModuleMessage } /// - /// Gets the title for the cmdlet details + /// Gets the title for the cmdlet details. /// public string DetailsTitle { @@ -390,14 +390,14 @@ public string DetailsTitle { if (this.IsImported) { - return String.Format( + return string.Format( CultureInfo.CurrentCulture, ShowCommandResources.DetailsParameterTitleFormat, this.Name); } else { - return String.Format( + return string.Format( CultureInfo.CurrentCulture, ShowCommandResources.NameLabelFormat, this.Name); @@ -407,7 +407,7 @@ public string DetailsTitle #endregion /// - /// Gets a Grid length constant + /// Gets a Grid length constant. /// internal static GridLength Star { @@ -417,33 +417,33 @@ internal static GridLength Star /// /// Gets the builded PowerShell script. /// - /// Return script as string + /// 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)) + if (this.ModuleQualifyCommandName && !string.IsNullOrEmpty(this.ModuleName)) { commandName = this.ModuleName + "\\" + commandName; } - if (commandName.IndexOf(' ') != -1) + if (commandName.Contains(' ')) { - builder.AppendFormat("& \"{0}\"", commandName); + builder.Append($"& \"{commandName}\""); } else { builder.Append(commandName); } - builder.Append(" "); + builder.Append(' '); if (this.SelectedParameterSet != null) { builder.Append(this.SelectedParameterSet.GetScript()); - builder.Append(" "); + builder.Append(' '); } if (this.CommonParameters != null) @@ -467,7 +467,7 @@ public void OpenHelpWindow() /// /// Determines whether current command name and a specified ParameterSetName have same name. /// - /// The name of ShareParameterSet + /// The name of ShareParameterSet. /// Return true is ShareParameterSet. Else return false. internal static bool IsSharedParameterSetName(string name) { @@ -477,9 +477,9 @@ internal static bool IsSharedParameterSetName(string name) /// /// Creates a new CommandViewModel out the . /// - /// Module to which the CommandViewModel will belong to - /// Will showing command - /// true to omit displaying common parameter + /// 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 @@ -487,13 +487,10 @@ internal static bool IsSharedParameterSetName(string name) /// are retrieved: /// function CrashMe ([I.Am.A.Type.That.Does.Not.Exist]$name) {} /// - /// The CommandViewModel corresponding to commandInfo + /// The CommandViewModel corresponding to commandInfo. internal static CommandViewModel GetCommandViewModel(ModuleViewModel module, ShowCommandCommandInfo commandInfo, bool noCommonParameters) { - if (commandInfo == null) - { - throw new ArgumentNullException("commandInfo"); - } + ArgumentNullException.ThrowIfNull(commandInfo); CommandViewModel returnValue = new CommandViewModel(); returnValue.commandInfo = commandInfo; @@ -502,8 +499,6 @@ internal static CommandViewModel GetCommandViewModel(ModuleViewModel module, Sho Dictionary commonParametersTable = new Dictionary(); - bool isWorkflow = (commandInfo.CommandType & CommandTypes.Workflow) != 0; - foreach (ShowCommandParameterSetInfo parameterSetInfo in commandInfo.ParameterSets) { if (parameterSetInfo.IsDefault) @@ -515,9 +510,8 @@ internal static CommandViewModel GetCommandViewModel(ModuleViewModel module, Sho foreach (ShowCommandParameterInfo parameterInfo in parameterSetInfo.Parameters) { bool isCommon = Cmdlet.CommonParameters.Contains(parameterInfo.Name); - bool isCommonWorkflow = isWorkflow && SMAI.CommonParameters.CommonWorkflowParameters.Contains(parameterInfo.Name, StringComparer.OrdinalIgnoreCase); - if (isCommon || isCommonWorkflow) + if (isCommon) { if (!commonParametersTable.ContainsKey(parameterInfo.Name)) { @@ -537,7 +531,7 @@ internal static CommandViewModel GetCommandViewModel(ModuleViewModel module, Sho } List commonParametersList = commonParametersTable.Values.ToList(); - returnValue.commonParameters = new ParameterSetViewModel(String.Empty, commonParametersList); + returnValue.comonParameters = new ParameterSetViewModel(string.Empty, commonParametersList); returnValue.parameterSets.Sort(returnValue.Compare); @@ -557,7 +551,7 @@ internal static CommandViewModel GetCommandViewModel(ModuleViewModel module, Sho } /// - /// Called to trigger the event fired when help is needed for the command + /// Called to trigger the event fired when help is needed for the command. /// internal void OnHelpNeeded() { @@ -569,7 +563,7 @@ internal void OnHelpNeeded() } /// - /// Called to trigger the event fired when a module needs to be imported + /// Called to trigger the event fired when a module needs to be imported. /// internal void OnImportModule() { @@ -582,7 +576,7 @@ internal void OnImportModule() #region Private Methods /// - /// Called to set the height for common parameters initially or when the AreCommonParametersExpanded changes + /// Called to set the height for common parameters initially or when the AreCommonParametersExpanded changes. /// private void SetCommonParametersHeight() { @@ -590,11 +584,11 @@ private void SetCommonParametersHeight() } /// - /// Compares source and target by being the default parameter set and then by name + /// Compares source and target by being the default parameter set and then by name. /// - /// source parameterset - /// target parameterset - /// 0 if they are the same, -1 if source is smaller, 1 if source is larger + /// 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) @@ -615,13 +609,13 @@ private int Compare(ParameterSetViewModel source, ParameterSetViewModel target) } } - return String.Compare(source.Name, target.Name, StringComparison.Ordinal); + return string.CompareOrdinal(source.Name, target.Name); } /// - /// If property changed will be notify + /// If property changed will be notify. /// - /// The changed property + /// The changed property. private void OnNotifyPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = this.PropertyChanged; @@ -632,10 +626,10 @@ private void OnNotifyPropertyChanged(string propertyName) } /// - /// Called when the PropertyChanged event is triggered on the SelectedParameterSet + /// Called when the PropertyChanged event is triggered on the SelectedParameterSet. /// - /// event sender - /// event arguments + /// Event sender. + /// Event arguments. private void SelectedParameterSet_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (!e.PropertyName.Equals("AllMandatoryParametersHaveValues")) 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.PowerShell.GraphicalHost/ShowCommand/ViewModel/ImportModuleEventArgs.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ImportModuleEventArgs.cs similarity index 78% rename from src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/ImportModuleEventArgs.cs rename to src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ImportModuleEventArgs.cs index 58260e0949a..3d7a7ccf3f0 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/ImportModuleEventArgs.cs +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ImportModuleEventArgs.cs @@ -1,25 +1,22 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; namespace Microsoft.PowerShell.Commands.ShowCommandInternal { - using System; - /// - /// Arguments for the event triggered when it is necessary to display help for a command + /// 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 + /// 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 + /// the module path or name for the module we want to import. /// private string parentModuleName; @@ -32,8 +29,8 @@ public class ImportModuleEventArgs : EventArgs /// /// 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 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 @@ -46,7 +43,7 @@ public ImportModuleEventArgs(string commandName, string parentModuleName, string } /// - /// Gets the name for the command belonging to the module to be imported + /// Gets the name for the command belonging to the module to be imported. /// public string CommandName { @@ -54,7 +51,7 @@ public string CommandName } /// - /// Gets the module path or name for the module we want to import + /// Gets the module path or name for the module we want to import. /// public string ParentModuleName { diff --git a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/ModuleViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ModuleViewModel.cs similarity index 75% rename from src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/ModuleViewModel.cs rename to src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ModuleViewModel.cs index 84095b5a789..950dbe93758 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/ModuleViewModel.cs +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ModuleViewModel.cs @@ -1,26 +1,25 @@ -//----------------------------------------------------------------------- -// -// 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.ComponentModel; +using System.Management.Automation; +using System.Windows; + +using Microsoft.Management.UI.Internal; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + namespace Microsoft.PowerShell.Commands.ShowCommandInternal { - 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; - /// /// ModuleViewModel Contains information about a PowerShell module. /// public class ModuleViewModel : INotifyPropertyChanged { /// - /// True if the module is imported + /// True if the module is imported. /// private bool isModuleImported; @@ -30,12 +29,12 @@ public class ModuleViewModel : INotifyPropertyChanged private string name; /// - /// Filter commands property of this module + /// Filter commands property of this module. /// private ObservableCollection filteredCommands; /// - /// The selected command property of this module + /// The selected command property of this module. /// private CommandViewModel selectedCommand; @@ -51,12 +50,12 @@ public class ModuleViewModel : INotifyPropertyChanged private bool isThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues; /// - /// value indicating whether there is a selected command + /// value indicating whether there is a selected command. /// private bool isThereASelectedCommand; /// - /// The AllModulesViewModel containing this, if any + /// The AllModulesViewModel containing this, if any. /// private AllModulesViewModel allModules; @@ -64,14 +63,11 @@ public class ModuleViewModel : INotifyPropertyChanged /// /// Initializes a new instance of the ModuleViewModel class. /// - /// Module name - /// All loaded modules + /// Module name. + /// All loaded modules. public ModuleViewModel(string name, Dictionary importedModules) { - if (name == null) - { - throw new ArgumentNullException("name"); - } + ArgumentNullException.ThrowIfNull(name); this.name = name; this.commands = new List(); @@ -93,29 +89,29 @@ public ModuleViewModel(string name, Dictionary im #region INotifyPropertyChanged Members /// - /// PropertyChanged Event + /// PropertyChanged Event. /// public event PropertyChangedEventHandler PropertyChanged; #endregion /// - /// Indicates the selected command in needs to display the help for a command + /// 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 + /// Indicates the selected command needs to import a module. /// public event EventHandler SelectedCommandNeedsImportModule; /// - /// Indicates the selected command should be run + /// Indicates the selected command should be run. /// public event EventHandler RunSelectedCommand; #region Public Property /// - /// Gets the name property of this ModuleView + /// Gets the name property of this ModuleView. /// public string Name { @@ -123,13 +119,13 @@ public string Name } /// - /// Gets the GUI friendly module name + /// Gets the GUI friendly module name. /// public string DisplayName { get { - if (!String.IsNullOrEmpty(this.name)) + if (!string.IsNullOrEmpty(this.name)) { return this.name; } @@ -139,7 +135,7 @@ public string DisplayName } /// - /// Gets CommandControl is visibility or not + /// Gets CommandControl is visibility or not. /// public Visibility CommandControlVisibility { @@ -147,7 +143,7 @@ public Visibility CommandControlVisibility } /// - /// Gets CommandControl Height + /// Gets CommandControl Height. /// public GridLength CommandRowHeight { @@ -155,7 +151,7 @@ public GridLength CommandRowHeight } /// - /// Gets the commands under in this module + /// Gets the commands under in this module. /// public List Commands { @@ -163,7 +159,7 @@ public List Commands } /// - /// Gets the filter commands of this module + /// Gets the filter commands of this module. /// public ObservableCollection FilteredCommands { @@ -171,7 +167,7 @@ public ObservableCollection FilteredCommands } /// - /// Gets or sets the selected commands of this module + /// Gets or sets the selected commands of this module. /// public CommandViewModel SelectedCommand { @@ -189,9 +185,9 @@ public CommandViewModel SelectedCommand if (this.selectedCommand != null) { - this.selectedCommand.PropertyChanged -= new PropertyChangedEventHandler(this.SelectedCommand_PropertyChanged); - this.selectedCommand.HelpNeeded -= new EventHandler(this.SelectedCommand_HelpNeeded); - this.selectedCommand.ImportModule -= new EventHandler(this.SelectedCommand_ImportModule); + this.selectedCommand.PropertyChanged -= this.SelectedCommand_PropertyChanged; + this.selectedCommand.HelpNeeded -= this.SelectedCommand_HelpNeeded; + this.selectedCommand.ImportModule -= this.SelectedCommand_ImportModule; } this.selectedCommand = value; @@ -200,9 +196,9 @@ public CommandViewModel SelectedCommand if (this.selectedCommand != null) { - this.selectedCommand.PropertyChanged += new PropertyChangedEventHandler(this.SelectedCommand_PropertyChanged); - this.selectedCommand.HelpNeeded += new EventHandler(this.SelectedCommand_HelpNeeded); - this.selectedCommand.ImportModule += new EventHandler(this.SelectedCommand_ImportModule); + this.selectedCommand.PropertyChanged += this.SelectedCommand_PropertyChanged; + this.selectedCommand.HelpNeeded += this.SelectedCommand_HelpNeeded; + this.selectedCommand.ImportModule += this.SelectedCommand_ImportModule; this.IsThereASelectedCommand = true; } else @@ -217,7 +213,7 @@ public CommandViewModel SelectedCommand } /// - /// Gets or sets a value indicating whether there is a selected command + /// Gets or sets a value indicating whether there is a selected command. /// public bool IsThereASelectedCommand { @@ -264,7 +260,7 @@ public bool IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues } /// - /// Gets the AllModulesViewModel containing this, if any + /// Gets the AllModulesViewModel containing this, if any. /// public AllModulesViewModel AllModules { @@ -276,7 +272,7 @@ public AllModulesViewModel AllModules #endregion /// - /// Gets a value indicating whether the module is imported + /// Gets a value indicating whether the module is imported. /// internal bool IsModuleImported { @@ -287,18 +283,18 @@ internal bool IsModuleImported } /// - /// Sets the AllModulesViewModel containing this + /// Sets the AllModulesViewModel containing this. /// - /// the AllModulesViewModel containing this + /// The AllModulesViewModel containing this. internal void SetAllModules(AllModulesViewModel parentAllModules) { this.allModules = parentAllModules; } /// - /// Sorts commands and optionally sets ModuleQualifyCommandName + /// Sorts commands and optionally sets ModuleQualifyCommandName. /// - /// true to mark repeated commands with a flag that will produce a module qualified name in GetScript + /// 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); @@ -325,13 +321,13 @@ internal void SortCommands(bool markRepeatedCmdlets) } /// - /// According commandNameFilter to filter command,and added the filter commands into filteredCommands property + /// According commandNameFilter to filter command,and added the filter commands into filteredCommands property. /// - /// current filter + /// Current filter. internal void RefreshFilteredCommands(string filter) { this.filteredCommands.Clear(); - if (String.IsNullOrEmpty(filter)) + if (string.IsNullOrEmpty(filter)) { foreach (CommandViewModel command in this.Commands) { @@ -374,7 +370,7 @@ internal void RefreshFilteredCommands(string filter) } /// - /// Called in response to a GUI event that requires the command to be run + /// Called in response to a GUI event that requires the command to be run. /// internal void OnRunSelectedCommand() { @@ -386,9 +382,9 @@ internal void OnRunSelectedCommand() } /// - /// Triggers the SelectedCommandNeedsHelp event + /// Triggers the SelectedCommandNeedsHelp event. /// - /// event arguments + /// Event arguments. internal void OnSelectedCommandNeedsHelp(HelpNeededEventArgs e) { EventHandler handler = this.SelectedCommandNeedsHelp; @@ -399,7 +395,7 @@ internal void OnSelectedCommandNeedsHelp(HelpNeededEventArgs e) } /// - /// Triggers the SelectedCommandNeedsImportModule event + /// Triggers the SelectedCommandNeedsImportModule event. /// internal void OnSelectedCommandNeedsImportModule() { @@ -412,12 +408,12 @@ internal void OnSelectedCommandNeedsImportModule() #region Private Method /// - /// Uses pattern matching if pattern is not null or calls MatchesEvenIfInPlural otherwise + /// Uses pattern matching if pattern is not null or calls MatchesEvenIfInPlural otherwise. /// - /// pattern corresponding to filter - /// command name string - /// filter string - /// true if comparisonText matches str or pattern + /// 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) @@ -429,58 +425,58 @@ private static bool Matches(WildcardPattern filterPattern, string commandName, s } /// - /// Returns true if filter matches commandName, even when filter is in the plural + /// Returns true if filter matches commandName, even when filter is in the plural. /// - /// command name string - /// filter string - /// return match result + /// Command name string. + /// Filter string. + /// Return match result. private static bool MatchesEvenIfInPlural(string commandName, string filter) { - if (commandName.IndexOf(filter, StringComparison.OrdinalIgnoreCase) != -1) + if (commandName.Contains(filter, StringComparison.OrdinalIgnoreCase)) { return true; } if (filter.Length > 5 && filter.EndsWith("es", StringComparison.OrdinalIgnoreCase)) { - filter = filter.Substring(0, filter.Length - 2); - return commandName.IndexOf(filter, StringComparison.OrdinalIgnoreCase) != -1; + ReadOnlySpan filterSpan = filter.AsSpan(0, filter.Length - 2); + return commandName.AsSpan().Contains(filterSpan, StringComparison.OrdinalIgnoreCase); } if (filter.Length > 4 && filter.EndsWith("s", StringComparison.OrdinalIgnoreCase)) { - filter = filter.Substring(0, filter.Length - 1); - return commandName.IndexOf(filter, StringComparison.OrdinalIgnoreCase) != -1; + 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 + /// Handles the HelpNeeded event in the selected command and triggers the SelectedCommandNeedsHelp event. /// - /// HelpNeeded event sender - /// HelpNeeded event argument + /// 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 + /// Handles the ImportModule event in the selected command and triggers the SelectedCommandNeedsImportModule event. /// - /// HelpNeeded event sender - /// HelpNeeded event argument + /// 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 + /// Called when the SelectedCommand property changes to update IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues. /// - /// event sender - /// event arguments + /// Event sender. + /// Event arguments. private void SelectedCommand_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (!e.PropertyName.Equals("SelectedParameterSetAllMandatoryParametersHaveValues")) @@ -505,21 +501,21 @@ private void SetIsThereASelectedImportedCommandWhereAllMandatoryParametersHaveVa } /// - /// Compare source commandmodule is equal like target commandmodule + /// Compare source commandmodule is equal like target commandmodule. /// - /// source commandmodule - /// target commandmodule - /// return compare result + /// Source commandmodule. + /// Target commandmodule. + /// Return compare result. private int Compare(CommandViewModel source, CommandViewModel target) { - return String.Compare(source.Name, target.Name, StringComparison.OrdinalIgnoreCase); + return string.Compare(source.Name, target.Name, StringComparison.OrdinalIgnoreCase); } #endregion /// - /// If property changed will be notify + /// If property changed will be notify. /// - /// The changed property + /// The changed property. private void OnNotifyPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = this.PropertyChanged; diff --git a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/ParameterSetViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterSetViewModel.cs similarity index 79% rename from src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/ParameterSetViewModel.cs rename to src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterSetViewModel.cs index 380d6b0798b..b4f42dd78a2 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/ParameterSetViewModel.cs +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterSetViewModel.cs @@ -1,19 +1,18 @@ -//----------------------------------------------------------------------- -// -// Copyright © 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.CodeAnalysis; +using System.Management.Automation; +using System.Management.Automation.Language; +using System.Text; + +using Microsoft.PowerShell.Commands.ShowCommandExtension; + namespace Microsoft.PowerShell.Commands.ShowCommandInternal { - 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; - /// /// Contains information about a single ParameterSet inside a cmdlet. /// @@ -25,7 +24,7 @@ public class ParameterSetViewModel : INotifyPropertyChanged private string name; /// - /// value indicating all mandatory parameters have values + /// value indicating all mandatory parameters have values. /// private bool allMandatoryParametersHaveValues; @@ -39,22 +38,16 @@ public class ParameterSetViewModel : INotifyPropertyChanged /// /// Initializes a new instance of the ParameterSetViewModel class. /// - /// The name of the parameterSet - /// The array parameters of the parameterSet + /// 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) { - if (name == null) - { - throw new ArgumentNullException("name"); - } + ArgumentNullException.ThrowIfNull(name); - if (parameters == null) - { - throw new ArgumentNullException("parameters"); - } + ArgumentNullException.ThrowIfNull(parameters); parameters.Sort(Compare); @@ -67,7 +60,7 @@ public ParameterSetViewModel( continue; } - parameter.PropertyChanged += new PropertyChangedEventHandler(this.MandatoryParameter_PropertyChanged); + parameter.PropertyChanged += this.MandatoryParameter_PropertyChanged; } this.EvaluateAllMandatoryParametersHaveValues(); @@ -77,7 +70,7 @@ public ParameterSetViewModel( #region INotifyPropertyChanged Members /// - /// PropertyChanged Event + /// PropertyChanged Event. /// public event PropertyChangedEventHandler PropertyChanged; @@ -85,7 +78,7 @@ public ParameterSetViewModel( #region Public Property /// - /// Gets the ParameterSet Name + /// Gets the ParameterSet Name. /// public string Name { @@ -93,7 +86,7 @@ public string Name } /// - /// Gets the Parameters of this parameterset + /// Gets the Parameters of this parameterset. /// public List Parameters { @@ -101,7 +94,7 @@ public List Parameters } /// - /// Gets or sets a value indicating whether all mandatory parameters have values + /// Gets or sets a value indicating whether all mandatory parameters have values. /// public bool AllMandatoryParametersHaveValues { @@ -123,9 +116,9 @@ public bool AllMandatoryParametersHaveValues #region Public Method /// - /// Creates script according parameters of this parameterset + /// Creates script according parameters of this parameterset. /// - /// Return script of this parameterset parameters + /// Return script of this parameterset parameters. public string GetScript() { if (this.Parameters == null || this.Parameters.Count == 0) @@ -145,7 +138,7 @@ public string GetScript() { if (((bool?)parameter.Value) == true) { - builder.AppendFormat("-{0} ", parameter.Name); + builder.Append($"-{parameter.Name} "); } continue; @@ -164,7 +157,7 @@ public string GetScript() { parameterValueString = ParameterSetViewModel.GetDelimitedParameter(parameterValueString, "\"", "\""); } - else if(parameterType.IsScriptBlock) + else if (parameterType.IsScriptBlock) { parameterValueString = ParameterSetViewModel.GetDelimitedParameter(parameterValueString, "{", "}"); } @@ -173,16 +166,16 @@ public string GetScript() parameterValueString = ParameterSetViewModel.GetDelimitedParameter(parameterValueString, "(", ")"); } - builder.AppendFormat("-{0} {1} ", parameter.Name, parameterValueString); + builder.Append($"-{parameter.Name} {parameterValueString} "); } return builder.ToString().Trim(); } /// - /// Gets the individual parameter count of this parameterset + /// Gets the individual parameter count of this parameterset. /// - /// Return individual parameter count of this parameterset + /// Return individual parameter count of this parameterset. public int GetIndividualParameterCount() { if (this.Parameters == null || this.Parameters.Count == 0) @@ -210,11 +203,11 @@ public int GetIndividualParameterCount() #region Internal Method /// - /// Compare source parametermodel is equal like target parametermodel + /// Compare source parametermodel is equal like target parametermodel. /// - /// the source of parametermodel - /// the target of parametermodel - /// Return compare result + /// 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) @@ -227,18 +220,18 @@ internal static int Compare(ParameterViewModel source, ParameterViewModel target return 1; } - return String.Compare(source.Parameter.Name, target.Parameter.Name); + return string.Compare(source.Parameter.Name, target.Parameter.Name); } #endregion /// - /// Gets the delimited parameter if it needs delimitation and is not delimited + /// 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 + /// 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(); @@ -258,17 +251,16 @@ private static string GetDelimitedParameter(string parameterValue, string openDe case '\"': return '\"' + parameterValue + '\"'; default: - Diagnostics.Assert(delimitationChar == '0', "all other possible return values are in the cases above"); 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 + /// 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 + /// 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; @@ -325,7 +317,6 @@ private static char ParameterNeedsDelimitation(string parameterValue, bool requi } else { - Diagnostics.Assert(stringValue.StringConstantType == StringConstantType.SingleQuoted || stringValue.StringConstantType == StringConstantType.SingleQuotedHereString, "those are the 2 enumeration values left"); stringTerminationChar = '\''; } @@ -344,7 +335,7 @@ private static char ParameterNeedsDelimitation(string parameterValue, bool requi } /// - /// Called to evaluate the value of AllMandatoryParametersHaveValues + /// Called to evaluate the value of AllMandatoryParametersHaveValues. /// private void EvaluateAllMandatoryParametersHaveValues() { @@ -367,9 +358,9 @@ private void EvaluateAllMandatoryParametersHaveValues() } /// - /// If property changed will be notify + /// If property changed will be notify. /// - /// The changed property + /// The changed property. private void OnNotifyPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = this.PropertyChanged; @@ -380,10 +371,10 @@ private void OnNotifyPropertyChanged(string propertyName) } /// - /// Used to track changes to parameter values in order to verify the enabled state of buttons + /// Used to track changes to parameter values in order to verify the enabled state of buttons. /// - /// event arguments - /// event sender + /// Event arguments. + /// Event sender. private void MandatoryParameter_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (!e.PropertyName.Equals("Value", StringComparison.Ordinal)) diff --git a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/ParameterViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterViewModel.cs similarity index 76% rename from src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/ParameterViewModel.cs rename to src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterViewModel.cs index be529630fa8..93227df87a1 100644 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/ParameterViewModel.cs +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterViewModel.cs @@ -1,18 +1,17 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// 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 { - 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; - /// /// Contains information about a single parameter inside a parameter set. /// If a parameter with the same name belongs to two (or more) parameter sets, @@ -32,12 +31,12 @@ public class ParameterViewModel : INotifyPropertyChanged private ShowCommandParameterInfo parameter; /// - /// value entered in the GUI for the parameter + /// value entered in the GUI for the parameter. /// private object parameterValue; /// - /// Name of the parameter set this parameter is in + /// Name of the parameter set this parameter is in. /// private string parameterSetName; @@ -45,19 +44,13 @@ public class ParameterViewModel : INotifyPropertyChanged /// /// Initializes a new instance of the ParameterViewModel class. /// - /// The parameter information for this parameter - /// the name of the parameter set this parameter is in + /// The parameter information for this parameter. + /// The name of the parameter set this parameter is in. public ParameterViewModel(ShowCommandParameterInfo parameter, string parameterSetName) { - if (parameter == null) - { - throw new ArgumentNullException("parameter"); - } + ArgumentNullException.ThrowIfNull(parameter); - if (parameterSetName == null) - { - throw new ArgumentNullException("parameterSetName"); - } + ArgumentNullException.ThrowIfNull(parameterSetName); this.parameter = parameter; this.parameterSetName = parameterSetName; @@ -68,7 +61,7 @@ public ParameterViewModel(ShowCommandParameterInfo parameter, string parameterSe } else { - this.parameterValue = String.Empty; + this.parameterValue = string.Empty; } } #endregion @@ -76,7 +69,7 @@ public ParameterViewModel(ShowCommandParameterInfo parameter, string parameterSe #region INotifyPropertyChanged Members /// - /// PropertyChanged Event + /// PropertyChanged Event. /// public event PropertyChangedEventHandler PropertyChanged; @@ -93,7 +86,7 @@ public ShowCommandParameterInfo Parameter } /// - /// Gets or sets the value for this parameter from the GUI + /// Gets or sets the value for this parameter from the GUI. /// public object Value { @@ -113,7 +106,7 @@ public object Value } /// - /// Gets the parameter name + /// Gets the parameter name. /// public string Name { @@ -121,7 +114,7 @@ public string Name } /// - /// Gets the name of the parameter set this parameter is in + /// Gets the name of the parameter set this parameter is in. /// public string ParameterSetName { @@ -129,7 +122,7 @@ public string ParameterSetName } /// - /// Gets a value indicating whether this parameter is in the shared parameterset + /// Gets a value indicating whether this parameter is in the shared parameterset. /// public bool IsInSharedParameterSet { @@ -137,19 +130,19 @@ public bool IsInSharedParameterSet } /// - /// Gets Name with an extra suffix to indicate if the parameter is mandatory to serve + /// 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( + string.Format( CultureInfo.CurrentUICulture, ShowCommandResources.MandatoryNameLabelFormat, this.Name, ShowCommandResources.MandatoryLabelSegment) : - String.Format( + string.Format( CultureInfo.CurrentUICulture, ShowCommandResources.NameLabelFormat, this.Name); @@ -157,7 +150,7 @@ public string NameTextLabel } /// - /// Gets Label in the case this parameter is used in a combo box + /// Gets Label in the case this parameter is used in a combo box. /// public string NameCheckLabel { @@ -166,7 +159,7 @@ public string NameCheckLabel string returnValue = this.Parameter.Name; if (this.Parameter.IsMandatory) { - returnValue = string.Format(CultureInfo.CurrentUICulture, "{0}{1}", returnValue, ShowCommandResources.MandatoryLabelSegment); + returnValue = string.Create(CultureInfo.CurrentUICulture, $"{returnValue}{ShowCommandResources.MandatoryLabelSegment}"); } return returnValue; @@ -174,7 +167,7 @@ public string NameCheckLabel } /// - /// Gets Tooltip string for the parameter + /// Gets Tooltip string for the parameter. /// public string ToolTip { @@ -190,7 +183,7 @@ public string ToolTip } /// - /// Gets a value indicating whether the parameter is mandatory + /// Gets a value indicating whether the parameter is mandatory. /// public bool IsMandatory { @@ -198,7 +191,7 @@ public bool IsMandatory } /// - /// Gets a value indicating whether the parameter has a value + /// Gets a value indicating whether the parameter has a value. /// public bool HasValue { @@ -220,17 +213,17 @@ public bool HasValue #endregion /// - /// Evaluates the tooltip based on the parameters + /// 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 + /// 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( + StringBuilder returnValue = new StringBuilder(string.Format( CultureInfo.CurrentCulture, ShowCommandResources.TypeFormat, typeName)); @@ -238,7 +231,7 @@ internal static string EvaluateTooltip(string typeName, int position, bool manda if (position >= 0) { - string positionFormat = String.Format( + string positionFormat = string.Format( CultureInfo.CurrentCulture, ShowCommandResources.PositionFormat, position); @@ -264,9 +257,9 @@ internal static string EvaluateTooltip(string typeName, int position, bool manda } /// - /// If property changed will be notify + /// If property changed will be notify. /// - /// The changed property + /// The changed property. private void OnNotifyPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = this.PropertyChanged; 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..10690b65dc7 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/commandHelpers/ShowCommandHelper.cs @@ -0,0 +1,1267 @@ +// 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) + { + // The value of 'PrivateData' property may be null for the default host or a custom host. + 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.Activities/Activities/ActivityGenerator.cs b/src/Microsoft.PowerShell.Activities/Activities/ActivityGenerator.cs deleted file mode 100644 index 1657310a3d8..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/ActivityGenerator.cs +++ /dev/null @@ -1,795 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Reflection; -using Microsoft.CSharp; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Microsoft.PowerShell.Cmdletization; -using Microsoft.PowerShell.Cmdletization.Xml; -using System.Xml; -using System.Xml.Serialization; -using System.Xml.Schema; -using System.CodeDom.Compiler; -using System.CodeDom; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Generates an activity that corresponds to a PowerShell command - /// - public static class ActivityGenerator - { - private static readonly Lazy xmlSerializer = new Lazy(ConstructXmlSerializer); - private static readonly Lazy xmlReaderSettings = new Lazy(ConstructXmlReaderSettings); - - static XmlSerializer ConstructXmlSerializer() - { - XmlSerializer xmlSerializer = new Microsoft.PowerShell.Cmdletization.Xml.PowerShellMetadataSerializer(); - return xmlSerializer; - } - - static XmlReaderSettings ConstructXmlReaderSettings() - { - // - // XmlReaderSettings - // - XmlReaderSettings result = new XmlReaderSettings(); - // general settings - result.CheckCharacters = true; - result.CloseInput = false; - result.ConformanceLevel = ConformanceLevel.Document; - result.IgnoreComments = true; - result.IgnoreProcessingInstructions = true; - result.IgnoreWhitespace = false; - result.MaxCharactersFromEntities = 16384; // generous guess for the upper bound - result.MaxCharactersInDocument = 128 * 1024 * 1024; // generous guess for the upper bound - result.DtdProcessing = DtdProcessing.Parse; // Allowing DTD parsing with limits of MaxCharactersFromEntities/MaxCharactersInDocument - result.XmlResolver = null; // do not fetch external documents - // xsd schema related settings - result.ValidationFlags = XmlSchemaValidationFlags.ProcessIdentityConstraints | - XmlSchemaValidationFlags.ReportValidationWarnings; - result.ValidationType = ValidationType.Schema; - string cmdletizationXsd = ActivityResources.Xml_cmdletsOverObjectsXsd; - XmlReader cmdletizationSchemaReader = XmlReader.Create(new StringReader(cmdletizationXsd), result); - result.Schemas = new XmlSchemaSet(); - result.Schemas.Add(null, cmdletizationSchemaReader); - result.Schemas.XmlResolver = null; // do not fetch external documents - - return result; - } - - static string templateCommand = @" -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace {0} -{{ - /// - /// Activity to invoke the {1} command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode(""Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName"", ""3.0"")] - public sealed class {2} : {6} - {{ - /// - /// Gets the display name of the command invoked by this activity. - /// - public {2}() - {{ - this.DisplayName = ""{8}""; - }} - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName {{ get {{ return ""{4}""; }} }} - - // Arguments - {3} - - // Module defining this command - {7} - - // Optional custom code for this activity - {9} - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - {{ - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - {5} - - return new ActivityImplementationContext() {{ PowerShellInstance = invoker }}; - }} - }} -}}"; - - const string templateParameter = @" - /// - /// Provides access to the {1} parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument<{0}> {1} {{ get; set; }}"; - - const string templateParameterSetter = @" - if({0}.Expression != null) - {{ - targetCommand.AddParameter(""{1}"", {0}.Get(context)); - }}"; - - const string customRemotingMapping = @" - if(GetIsComputerNameSpecified(context) && (PSRemotingBehavior.Get(context) == RemotingBehavior.Custom)) - { - targetCommand.AddParameter(""ComputerName"", PSComputerName.Get(context)); - }"; - - const string supportsCustomRemoting = @" - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } }"; - - /// - /// Generate an activity for the named command. - /// - /// The command name to generate. - /// The namespace that will contain the command - for example, - /// Microsoft.PowerShell.Activities. - /// - /// A string representing the C# source code of the generated activity. - public static string GenerateFromName(string command, string activityNamespace) - { - return GenerateFromName(command, activityNamespace, false); - } - - /// - /// Generate an activity for the named command. - /// - /// The command name to generate. - /// The namespace that will contain the command - for example, - /// Microsoft.PowerShell.Activities. - /// - /// True if remoting-related parameters should be suppressed. This - /// should only be specified for commands that offer no value when run on a remote computer. - /// - /// A string representing the C# source code of the generated activity. - public static string GenerateFromName(string command, string activityNamespace, bool shouldRunLocally) - { - StringBuilder output = new StringBuilder(); - - // Get the command from the runspace - using (System.Management.Automation.PowerShell invoker = System.Management.Automation.PowerShell.Create()) - { - invoker.AddCommand("Get-Command").AddParameter("Name", command); - Collection result = invoker.Invoke(); - - if (result.Count == 0) - { - string message = String.Format(CultureInfo.InvariantCulture, ActivityResources.ActivityNameNotFound, command); - throw new ArgumentException(message, "command"); - } - - foreach (CommandInfo commandToGenerate in result) - { - output.AppendLine(GenerateFromCommandInfo(commandToGenerate, activityNamespace, shouldRunLocally)); - } - } - - return output.ToString().Trim(); - } - - - /// - /// By default, the activity wrapper uses the remoting command base. - /// - /// The command name to generate. - /// The namespace that will contain the command - for example, - /// Microsoft.PowerShell.Activities. - /// - /// - public static string GenerateFromCommandInfo(CommandInfo command, string activityNamespace) - { - return GenerateFromCommandInfo(command, activityNamespace, false); - } - - /// - /// By default, the activity wrapper uses the remoting command base. - /// - /// The command name to generate. - /// The namespace that will contain the command - for example, - /// Microsoft.PowerShell.Activities. - /// - /// True if remoting-related parameters should be suppressed. This - /// should only be specified for commands that offer no value when run on a remote computer. - /// - /// - public static string GenerateFromCommandInfo(CommandInfo command, string activityNamespace, bool shouldRunLocally) - { - string activityBaseClass = "PSRemotingActivity"; - if (shouldRunLocally || (command.RemotingCapability == RemotingCapability.None)) - { - activityBaseClass = "PSActivity"; - } - - return GenerateFromCommandInfo(command, activityNamespace, activityBaseClass, null, null, String.Empty); - } - - /// - /// Generate an activity for the given command. - /// - /// The command to use as the basis of the generated activity. - /// The namespace that will contain the command - for example, - /// Microsoft.PowerShell.Activities. - /// - /// The class to use as the base class for this activity - /// - /// A list of parameters on the command being wrapped that should not - /// be copied to the activity. - /// - /// The module that contains the wrapped command - /// Addition text to inset in the class definition - /// A string representing the C# source code of the generated activity. - public static string GenerateFromCommandInfo( - CommandInfo command, - string activityNamespace, - string activityBaseClass, - string[] parametersToExclude, - string moduleToLoad, - string moduleDefinitionText - ) - { - if (command == null) - { - throw new ArgumentNullException("command"); - } - - if (String.IsNullOrEmpty(activityNamespace)) - { - throw new ArgumentNullException("activityNamespace"); - } - - - if (String.IsNullOrEmpty(activityBaseClass)) - { - throw new ArgumentNullException("activityBaseClass"); - } - - StringBuilder parameterBlock = new StringBuilder(); - StringBuilder parameterInitialization = new StringBuilder(); - string commandName = command.Name; - commandName = commandName.Substring(0, 1).ToUpper(CultureInfo.CurrentCulture) + commandName.Substring(1); - string activityName = command.Name.Replace("-", ""); - activityName = activityName.Substring(0, 1).ToUpper(CultureInfo.CurrentCulture) + activityName.Substring(1); - - String displayName = commandName; - - // Verify that the activity name doesn't conflict with anything in the inheritance hierarchy - Type testType = typeof(PSRemotingActivity); - while (testType != null) - { - if (String.Equals(testType.Name, activityName, StringComparison.OrdinalIgnoreCase)) - { - string message = String.Format(CultureInfo.InvariantCulture, ActivityResources.ActivityNameConflict, activityName); - throw new ArgumentException(message, "command"); - } - - testType = testType.BaseType; - } - - // The default list of parameters that need to be ignored. - List ignoredParameters = new List(Cmdlet.CommonParameters.Concat(Cmdlet.OptionalCommonParameters)); - - // Add in any additional parameters the caller requested to ignore - if (parametersToExclude != null && parametersToExclude.Length > 0) - { - ignoredParameters.AddRange(parametersToExclude); - } - - // If this activity supports its own remoting, ignore the ComputerName - // parameter (we will add special handling for that later) - if (command.RemotingCapability == RemotingCapability.SupportedByCommand) - { - ignoredParameters.Add("ComputerName"); - } - - // Avoid properties in parent classes. - List parentProperties = new List(); - testType = typeof(PSRemotingActivity); - while (testType != typeof(PSActivity).BaseType) - { - parentProperties.AddRange( - from property in testType.GetProperties() select property.Name - ); - - testType = testType.BaseType; - } - - foreach(KeyValuePair parameter in command.Parameters) - { - // Get the name (with capitalized first letter) - string name = parameter.Key; - name = name.Substring(0, 1).ToUpper(CultureInfo.CurrentCulture) + name.Substring(1); - - // Ignore the common parameters - if(ignoredParameters.Contains(name, StringComparer.OrdinalIgnoreCase)) - { - continue; - } - - // Avoid parameters used by the parent activity, currently "Id" and - // "DisplayName". If the command has a noun, we name it: - // NounId and NounDisplayName - for example, ProcessId, and - // ServiceDisplayName. - string originalName = name; - if (parentProperties.Contains(name, StringComparer.OrdinalIgnoreCase)) - { - if (commandName.Contains('-')) - { - string[] commandParts = commandName.Split('-'); - string noun = commandParts[1]; - noun = noun.Substring(0, 1).ToUpper(CultureInfo.CurrentCulture) + noun.Substring(1); - name = noun + name; - } - else - { - name = commandName + name; - } - } - - // If the parameter name is the same as the command name, add "Activity" - // to the command name. Otherwise, we run afoul of the error: - // "Member names cannot be the same as their enclosing type". - if (String.Equals(name, activityName, StringComparison.OrdinalIgnoreCase)) - { - activityName += "Activity"; - } - - // And the type - string type = parameter.Value.ParameterType.ToString(); - - // Fix generic types - if(type.Contains('`')) - { - type = System.Text.RegularExpressions.Regex.Replace(type, "`[\\d]+", ""); - type = System.Text.RegularExpressions.Regex.Replace(type, "\\[", "<"); - type = System.Text.RegularExpressions.Regex.Replace(type, "\\]", ">"); - } - - // Fix nested classes... - if (type.Contains('+')) - { - type = System.Text.RegularExpressions.Regex.Replace(type, "\\+", "."); - } - - // Append the parameter ( InArgument Name { get; set } ... ) - parameterBlock.AppendLine( - String.Format(CultureInfo.InvariantCulture, - templateParameter, - type, - name)); - - // Append the parameter initializer (... Parameters.Add(...) ) - // This may have to be mapped from name to originalName when the - // parameter has a conflict with the parent activity. - parameterInitialization.AppendLine( - String.Format(CultureInfo.InvariantCulture, - templateParameterSetter, - name, - originalName)); - } - - // Append the remoting support to parameter initialization - if (command.RemotingCapability == RemotingCapability.SupportedByCommand) - { - parameterBlock.AppendLine(supportsCustomRemoting); - parameterInitialization.AppendLine(customRemotingMapping); - } - - - // If no module definition string has been included then add the defining module - // to the list of modules and make use a module-qualified name - string psDefiningModule = ""; - if (string.IsNullOrEmpty(moduleDefinitionText)) - { - // Prefer the module to load that was passed in over the module that - // eventually defined the cmdlet... - if (!string.IsNullOrEmpty(moduleToLoad)) - { - commandName = moduleToLoad + "\\" + commandName; - } - else if (!String.IsNullOrEmpty(command.ModuleName)) - { - commandName = command.ModuleName + "\\" + commandName; - } - - if (!String.IsNullOrEmpty(moduleToLoad)) - { - psDefiningModule = " /// \n/// Script module contents for this activity`n/// \n" + - @"protected override string PSDefiningModule { get { return """ + moduleToLoad + @"""; } }"; - } - } - - return String.Format(CultureInfo.InvariantCulture, - templateCommand, - activityNamespace, - commandName, - activityName, - parameterBlock.ToString(), - commandName.Replace("\\", "\\\\"), - parameterInitialization.ToString(), - activityBaseClass, - psDefiningModule, - displayName, - moduleDefinitionText); - } - - /// - /// Generates a complete activity source file from a module. - /// - /// - /// The namespace to use for the target classes - /// An array of code elements to compile into an assembly - static public string[] GenerateFromModuleInfo(PSModuleInfo moduleToProcess, string activityNamespace) - { - if (moduleToProcess == null) - throw new ArgumentNullException("moduleToProcess"); - - List codeToCompile = new List(); - - // Cmdlets and function need to exist in separate namespaces... - if (moduleToProcess.ExportedCmdlets != null) - { - string namespaceToUse = ! string.IsNullOrEmpty(activityNamespace) ? activityNamespace : moduleToProcess.Name + "_Cmdlet_Activities"; - foreach (CmdletInfo ci in moduleToProcess.ExportedCmdlets.Values) - { - string code = Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromCommandInfo(ci, namespaceToUse, "PSRemotingActivity", null, null, ""); - codeToCompile.Add(code); - } - } - - Dictionary modules = new Dictionary(); - PSModuleInfo cimModule = moduleToProcess; - - if (moduleToProcess.ExportedFunctions != null) - { - string namespaceToUse = !string.IsNullOrEmpty(activityNamespace) ? activityNamespace : moduleToProcess.Name + "_Function_Activities"; - foreach (FunctionInfo fi in moduleToProcess.ExportedFunctions.Values) - { - string moduleName = null; - string moduleDefinition = null; - - // Save the module defining this function - we may need to extract - // embedded types further on - if (fi.ScriptBlock.Module != null && !string.IsNullOrEmpty(fi.ScriptBlock.Module.Definition)) - { - moduleName = fi.ScriptBlock.Module.Name; - moduleDefinition = fi.ScriptBlock.Module.Definition; - } - - string code; - if (fi.ScriptBlock.Module.ModuleType == ModuleType.Cim) - { - // Special-case CIM activities - string embeddedDefinition = ""; - - // Embed the module definition in the activity... - if (moduleDefinition != null) - { - // Remove all of the calls to Export-ModuleMember and getcommand - string editedDefinition = System.Text.RegularExpressions.Regex.Replace(moduleDefinition, @"Microsoft.PowerShell.Core\\Export-ModuleMember[^\n]*\n", ""); - editedDefinition = System.Text.RegularExpressions.Regex.Replace(editedDefinition, - @"if \(\$\(Microsoft.PowerShell.Core\\Get-Command Set-StrictMode[^\n]*\n", ""); - - embeddedDefinition = "protected override string ModuleDefinition { get { return _moduleDefinition; } }\r\n const string _moduleDefinition = @\"" - + editedDefinition.Replace("\"", "\"\"") + "\";"; - } - code = Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromCommandInfo( - fi, namespaceToUse, "PSGeneratedCIMActivity", new string[] { "Computer", "AsJob", "CimSession" }, null, embeddedDefinition); - - cimModule = fi.ScriptBlock.Module; - } - else - { - code = Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromCommandInfo( - fi, namespaceToUse, "PSRemotingActivity", new string[] { "Computer", "AsJob" }, moduleToProcess.Name, ""); - } - codeToCompile.Add(code); - - if (moduleName != null && !modules.ContainsKey(fi.ScriptBlock.Module.Name)) - { - modules.Add(moduleName, moduleDefinition); - } - - - } - } - - string fileName = cimModule.Path; - - // See if there are any embedded types to extract - if (Path.GetExtension(fileName).Equals(".cdxml", StringComparison.OrdinalIgnoreCase)) - { - // generate cmdletization proxies - using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read)) - { - XmlReader xmlReader = XmlReader.Create(file, xmlReaderSettings.Value); - - PowerShellMetadata cmdletizationMetadata = (PowerShellMetadata)xmlSerializer.Value.Deserialize(xmlReader); - - if (cmdletizationMetadata != null && cmdletizationMetadata.Enums != null) - { - foreach (EnumMetadataEnum enumMetadata in cmdletizationMetadata.Enums) - { - codeToCompile.Add(GetCSharpCode(enumMetadata)); - } - } - } - } - - return codeToCompile.ToArray(); - } - - internal static string GetCSharpCode(EnumMetadataEnum enumMetadata) - { - var codeCompileUnit = CreateCodeCompileUnit(enumMetadata); - - var stringWriter = new StringWriter(CultureInfo.InvariantCulture); - CodeDomProvider.CreateProvider("C#").GenerateCodeFromCompileUnit( - codeCompileUnit, - stringWriter, - new CodeGeneratorOptions()); - return stringWriter.ToString(); - } - - private const string namespacePrefix = "Microsoft.PowerShell.Cmdletization.GeneratedTypes"; - - private static CodeCompileUnit CreateCodeCompileUnit(EnumMetadataEnum enumMetadata) - { - var codeDomProvider = CodeDomProvider.CreateProvider("C#"); - - string subnamespaceText = string.Empty; - string enumNameText; - int indexOfLastDot = enumMetadata.EnumName.LastIndexOf('.'); - if (indexOfLastDot < 0) - { - enumNameText = enumMetadata.EnumName; - } - else - { - subnamespaceText = "." + enumMetadata.EnumName.Substring(0, indexOfLastDot); - enumNameText = enumMetadata.EnumName.Substring( - indexOfLastDot + 1, enumMetadata.EnumName.Length - indexOfLastDot - 1); - } - - // defense in depth (in case xsd is allowing some invalid identifiers) - // + xsd allows reserved keywords (i.e. "namespace" passes the regex test, but is not a valid identifier) - if (!codeDomProvider.IsValidIdentifier(enumNameText)) - { - var errorMessage = string.Format( - CultureInfo.InvariantCulture, - ActivityResources.EnumWriter_InvalidEnumName, - enumMetadata.EnumName); - throw new XmlException(errorMessage); - } - var newEnum = new CodeTypeDeclaration(codeDomProvider.CreateValidIdentifier(enumNameText)) { IsEnum = true, Attributes = MemberAttributes.Public }; - - if (enumMetadata.BitwiseFlagsSpecified && enumMetadata.BitwiseFlags) - { - newEnum.CustomAttributes.Add( - new CodeAttributeDeclaration(new CodeTypeReference(typeof(FlagsAttribute)))); - } - - Type underlyingType = null; - if (enumMetadata.UnderlyingType != null) - { - underlyingType = Type.GetType(enumMetadata.UnderlyingType, false, true); - - if (underlyingType != null) - { - newEnum.BaseTypes.Add(underlyingType); - } - else - { - underlyingType = typeof(Int32); - } - } - else - { - underlyingType = typeof(Int32); - } - - foreach (var value in enumMetadata.Value) - { - // defense in depth (in case xsd is allowing some invalid identifiers) - // + xsd allows reserved keywords (i.e. "namespace" passes the regex test, but is not a valid identifier) - if (!codeDomProvider.IsValidIdentifier(value.Name)) // defense in depth (in case xsd is allowing some invalid identifiers) - { - var errorMessage = string.Format( - CultureInfo.InvariantCulture, - ActivityResources.EnumWriter_InvalidValueName, - value.Name); - throw new XmlException(errorMessage); - } - - var nameValuePair = new CodeMemberField(underlyingType, codeDomProvider.CreateValidIdentifier(value.Name)); - - object integerValue = LanguagePrimitives.ConvertTo( - value.Value, underlyingType, CultureInfo.InvariantCulture); - nameValuePair.InitExpression = new CodePrimitiveExpression(integerValue); - - newEnum.Members.Add(nameValuePair); - } - - var topLevelNamespace = new CodeNamespace(namespacePrefix + subnamespaceText); - topLevelNamespace.Types.Add(newEnum); - - var codeCompileUnit = new CodeCompileUnit(); - codeCompileUnit.Namespaces.Add(topLevelNamespace); - codeCompileUnit.ReferencedAssemblies.Add("System.dll"); - - return codeCompileUnit; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static Assembly GenerateAssemblyFromModuleInfo( - PSModuleInfo moduleToProcess, - string activityNamespace, - string outputAssemblyPath, - string[] referenceAssemblies, - out string errors - ) - { - string[] src = GenerateFromModuleInfo(moduleToProcess, activityNamespace); - - bool toAssembly = ! string.IsNullOrEmpty(outputAssemblyPath); - - return CompileStrings(src, referenceAssemblies, toAssembly, outputAssemblyPath, out errors); - } - - private static Assembly CompileStrings( - string[] src, - string[] referenceAssemblies, - bool toAssembly, - string outputAssemblyPath, - out string errors - ) - { - var cpar = new System.CodeDom.Compiler.CompilerParameters() - { - GenerateInMemory = ! toAssembly, - OutputAssembly = outputAssemblyPath, - }; - - // Add default references... - cpar.ReferencedAssemblies.Add(typeof(System.Activities.Activity).Assembly.Location); - cpar.ReferencedAssemblies.Add(typeof(System.CodeDom.Compiler.CodeCompiler).Assembly.Location); - cpar.ReferencedAssemblies.Add(typeof(PSObject).Assembly.Location); - cpar.ReferencedAssemblies.Add(typeof(Microsoft.PowerShell.Activities.PSActivity).Assembly.Location); - cpar.ReferencedAssemblies.Add(ResolveReferencedAssembly("Microsoft.Management.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")); - cpar.ReferencedAssemblies.Add(ResolveReferencedAssembly("Microsoft.PowerShell.Commands.Management, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")); - - // Add user supplied references... - if (referenceAssemblies != null) - { - foreach (string asm in referenceAssemblies) - { - cpar.ReferencedAssemblies.Add(ResolveReferencedAssembly(asm)); - } - } - - var compiler = new Microsoft.CSharp.CSharpCodeProvider(); - var cr = compiler.CompileAssemblyFromSource(cpar, src); - if (cr.Errors == null || cr.Errors.Count == 0) - { - errors = string.Empty; - } - else - { - StringBuilder errorBuilder = new StringBuilder(); - foreach (var err in cr.Errors) - { - errorBuilder.Append(err.ToString()); - errorBuilder.Append('\n'); - } - - errors = errorBuilder.ToString(); - } - - if (errors.Length > 0) - { - return null; - } - - // If the assembly was written to disk, return null - // since we don't want to load the assembly we've just created. - if (toAssembly) - { - return null; - } - else - { - return cr.CompiledAssembly; - } - } - - [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadWithPartialName")] - [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] - private static string ResolveReferencedAssembly(string assembly) - { - Assembly asm = null; - - if (assembly == null) - { - throw new ArgumentNullException("assembly"); - } - - if (System.IO.Path.IsPathRooted(assembly)) - { - return assembly; - } - - if (assembly.Contains(',')) - { - try - { - asm = Assembly.Load(assembly); - return asm.Location; - } - catch (Exception) - { - ; - } - } - - - if (asm == null) - { - try - { -#pragma warning disable 0618 - asm = Assembly.LoadWithPartialName(assembly); - return asm.Location; - } - catch (Exception) - { - ; - } - } - - if (asm == null) - { - throw new InvalidOperationException(assembly); - } - return null; - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/GetCimAssociatedInstanceActivity.cs b/src/Microsoft.PowerShell.Activities/Activities/GetCimAssociatedInstanceActivity.cs deleted file mode 100644 index 046fdb79fe7..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/GetCimAssociatedInstanceActivity.cs +++ /dev/null @@ -1,135 +0,0 @@ - -using Microsoft.PowerShell.Activities; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Activity to invoke the CimCmdlets\Get-CimAssociatedInstance command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetCimAssociatedInstance : GenericCimCmdletActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetCimAssociatedInstance() - { - this.DisplayName = "Get-CimAssociatedInstance"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "CimCmdlets\\Get-CimAssociatedInstance"; } } - - /// - /// The .NET type implementing the cmdlet to invoke. - /// - public override System.Type TypeImplementingCmdlet { get { return typeof(Microsoft.Management.Infrastructure.CimCmdlets.GetCimAssociatedInstanceCommand); } } - - // Arguments - - /// - /// Provides access to the Association parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Association { get; set; } - - /// - /// Provides access to the ResultClassName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ResultClassName { get; set; } - - /// - /// Provides access to the InputObject parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument InputObject { get; set; } - - /// - /// Provides access to the Namespace parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Namespace { get; set; } - - /// - /// Provides access to the OperationTimeoutSec parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OperationTimeoutSec { get; set; } - - /// - /// Provides access to the KeyOnly parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument KeyOnly { get; set; } - - /// - /// No module needed for this activity - /// - protected override string PSDefiningModule { get { return null; } } - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(Association.Expression != null) - { - targetCommand.AddParameter("Association", Association.Get(context)); - } - - if (ResultClassName.Expression != null) - { - targetCommand.AddParameter("ResultClassName", ResultClassName.Get(context)); - } - - if (InputObject.Expression != null) - { - targetCommand.AddParameter("InputObject", InputObject.Get(context)); - } - - if(Namespace.Expression != null) - { - targetCommand.AddParameter("Namespace", Namespace.Get(context)); - } - - if(OperationTimeoutSec.Expression != null) - { - targetCommand.AddParameter("OperationTimeoutSec", OperationTimeoutSec.Get(context)); - } - - if(KeyOnly.Expression != null) - { - targetCommand.AddParameter("KeyOnly", KeyOnly.Get(context)); - } - - if (ResourceUri != null) - { - targetCommand.AddParameter("ResourceUri", ResourceUri.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/GetCimClassActivity.cs b/src/Microsoft.PowerShell.Activities/Activities/GetCimClassActivity.cs deleted file mode 100644 index 04cf6c7bd2b..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/GetCimClassActivity.cs +++ /dev/null @@ -1,131 +0,0 @@ - -using Microsoft.PowerShell.Activities; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Activity to invoke the CimCmdlets\Get-CimClass command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetCimClass : GenericCimCmdletActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetCimClass() - { - this.DisplayName = "Get-CimClass"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "CimCmdlets\\Get-CimClass"; } } - - /// - /// The .NET type implementing the cmdlet to invoke. - /// - public override System.Type TypeImplementingCmdlet { get { return typeof(Microsoft.Management.Infrastructure.CimCmdlets.GetCimClassCommand); } } - - // Arguments - - /// - /// Provides access to the ClassName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ClassName { get; set; } - - /// - /// Provides access to the Namespace parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Namespace { get; set; } - - /// - /// Provides access to the OperationTimeoutSec parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OperationTimeoutSec { get; set; } - - /// - /// Provides access to the MethodName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument MethodName { get; set; } - - /// - /// Provides access to the PropertyName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument PropertyName { get; set; } - - /// - /// Provides access to the QualifierName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument QualifierName { get; set; } - - /// - /// No module needed for this activity - /// - protected override string PSDefiningModule { get { return null; } } - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - // Specified ClassName cannot be WhiteSpace or NULL - // - if (ClassName.Expression != null && !string.IsNullOrWhiteSpace(ClassName.Get(context))) - { - targetCommand.AddParameter("ClassName", ClassName.Get(context)); - } - - if(Namespace.Expression != null) - { - targetCommand.AddParameter("Namespace", Namespace.Get(context)); - } - - if(OperationTimeoutSec.Expression != null) - { - targetCommand.AddParameter("OperationTimeoutSec", OperationTimeoutSec.Get(context)); - } - - if(MethodName.Expression != null) - { - targetCommand.AddParameter("MethodName", MethodName.Get(context)); - } - - if(PropertyName.Expression != null) - { - targetCommand.AddParameter("PropertyName", PropertyName.Get(context)); - } - - if(QualifierName.Expression != null) - { - targetCommand.AddParameter("QualifierName", QualifierName.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/GetCimInstanceActivity.cs b/src/Microsoft.PowerShell.Activities/Activities/GetCimInstanceActivity.cs deleted file mode 100644 index 10bada78859..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/GetCimInstanceActivity.cs +++ /dev/null @@ -1,177 +0,0 @@ - -using Microsoft.PowerShell.Activities; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Activity to invoke the CimCmdlets\Get-CimInstance command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetCimInstance : GenericCimCmdletActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetCimInstance() - { - this.DisplayName = "Get-CimInstance"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "CimCmdlets\\Get-CimInstance"; } } - - /// - /// The .NET type implementing the cmdlet to invoke. - /// - public override System.Type TypeImplementingCmdlet { get { return typeof(Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand); } } - - // Arguments - - /// - /// Provides access to the ClassName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ClassName { get; set; } - - /// - /// Provides access to the Filter parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Filter { get; set; } - - /// - /// Provides access to the KeyOnly parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument KeyOnly { get; set; } - - /// - /// Provides access to the Namespace parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Namespace { get; set; } - - /// - /// Provides access to the OperationTimeoutSec parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OperationTimeoutSec { get; set; } - - /// - /// Provides access to the InputObject parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument InputObject { get; set; } - - /// - /// Provides access to the Query parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Query { get; set; } - - /// - /// Provides access to the QueryDialect parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument QueryDialect { get; set; } - - /// - /// Provides access to the Shallow parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Shallow { get; set; } - - /// - /// Provides access to the Property parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Property { get; set; } - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ClassName.Expression != null) - { - targetCommand.AddParameter("ClassName", ClassName.Get(context)); - } - - if (Filter.Expression != null) - { - targetCommand.AddParameter("Filter", Filter.Get(context)); - } - - if(KeyOnly.Expression != null) - { - targetCommand.AddParameter("KeyOnly", KeyOnly.Get(context)); - } - - if(Namespace.Expression != null) - { - targetCommand.AddParameter("Namespace", Namespace.Get(context)); - } - - if(OperationTimeoutSec.Expression != null) - { - targetCommand.AddParameter("OperationTimeoutSec", OperationTimeoutSec.Get(context)); - } - - if (InputObject.Expression != null) - { - targetCommand.AddParameter("InputObject", InputObject.Get(context)); - } - - if(Query.Expression != null) - { - targetCommand.AddParameter("Query", Query.Get(context)); - } - - if(QueryDialect.Expression != null) - { - targetCommand.AddParameter("QueryDialect", QueryDialect.Get(context)); - } - - if(Shallow.Expression != null) - { - targetCommand.AddParameter("Shallow", Shallow.Get(context)); - } - - if (Property.Expression != null) - { - targetCommand.AddParameter("Property", Property.Get(context)); - } - - if (ResourceUri != null) - { - targetCommand.AddParameter("ResourceUri", ResourceUri.Get(context)); - } - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/GetPSWorkflowData.cs b/src/Microsoft.PowerShell.Activities/Activities/GetPSWorkflowData.cs deleted file mode 100644 index 599480c688c..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/GetPSWorkflowData.cs +++ /dev/null @@ -1,338 +0,0 @@ -using System; -using System.Activities; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Globalization; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; -using System.Management.Automation.Tracing; -using System.ComponentModel; - -namespace Microsoft.PowerShell.Activities -{ - - /// - /// - /// - public enum PSWorkflowRuntimeVariable - { - // Command Parameters - /// - /// - /// - PSComputerName = 0, - /// - /// - /// - PSCredential = 1, - /// - /// - /// - PSPort = 2, - /// - /// - /// - PSUseSsl = 3, - /// - /// - /// - PSConfigurationName = 4, - /// - /// - /// - PSApplicationName = 5, - /// - /// - /// - PSConnectionUri = 6, - /// - /// - /// - PSAllowRedirection = 7, - /// - /// - /// - PSSessionOption = 8, - /// - /// - /// - PSAuthentication = 9, - /// - /// - /// - PSAuthenticationLevel = 10, - /// - /// - /// - PSCertificateThumbprint = 11, - /// - /// - /// - Input = 13, - /// - /// - /// - Verbose = 15, - - // Retry policy constants - /// - /// - /// - PSConnectionRetryCount = 19, - /// - /// - /// - PSConnectionRetryIntervalSec = 21, - - /// - /// - /// - PSPrivateMetadata = 24, - - // Timers - /// - /// - /// - PSRunningTimeoutSec = 27, - /// - /// - /// - PSElapsedTimeoutSec = 28, - - /// - /// - /// - PSWorkflowRoot = 31, - - /// - /// - /// - JobName = 32, - /// - /// - /// - JobInstanceId = 33, - /// - /// - /// - JobId = 34, - - /// - /// - /// - JobCommandName = 36, - - /// - /// - /// - ParentJobInstanceId = 40, - - /// - /// - /// - ParentJobName = 41, - - /// - /// - /// - ParentJobId = 42, - - /// - /// - /// - ParentCommandName = 43, - - /// - /// - /// - WorkflowInstanceId = 48, - - /// - /// - /// - PSSenderInfo = 49, - - /// - /// - /// - PSCulture = 50, - - /// - /// - /// - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly")] - PSUICulture = 51, - - /// - /// - /// - PSVersionTable = 52, - - /// - /// PSPersist - /// - PSPersist = 53, - - /// - /// ErrorAction - /// - ErrorAction = 54, - - /// - /// WarningAction - /// - WarningAction = 55, - - /// - /// InformationAction - /// - InformationAction = 56, - - /// - /// Tell the activity to look for a custom string - /// - Other = 1000, - - /// - /// Return all values as a hashtable - /// - All = 1001, - } - - /// - /// Activity to retrieve the value of a workflow runtime variable. - /// - public sealed class GetPSWorkflowData : NativeActivity - { - /// - /// The variable to retrieve. - /// - [RequiredArgument] - public PSWorkflowRuntimeVariable VariableToRetrieve - { - get; - set; - } - - /// - /// The variable to retrieve, if not included in the PSWorkflowRuntimeVariable enum. - /// - [DefaultValue(null)] - public InArgument OtherVariableName - { - get; - set; - } - - /// - /// Execute the logic for this activity... - /// - /// - protected override void Execute(NativeActivityContext context) - { - // Retrieve our host overrides - HostParameterDefaults hostValues = context.GetExtension(); - - PropertyDescriptorCollection col = context.DataContext.GetProperties(); - string variableName = null; - - if (VariableToRetrieve != PSWorkflowRuntimeVariable.Other) - { - // Get the symbolic name for the enum - variableName = LanguagePrimitives.ConvertTo(VariableToRetrieve); - } - else - { - if (OtherVariableName.Expression != null) - { - string value = OtherVariableName.Get(context); - - if (!string.IsNullOrWhiteSpace(value)) - { - variableName = value; - } - } - } - - //BUGBUG need a better exception here, could also do this as a custom validator - // Make sure we have a variable here... - if (string.IsNullOrWhiteSpace(variableName)) - { - throw new InvalidOperationException("OtherVariable"); - } - - object valueToReturn = null; - PSDataCollection outputStream = null; - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (string.Equals(property.Name, "ParameterDefaults", StringComparison.OrdinalIgnoreCase)) - { - foreach (var parameter in ((Microsoft.PowerShell.Activities.HostParameterDefaults)property.GetValue(context.DataContext)).Parameters) - { - if (parameter.Key.Equals(variableName, StringComparison.OrdinalIgnoreCase)) - { - valueToReturn = parameter.Value; - } - else if (parameter.Key.Equals("Result")) - { - outputStream = parameter.Value as PSDataCollection; - } - } - - // - // If the property to return was all, then just return the entire collection as a hashtable. - // (We still needed to loop to find the output stream to write into.) - // - if (VariableToRetrieve == PSWorkflowRuntimeVariable.All) - { - System.Collections.Hashtable workflowRuntimeVariables = new System.Collections.Hashtable(StringComparer.OrdinalIgnoreCase); - - string[] enumNames = VariableToRetrieve.GetType().GetEnumNames(); - - // Skipping last two enum names, Other and All, as they are not actual variable names - // - for (int i=0; i < (enumNames.Length - 2); i++) - { - workflowRuntimeVariables.Add(enumNames[i], null); - } - - Dictionary dictionaryParam = ((Microsoft.PowerShell.Activities.HostParameterDefaults)property.GetValue(context.DataContext)).Parameters; - - foreach(string varKey in dictionaryParam.Keys) - { - // We need to get the values of required runtime variables only, not everything from DataContext parameters - // - if (workflowRuntimeVariables.ContainsKey(varKey)) - { - Object value = null; - dictionaryParam.TryGetValue(varKey, out value); - workflowRuntimeVariables[varKey] = value; - } - } - - valueToReturn = workflowRuntimeVariables; - } - break; - } - } - - if (this.Result.Expression != null) - { - this.Result.Set(context, valueToReturn); - } - else if (outputStream != null) - { - if (valueToReturn != null) - { - outputStream.Add(PSObject.AsPSObject(valueToReturn)); - } - } - else - { - //BUGBUG need a better exception here... - throw new InvalidOperationException("Result"); - } - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/InlineScript.cs b/src/Microsoft.PowerShell.Activities/Activities/InlineScript.cs deleted file mode 100644 index 1601beb0805..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/InlineScript.cs +++ /dev/null @@ -1,812 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.Activities; -using System.Collections.Generic; -using System.Globalization; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.ComponentModel; -using System.Text; -using System.Reflection; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Activity to support the invocation of PowerShell script content in a Workflow. - /// -#if _NOTARMBUILD_ - [Designer(typeof(InlineScriptDesigner))] -#endif - public sealed class InlineScript : PSRemotingActivity - { - /// - /// The script text to invoke. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public string Command - { - get { return _command; } - set - { - _command = value; - _commandSpecified = true; - } - } - private string _command; - private bool _commandSpecified; - - /// - /// Name of the command to invoke - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CommandName { get; set; } - - /// - /// Parameters to invoke the command with. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Parameters { get; set; } - - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } } - - private ScriptBlock _compiledScriptForInProc; - private ScriptBlock _compiledScriptForOutProc; - private string _scriptWithoutUsing; - private HashSet _usingVariables; - // Remember the names of the variables/arguments that statically exist in the - // workflow context and potentially can be referenced by a using variable in - // an InlineScript. Those static variables/arguments include: - // 1. the default arguments of InlineScript and its parents - // 2. the workflow runtime variables - // - // This static set is used to decide whether to add the special prefix - // to a variable or not, when replacing a using variable. - private static readonly HashSet StaticPotentialUsingVariableSet = new HashSet(StringComparer.OrdinalIgnoreCase); - private const string VariablePrefix = "__PSUsingVariable_"; - - - static InlineScript() - { - PopulatePotentialUsingVariableStaticSet(); - } - - private static void PopulatePotentialUsingVariableStaticSet() - { - var namesToExclude = new HashSet(StringComparer.OrdinalIgnoreCase) - { - // from inlinescript common arguments - "Result", "PSError", "PSWarning", "PSVerbose", "PSDebug", "PSProgress", "PSInformation", - - // from workflow runtime variables - "Other", "All", - - // some workflow variables/arguments conflict with the built-in powershell variables, including: - // Input, PSSessionOption, PSCulture, PSUICulture, PSVersionTable - // - // per discussion with Hemant and Rahim, we want to: - // 1. treat $using:input, $using:PSSessionOption, $using:PSCulture, and $using:PSUICulture as workflow - // variable; add the special prefix when replacing 'using'. - // 2. treat PSVersionTable as powershell variable, so never add special prefix to it. - "PSVersionTable" - }; - - // Handle InlineScript activity common arguments - foreach (string argumentName in GetInlineScriptActivityArguments()) - { - if (namesToExclude.Contains(argumentName)) { continue; } - if (!StaticPotentialUsingVariableSet.Contains(argumentName)) - { - StaticPotentialUsingVariableSet.Add(argumentName); - } - } - - // Handle workflow runtime variables - var wfRuntimeVariables = typeof(PSWorkflowRuntimeVariable).GetEnumNames(); - foreach (string variableName in wfRuntimeVariables) - { - if (namesToExclude.Contains(variableName)) { continue; } - if (!StaticPotentialUsingVariableSet.Contains(variableName)) - { - StaticPotentialUsingVariableSet.Add(variableName); - } - } - } - - // Use the same logic as the PSActivity.GetActivityArguments to retrieve the names of all default - // arguments from the InlineScript and its parents - internal static IEnumerable GetInlineScriptActivityArguments() - { - Type activityType = typeof(InlineScript); - - while (activityType != null) - { - // We don't want to support parameter defaults for arguments on - // concrete types (as they almost guaranteed to collide with other types), - // but base classes make sense. - if (activityType.IsAbstract) - { - // Populate any parameter defaults. We only look at fields that are defined on this - // specific type (as opposed to derived types) so that we don't make assumptions about - // other activities and their defaults. - foreach (PropertyInfo field in activityType.GetProperties()) - { - // See if it's an argument - if (typeof(Argument).IsAssignableFrom(field.PropertyType)) - { - // Get the argument name - yield return field.Name; - } - } - } - - // Go to our base type, but stop when we go above PSActivity - activityType = activityType.BaseType; - if (!typeof(PSActivity).IsAssignableFrom(activityType)) - activityType = null; - } - } - - /// - /// Validates the contents of the script block for this command. - /// - /// Metadata for this activity - protected override void CacheMetadata(NativeActivityMetadata metadata) - { - base.CacheMetadata(metadata); - - if (! string.IsNullOrWhiteSpace(Command)) - { - Token[] tokens; - ParseError[] errors; - Parser.ParseInput(Command, out tokens, out errors); - if (errors != null && errors.Length > 0) - { - string compositeErrorString = ""; - foreach (var e in errors) - { - // Format and add each error message... - compositeErrorString += string.Format(CultureInfo.InvariantCulture, - "[{0}, {1}]: {2}\n", e.Extent.StartLineNumber, e.Extent.StartColumnNumber, e.Message); - } - metadata.AddValidationError(compositeErrorString); - } - } - } - - /// - /// Indicates if preference variables need to be updated - /// - protected override bool UpdatePreferenceVariable - { - get { return false; } - } - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the script to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Microsoft.Reliability", - "CA2000:Dispose objects before losing scope", - Justification = "Disposed by the infrastructure.")] - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - ValidateParameters(); - System.Management.Automation.PowerShell invoker = null; - HashSet allWorkflowVarNames = new HashSet(StaticPotentialUsingVariableSet, StringComparer.OrdinalIgnoreCase); - Dictionary defaults = this.ParameterDefaults.Get(context); - Dictionary activityVariables = new Dictionary(StringComparer.OrdinalIgnoreCase); - Dictionary activityUsingVariables = new Dictionary(StringComparer.OrdinalIgnoreCase); - - string[] streams = - { - "Result", "PSError", "PSWarning", "PSVerbose", "PSDebug", "PSProgress", "PSInformation" - }; - - // First, set the variables from the user's variables - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (String.Equals(property.Name, "ParameterDefaults", StringComparison.OrdinalIgnoreCase)) - continue; - - // Add all user-defined variables/parameters in the same scope of the InlineScript activity - if (!allWorkflowVarNames.Contains(property.Name)) - { - allWorkflowVarNames.Add(property.Name); - } - - Object value = property.GetValue(context.DataContext); - if (value != null) - { - object tempValue = value; - - PSDataCollection collectionObject = value as PSDataCollection; - - if (collectionObject != null && collectionObject.Count == 1) - { - tempValue = collectionObject[0]; - } - - activityVariables[property.Name] = tempValue; - } - } - - // Then, set anything we received from parameters - foreach (PSActivityArgumentInfo currentArgument in GetActivityArguments()) - { - string @default = currentArgument.Name; - if (streams.Any(item => string.Equals(item, @default, StringComparison.OrdinalIgnoreCase))) - continue; - - object argumentValue = currentArgument.Value.Get(context); - if (argumentValue != null && !activityVariables.ContainsKey(currentArgument.Name)) - { - activityVariables[currentArgument.Name] = argumentValue; - } - } - - // Then, set the variables from the host defaults - if (defaults != null) - { - foreach (string hostDefault in defaults.Keys) - { - string @default = hostDefault; - if (streams.Any(item => string.Equals(item, @default, StringComparison.OrdinalIgnoreCase))) - continue; - - object propertyValue = defaults[hostDefault]; - if (propertyValue != null && !activityVariables.ContainsKey(hostDefault)) - { - activityVariables[hostDefault] = propertyValue; - } - } - } - - if (_commandSpecified) - { - string script = string.IsNullOrEmpty(Command) ? string.Empty : Command; - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Inline Script: '{1}'.", context.ActivityInstanceId, script)); - - if (IsBlocked(script)) - { - throw new PSInvalidOperationException(String.Format(CultureInfo.InvariantCulture, ActivityResources.CannotLaunchFormat, script)); - } - - string[] targetNodes = null; - if (this.PSComputerName.Expression != null) - { - targetNodes = this.PSComputerName.Get(context); - } - else - { - if (defaults != null && defaults.ContainsKey("PSComputerName")) - { - targetNodes = this.ParameterDefaults.Get(context)["PSComputerName"] as string[]; - } - } - - // See if this command will be run in process. - if ((targetNodes == null || targetNodes.Length == 0) && GetRunInProc(context)) - { - if (_compiledScriptForInProc == null || _ci == null) - { - lock (Syncroot) - { - if (_compiledScriptForInProc == null) - { - if (_scriptWithoutUsing == null) - { - _scriptWithoutUsing = RemoveUsingPrefix(script, allWorkflowVarNames, out _usingVariables); - } - _compiledScriptForInProc = ScriptBlock.Create(_scriptWithoutUsing); - } - - // Invoke using the CommandInfo for Invoke-Command directly, rather than going through - // the command discovery since this is much faster. - if (_ci == null) - { - _ci = new CmdletInfo("Invoke-Command", typeof(Microsoft.PowerShell.Commands.InvokeCommandCommand)); - } - } - } - - SetAvailableUsingVariables(activityVariables, activityUsingVariables); - Tracer.WriteMessage("PowerShell activity: executing InlineScript locally with ScriptBlock."); - invoker = System.Management.Automation.PowerShell.Create(); - invoker.AddCommand(_ci).AddParameter("NoNewScope").AddParameter("ScriptBlock", _compiledScriptForInProc); - } - else - { - // Try to convert the ScriptBlock to a powershell instance - if (_compiledScriptForOutProc == null) - { - lock (Syncroot) - { - if (_compiledScriptForOutProc == null) - { - _compiledScriptForOutProc = ScriptBlock.Create(script); - } - } - } - - try - { - // we trust the code inside inlinescript, set isTrusted as True. - invoker = _compiledScriptForOutProc.GetPowerShell(activityVariables, out activityUsingVariables, true); - Tracer.WriteMessage("PowerShell activity: executing InlineScript with ScriptBlock to powershell conversion."); - } - catch (Exception) - { - invoker = null; - } - - if (invoker == null) - { - // Since scriptblocks aren't serialized with fidelity in the remote case, we need to - // use AddScript instead. - if (_scriptWithoutUsing == null) - { - lock (Syncroot) - { - if (_scriptWithoutUsing == null) - { - _scriptWithoutUsing = RemoveUsingPrefix(script, allWorkflowVarNames, out _usingVariables); - } - } - } - - SetAvailableUsingVariables(activityVariables, activityUsingVariables); - Tracer.WriteMessage("PowerShell activity: executing InlineScript by using AddScript."); - invoker = System.Management.Automation.PowerShell.Create(); - invoker.AddScript(_scriptWithoutUsing); - } - } - } - else - { - string commandName = CommandName.Get(context); - if (String.IsNullOrEmpty(commandName)) - { - throw new ArgumentException(ActivityResources.CommandNameRequired); - } - - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Invoking command '{1}'.", context.ActivityInstanceId, commandName)); - invoker = System.Management.Automation.PowerShell.Create(); - invoker.AddCommand(commandName); - - System.Collections.Hashtable parameters = Parameters.Get(context); - - if (parameters != null && parameters.Count > 0) - { - foreach (var key in parameters.Keys) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity: Adding parameter '-{0} {1}'.", - key, parameters[key])); - } - invoker.AddParameters(parameters); - } - } - - var implementationContext = new ActivityImplementationContext - { - PowerShellInstance = invoker, - WorkflowContext = activityUsingVariables - }; - - return implementationContext; - } - - private void SetAvailableUsingVariables(Dictionary allActivityVariables, Dictionary activityUsingVariables) - { - if (_usingVariables == null) { return; } - - foreach (string varName in _usingVariables) - { - object value; - string varNameToUse = VariablePrefix + varName; - if (allActivityVariables.TryGetValue(varName, out value) && !activityUsingVariables.ContainsKey(varNameToUse)) - { - activityUsingVariables.Add(varNameToUse, value); - } - } - } - - private void ValidateParameters() - { - if (_commandSpecified) - { - if (CommandName.Expression != null || Parameters.Expression != null) - { - throw new ArgumentException(ActivityResources.CannotSpecifyBothCommandAndCommandName); - } - } - else - { - if (CommandName.Expression == null) - { - throw new ArgumentException(ActivityResources.CannotSpecifyBothCommandAndCommandName); - } - } - } - - /// - /// Checks if the script is blocked - /// - /// - private bool IsBlocked(string script) - { - string[] psUnsupportedConsoleApplications = new string[] - { - "cmd", - "cmd.exe", - "diskpart", - "diskpart.exe", - "edit.com", - "netsh", - "netsh.exe", - "nslookup", - "nslookup.exe", - "powershell", - "powershell.exe", - }; - - foreach (string app in psUnsupportedConsoleApplications) - { - if (script.Equals(app, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } - - #region "Using variable utility" - - /// - /// Remove the "Using" prefix for all UsingExpressionAsts that appear in the given script - /// - /// script text - /// all workflow variables/arguments that potentially can be referred by a using variable - /// names of the variables in the script that have the "Using" prefix - /// - /// Return script if the script text is empty string or null - /// Return script if there are errors when parsing the script text - /// Return script if there is no UsingExpressionAst in the given script - /// Return a new script text that has all the "Using" prefixes removed - /// - private static string RemoveUsingPrefix(string script, HashSet allWorkflowVariables, out HashSet usingVariables) - { - usingVariables = new HashSet(StringComparer.OrdinalIgnoreCase); - var usingAsts = GetUsingExpressionAsts(script); - if (usingAsts == null || !usingAsts.Any()) { return script; } - - StringBuilder newScript = null; - int startOffset = 0; - foreach (Ast ast in usingAsts) - { - var usingAst = ast as UsingExpressionAst; - if (usingAst == null) { continue; } - - VariableExpressionAst variableAst = UsingExpressionAst.ExtractUsingVariable(usingAst); - if (variableAst == null) { continue; } - - if (newScript == null) - { - newScript = new StringBuilder(); - } - - string varName = variableAst.VariablePath.UserPath; - string varSign = variableAst.Splatted ? "@" : "$"; - bool needPrefix = allWorkflowVariables.Contains(varName); - string newVar = needPrefix ? (varSign + VariablePrefix + varName) : (varSign + varName); - - // Add those variable names that potentially refer to workflow variables/arguments - if (needPrefix && !usingVariables.Contains(varName)) - { - usingVariables.Add(varName); - } - - newScript.Append(script.Substring(startOffset, variableAst.Extent.StartOffset - startOffset)); - newScript.Append(newVar); - startOffset = variableAst.Extent.EndOffset; - } - - if (newScript != null) - { - newScript.Append(script.Substring(startOffset)); - return newScript.ToString(); - } - - return script; - } - - /// - /// Get the UsingExpressionAsts out of a script - /// - /// - /// a list of UsingExpressionAsts ordered by the StartOffset - private static IEnumerable GetUsingExpressionAsts(string script) - { - if (String.IsNullOrEmpty(script)) - { - return null; - } - - ParseError[] errors; - Token[] tokens; - ScriptBlockAst scriptAst = Parser.ParseInput(script, out tokens, out errors); - if (errors.Length != 0) - { - return null; - } - - var list = scriptAst.FindAll(ast => ast is UsingExpressionAst, searchNestedScriptBlocks: true).ToList(); - if (list.Count > 1) - { - return list.OrderBy(a => a.Extent.StartOffset); - } - return list; - } - - #endregion "Using variable utility" - - /// - /// Adds the PSActivity variable to the active runspace, which is of type InlineScriptContext. - /// - /// The ActivityImplementationContext returned by the call to GetCommand. - protected override void PrepareSession(ActivityImplementationContext implementationContext) - { - if (implementationContext.PSActivityEnvironment == null) - { - implementationContext.PSActivityEnvironment = new PSActivityEnvironment(); - } - - // Update the preference variables - UpdatePreferenceVariables(implementationContext); - System.Management.Automation.PowerShell session = implementationContext.PowerShellInstance; - - implementationContext.PSActivityEnvironment.Variables["UserName"] = System.Environment.UserName; - - string computerName = null; - if (implementationContext.ConnectionInfo != null) - { - computerName = implementationContext.ConnectionInfo.ComputerName; - } - if (string.IsNullOrEmpty(computerName)) - { - computerName = "localhost"; - } - - implementationContext.PSActivityEnvironment.Variables["ComputerName"] = computerName; - implementationContext.PSActivityEnvironment.Variables["PSComputerName"] = computerName; - - string workflowCommandName = null; - - Dictionary activityVariables = (Dictionary)implementationContext.WorkflowContext; - if (activityVariables != null && activityVariables.ContainsKey("ParameterDefaults")) - { - HostParameterDefaults defaults = activityVariables["ParameterDefaults"] as HostParameterDefaults; - if (defaults != null) - { - workflowCommandName = defaults.Parameters["WorkflowCommandName"] as string; - } - } - - if (string.IsNullOrEmpty(workflowCommandName)) - { - workflowCommandName = "unknown"; - } - - implementationContext.PSActivityEnvironment.Variables["CommandName"] = workflowCommandName; - - // Populate the default variables - InlineScriptContext inlineScriptContext = new InlineScriptContext(this); - - // Populate the activity variables - foreach (KeyValuePair entry in activityVariables) - { - if (String.Equals(entry.Key, "ParameterDefaults", StringComparison.OrdinalIgnoreCase)) - { - System.Diagnostics.Debug.Assert(entry.Value is HostParameterDefaults, "ParameterDefaults does not contain a HostParameterDefaults object"); - inlineScriptContext.Variables[entry.Key] = ((HostParameterDefaults)entry.Value).Parameters; - continue; - } - inlineScriptContext.Variables[entry.Key] = entry.Value; - } - - // Set the PowerShell session variables... - foreach (KeyValuePair entry in activityVariables) - { - var value = entry.Value; - - if (String.Equals(entry.Key, "ParameterDefaults", StringComparison.OrdinalIgnoreCase)) - continue; - implementationContext.PSActivityEnvironment.Variables[entry.Key] = value; - } - } - - // InlineScript needs to handle these specially, since it might go through the PowerShell AddScript() API. - // If the parameter "CommandName" is in use, we add the preference configuration to the command parameters, - // otherwise, we add the preference configuration to the preference variable. - // All other activities have this set automatically by the infrastructure via parameters. - private void UpdatePreferenceVariables(ActivityImplementationContext implementationContext) - { - System.Management.Automation.PowerShell session = implementationContext.PowerShellInstance; - System.Management.Automation.Runspaces.Command command = null; - - if (!_commandSpecified) - { - // "CommandName" and "Parameters" are in use - command = session.Commands.Commands[0]; - } - - if (implementationContext.Verbose != null) - { - if (command != null) - { - command.Parameters.Add("Verbose", implementationContext.Verbose); - } - else - { - // Map the boolean / switch to an actual action preference - ActionPreference preference = ActionPreference.SilentlyContinue; - - if (implementationContext.Verbose.Value) - preference = ActionPreference.Continue; - - implementationContext.PSActivityEnvironment.Variables["VerbosePreference"] = preference; - } - } - - if (implementationContext.Debug != null) - { - if (command != null) - { - command.Parameters.Add("Debug", implementationContext.Debug); - } - else - { - // Map the boolean / switch to an actual action preference - ActionPreference preference = ActionPreference.SilentlyContinue; - - if (implementationContext.Debug.Value) - preference = ActionPreference.Continue; - - implementationContext.PSActivityEnvironment.Variables["DebugPreference"] = preference; - } - } - - if (implementationContext.WhatIf != null && command != null) - { - command.Parameters.Add("WhatIf", implementationContext.WhatIf); - } - - if (implementationContext.ErrorAction != null) - { - if (command != null) - { - command.Parameters.Add("ErrorAction", implementationContext.ErrorAction); - } - else - { - implementationContext.PSActivityEnvironment.Variables["ErrorActionPreference"] = implementationContext.ErrorAction; - } - } - - if (implementationContext.WarningAction != null) - { - if (command != null) - { - command.Parameters.Add("WarningAction", implementationContext.WarningAction); - } - else - { - implementationContext.PSActivityEnvironment.Variables["WarningPreference"] = implementationContext.WarningAction; - } - } - - if (implementationContext.InformationAction != null) - { - if (command != null) - { - command.Parameters.Add("InformationAction", implementationContext.InformationAction); - } - else - { - implementationContext.PSActivityEnvironment.Variables["InformationPreference"] = implementationContext.InformationAction; - } - } - - } - - static CommandInfo _ci; - static readonly object Syncroot = new object(); - } - - /// - /// Defines the context information available to scripts running within the - /// InlineScript activity. These are exposed through the $PSActivity automatic - /// variable. - /// - public class InlineScriptContext - { - /// - /// Creates a new InlineScriptContext - /// - /// The InlineScript activity being invoked - public InlineScriptContext(InlineScript current) - { - this.current = current; - this.variables = new Dictionary(StringComparer.OrdinalIgnoreCase); - this.current = null; - } - - /// - /// Gets the current InlineScript activity being invoked. - /// - //public InlineScript Current - //{ - // get { return current; } - //} - private InlineScript current; - - /// - /// Gets the current variables and arguments that are in-scope for - /// the current activity within its context in the workflow. - /// - public Dictionary Variables - { - get { return variables; } - } - private Dictionary variables; - } - - /// - /// Suspends the current workflow. - /// - public class Suspend : NativeActivity - { - /// - /// Optional field used for resuming the workflow for a specific label. - /// - public string Label { get; set; } - - /// - /// Returns true if the activity can induce an idle. - /// - protected override bool CanInduceIdle { get { return true; } } - - /// - /// Invokes the activity - /// - /// The activity context. - /// True if the given argument is set. - protected override void Execute(NativeActivityContext context) - { - string bookmarkname = string.IsNullOrEmpty(this.Label) ? - PSActivity.PSSuspendBookmarkPrefix : - PSActivity.PSSuspendBookmarkPrefix + this.Label + "_"; - - bookmarkname += Guid.NewGuid().ToString().Replace("-", "_"); - - context.CreateBookmark(bookmarkname, BookmarkResumed); - } - - private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value) - { - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/InlineScriptDesigner.xaml.cs b/src/Microsoft.PowerShell.Activities/Activities/InlineScriptDesigner.xaml.cs deleted file mode 100644 index a8580782e1b..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/InlineScriptDesigner.xaml.cs +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -#if _NOTARMBUILD_ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Interaction logic for InlineScriptDesigner.xaml - /// - public partial class InlineScriptDesigner - { - /// - /// Create the designer instance - /// - public InlineScriptDesigner() - { - InitializeComponent(); - } - } -} -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Activities/Activities/InvokeCimMethodActivity.cs b/src/Microsoft.PowerShell.Activities/Activities/InvokeCimMethodActivity.cs deleted file mode 100644 index 5dda7530c0b..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/InvokeCimMethodActivity.cs +++ /dev/null @@ -1,170 +0,0 @@ - -using Microsoft.PowerShell.Activities; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Activity to invoke the CimCmdlets\Invoke-CimMethod command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class InvokeCimMethod : GenericCimCmdletActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public InvokeCimMethod() - { - this.DisplayName = "Invoke-CimMethod"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "CimCmdlets\\Invoke-CimMethod"; } } - - /// - /// The .NET type implementing the cmdlet to invoke. - /// - public override System.Type TypeImplementingCmdlet { get { return typeof(Microsoft.Management.Infrastructure.CimCmdlets.InvokeCimMethodCommand); } } - - // Arguments - - /// - /// Provides access to the ClassName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ClassName { get; set; } - - /// - /// Provides access to the CimClass parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CimClass { get; set; } - - /// - /// Provides access to the Query parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Query { get; set; } - - /// - /// Provides access to the QueryDialect parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument QueryDialect { get; set; } - - /// - /// Provides access to the InputObject parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument InputObject { get; set; } - - /// - /// Provides access to the Arguments parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Arguments { get; set; } - - /// - /// Provides access to the MethodName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument MethodName { get; set; } - - /// - /// Provides access to the Namespace parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Namespace { get; set; } - - /// - /// Provides access to the OperationTimeoutSec parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OperationTimeoutSec { get; set; } - - /// - /// Script module contents for this activity - /// - protected override string PSDefiningModule { get { return null; } } - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ClassName.Expression != null) - { - targetCommand.AddParameter("ClassName", ClassName.Get(context)); - } - - if(CimClass.Expression != null) - { - targetCommand.AddParameter("CimClass", CimClass.Get(context)); - } - - if(Query.Expression != null) - { - targetCommand.AddParameter("Query", Query.Get(context)); - } - - if(QueryDialect.Expression != null) - { - targetCommand.AddParameter("QueryDialect", QueryDialect.Get(context)); - } - - if (InputObject.Expression != null) - { - targetCommand.AddParameter("InputObject", InputObject.Get(context)); - } - - if(Arguments.Expression != null) - { - targetCommand.AddParameter("Arguments", Arguments.Get(context)); - } - - if(MethodName.Expression != null) - { - targetCommand.AddParameter("MethodName", MethodName.Get(context)); - } - - if(Namespace.Expression != null) - { - targetCommand.AddParameter("Namespace", Namespace.Get(context)); - } - - if(OperationTimeoutSec.Expression != null) - { - targetCommand.AddParameter("OperationTimeoutSec", OperationTimeoutSec.Get(context)); - } - - if (ResourceUri != null) - { - targetCommand.AddParameter("ResourceUri", ResourceUri.Get(context)); - } - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/IsArgumentSet.cs b/src/Microsoft.PowerShell.Activities/Activities/IsArgumentSet.cs deleted file mode 100644 index f1e13655896..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/IsArgumentSet.cs +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.Activities; -using System.Collections.Generic; -using System.Globalization; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.ComponentModel; -using System.Text; -using System.Reflection; - -namespace Microsoft.PowerShell.Activities.Internal -{ - /// - /// Determines whether an argument to a PSActivity activity - /// has been set. - /// - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Needed Internal use only")] - public class IsArgumentSet : CodeActivity - { - /// - /// The argument to investigate. - /// - [DefaultValue(null)] - public Argument Argument { get; set; } - - /// - /// Invokes the activity - /// - /// The activity context. - /// True if the given argument is set. - protected override bool Execute(CodeActivityContext context) - { - return Argument != null && Argument.Expression != null; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Activities/Activities/NewCimInstanceActivity.cs b/src/Microsoft.PowerShell.Activities/Activities/NewCimInstanceActivity.cs deleted file mode 100644 index 7d503cb4e4a..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/NewCimInstanceActivity.cs +++ /dev/null @@ -1,163 +0,0 @@ - -using Microsoft.PowerShell.Activities; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Activity to invoke the CimCmdlets\New-CimInstance command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class NewCimInstance : GenericCimCmdletActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public NewCimInstance() - { - this.DisplayName = "New-CimInstance"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "CimCmdlets\\New-CimInstance"; } } - - /// - /// The .NET type implementing the cmdlet to invoke. - /// - public override System.Type TypeImplementingCmdlet { get { return typeof(Microsoft.Management.Infrastructure.CimCmdlets.NewCimInstanceCommand); } } - - // Arguments - - /// - /// Provides access to the ClassName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ClassName { get; set; } - - /// - /// Provides access to the Key parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Key { get; set; } - - /// - /// Provides access to the CimClass parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CimClass { get; set; } - - /// - /// Provides access to the Property parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Property { get; set; } - - /// - /// Provides access to the Namespace parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Namespace { get; set; } - - /// - /// Provides access to the OperationTimeoutSec parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OperationTimeoutSec { get; set; } - - - /// - /// Provides access to the ClientOnly parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ClientOnly { get; set; } - - /// - /// Script module contents for this activity`n/// - protected override string PSDefiningModule { get { return null; } } - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ClassName.Expression != null) - { - targetCommand.AddParameter("ClassName", ClassName.Get(context)); - } - - if(Key.Expression != null) - { - targetCommand.AddParameter("Key", Key.Get(context)); - } - - if(CimClass.Expression != null) - { - targetCommand.AddParameter("CimClass", CimClass.Get(context)); - } - - if(Property.Expression != null) - { - targetCommand.AddParameter("Property", Property.Get(context)); - } - - if(Namespace.Expression != null) - { - targetCommand.AddParameter("Namespace", Namespace.Get(context)); - } - - if(OperationTimeoutSec.Expression != null) - { - targetCommand.AddParameter("OperationTimeoutSec", OperationTimeoutSec.Get(context)); - } - - if (ResourceUri != null) - { - targetCommand.AddParameter("ResourceUri", ResourceUri.Get(context)); - } - - if (ClientOnly.Expression != null) - { - // Retrieve our host overrides - var hostValues = context.GetExtension(); - string[] computerName = null; - - if (hostValues != null) - { - Dictionary incomingArguments = hostValues.Parameters; - if (incomingArguments.ContainsKey("PSComputerName")) - { - computerName = incomingArguments["PSComputerName"] as string[]; - } - } - - if (computerName == null) - { - targetCommand.AddParameter("ClientOnly", ClientOnly.Get(context)); - } - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/NewCimSessionActivity.cs b/src/Microsoft.PowerShell.Activities/Activities/NewCimSessionActivity.cs deleted file mode 100644 index 291c9ef5ae9..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/NewCimSessionActivity.cs +++ /dev/null @@ -1,148 +0,0 @@ - -using Microsoft.PowerShell.Activities; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Activity to invoke the CimCmdlets\New-CimSession command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class NewCimSession : GenericCimCmdletActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public NewCimSession() - { - this.DisplayName = "New-CimSession"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "CimCmdlets\\New-CimSession"; } } - - /// - /// The .NET type implementing the cmdlet to invoke. - /// - public override System.Type TypeImplementingCmdlet { get { return typeof(Microsoft.Management.Infrastructure.CimCmdlets.NewCimSessionCommand); } } - - // Arguments - - /// - /// Provides access to the Authentication parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Authentication { get; set; } - - /// - /// Provides access to the Credential parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Credential { get; set; } - - /// - /// Provides access to the CertificateThumbprint parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CertificateThumbprint { get; set; } - - /// - /// Provides access to the Name parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Name { get; set; } - - /// - /// Provides access to the OperationTimeoutSec parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OperationTimeoutSec { get; set; } - - /// - /// Provides access to the Port parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Port { get; set; } - - /// - /// Provides access to the SessionOption parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SessionOption { get; set; } - - - - /// - /// Script module contents for this activity`n/// - protected override string PSDefiningModule { get { return "CimCmdlets"; } } - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (GetIsComputerNameSpecified(context)) - { - targetCommand.AddParameter("ComputerName", PSComputerName.Get(context)); - } - - if(Authentication.Expression != null) - { - targetCommand.AddParameter("Authentication", Authentication.Get(context)); - } - - if(Credential.Expression != null) - { - targetCommand.AddParameter("Credential", Credential.Get(context)); - } - - if(CertificateThumbprint.Expression != null) - { - targetCommand.AddParameter("CertificateThumbprint", CertificateThumbprint.Get(context)); - } - - if(Name.Expression != null) - { - targetCommand.AddParameter("Name", Name.Get(context)); - } - - if(OperationTimeoutSec.Expression != null) - { - targetCommand.AddParameter("OperationTimeoutSec", OperationTimeoutSec.Get(context)); - } - - if(Port.Expression != null) - { - targetCommand.AddParameter("Port", Port.Get(context)); - } - - if(SessionOption.Expression != null) - { - targetCommand.AddParameter("SessionOption", SessionOption.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/NewCimSessionOptionActivity.cs b/src/Microsoft.PowerShell.Activities/Activities/NewCimSessionOptionActivity.cs deleted file mode 100644 index 695072fffde..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/NewCimSessionOptionActivity.cs +++ /dev/null @@ -1,286 +0,0 @@ - -using Microsoft.PowerShell.Activities; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Activity to invoke the CimCmdlets\New-CimSessionOption command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class NewCimSessionOption : GenericCimCmdletActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public NewCimSessionOption() - { - this.DisplayName = "New-CimSessionOption"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "CimCmdlets\\New-CimSessionOption"; } } - - /// - /// The .NET type implementing the cmdlet to invoke. - /// - public override System.Type TypeImplementingCmdlet { get { return typeof(Microsoft.Management.Infrastructure.CimCmdlets.NewCimSessionOptionCommand); } } - - // Arguments - - /// - /// Provides access to the NoEncryption parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument NoEncryption { get; set; } - - /// - /// Provides access to the CertificateCACheck parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CertificateCACheck { get; set; } - - /// - /// Provides access to the CertificateCNCheck parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CertificateCNCheck { get; set; } - - /// - /// Provides access to the CertRevocationCheck parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CertRevocationCheck { get; set; } - - /// - /// Provides access to the EncodePortInServicePrincipalName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument EncodePortInServicePrincipalName { get; set; } - - /// - /// Provides access to the Encoding parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Encoding { get; set; } - - /// - /// Provides access to the HttpPrefix parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument HttpPrefix { get; set; } - - /// - /// Provides access to the MaxEnvelopeSizeKB parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument MaxEnvelopeSizeKB { get; set; } - - /// - /// Provides access to the ProxyAuthentication parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ProxyAuthentication { get; set; } - - /// - /// Provides access to the ProxyCertificateThumbprint parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ProxyCertificateThumbprint { get; set; } - - /// - /// Provides access to the ProxyCredential parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ProxyCredential { get; set; } - - /// - /// Provides access to the ProxyType parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ProxyType { get; set; } - - /// - /// Provides access to the UseSsl parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument UseSsl { get; set; } - - /// - /// Provides access to the Impersonation parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Impersonation { get; set; } - - /// - /// Provides access to the PacketIntegrity parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument PacketIntegrity { get; set; } - - /// - /// Provides access to the PacketPrivacy parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument PacketPrivacy { get; set; } - - /// - /// Provides access to the Protocol parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Protocol { get; set; } - - /// - /// Provides access to the UICulture parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument UICulture { get; set; } - - /// - /// Provides access to the Culture parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Culture { get; set; } - - - /// - /// Script module contents for this activity`n/// - protected override string PSDefiningModule { get { return null; } } - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(NoEncryption.Expression != null) - { - targetCommand.AddParameter("NoEncryption", NoEncryption.Get(context)); - } - - if(CertificateCACheck.Expression != null) - { - targetCommand.AddParameter("CertificateCACheck", CertificateCACheck.Get(context)); - } - - if(CertificateCNCheck.Expression != null) - { - targetCommand.AddParameter("CertificateCNCheck", CertificateCNCheck.Get(context)); - } - - if(CertRevocationCheck.Expression != null) - { - targetCommand.AddParameter("CertRevocationCheck", CertRevocationCheck.Get(context)); - } - - if(EncodePortInServicePrincipalName.Expression != null) - { - targetCommand.AddParameter("EncodePortInServicePrincipalName", EncodePortInServicePrincipalName.Get(context)); - } - - if(Encoding.Expression != null) - { - targetCommand.AddParameter("Encoding", Encoding.Get(context)); - } - - if(HttpPrefix.Expression != null) - { - targetCommand.AddParameter("HttpPrefix", HttpPrefix.Get(context)); - } - - if(MaxEnvelopeSizeKB.Expression != null) - { - targetCommand.AddParameter("MaxEnvelopeSizeKB", MaxEnvelopeSizeKB.Get(context)); - } - - if(ProxyAuthentication.Expression != null) - { - targetCommand.AddParameter("ProxyAuthentication", ProxyAuthentication.Get(context)); - } - - if(ProxyCertificateThumbprint.Expression != null) - { - targetCommand.AddParameter("ProxyCertificateThumbprint", ProxyCertificateThumbprint.Get(context)); - } - - if(ProxyCredential.Expression != null) - { - targetCommand.AddParameter("ProxyCredential", ProxyCredential.Get(context)); - } - - if(ProxyType.Expression != null) - { - targetCommand.AddParameter("ProxyType", ProxyType.Get(context)); - } - - if(UseSsl.Expression != null) - { - targetCommand.AddParameter("UseSsl", UseSsl.Get(context)); - } - - if(Impersonation.Expression != null) - { - targetCommand.AddParameter("Impersonation", Impersonation.Get(context)); - } - - if(PacketIntegrity.Expression != null) - { - targetCommand.AddParameter("PacketIntegrity", PacketIntegrity.Get(context)); - } - - if(PacketPrivacy.Expression != null) - { - targetCommand.AddParameter("PacketPrivacy", PacketPrivacy.Get(context)); - } - - if(Protocol.Expression != null) - { - targetCommand.AddParameter("Protocol", Protocol.Get(context)); - } - - if(UICulture.Expression != null) - { - targetCommand.AddParameter("UICulture", UICulture.Get(context)); - } - - if(Culture.Expression != null) - { - targetCommand.AddParameter("Culture", Culture.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/PSPersist.cs b/src/Microsoft.PowerShell.Activities/Activities/PSPersist.cs deleted file mode 100644 index 266c5056d15..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/PSPersist.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.Activities; -using System.Collections.Generic; -using System.Globalization; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.ComponentModel; -using System.Text; -using System.Reflection; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Persist the current workflow. Also defines the persistence point where suspend-job is getting suspended. - /// - public class PSPersist : NativeActivity - { - /// - /// Returns true if the activity can induce an idle. - /// - protected override bool CanInduceIdle { get { return true; } } - - /// - /// Invokes the activity - /// - /// The activity context. - protected override void Execute(NativeActivityContext context) - { - string bookmarkname = PSActivity.PSPersistBookmarkPrefix + Guid.NewGuid().ToString().Replace("-", "_"); - context.CreateBookmark(bookmarkname, BookmarkResumed); - - } - - private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value) - { - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/Pipeline.cs b/src/Microsoft.PowerShell.Activities/Activities/Pipeline.cs deleted file mode 100644 index fba27cc7275..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/Pipeline.cs +++ /dev/null @@ -1,296 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.Activities; -using System.Activities.Validation; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Runtime; -using System.Activities.Statements; -using System.Management.Automation; -using System.ComponentModel; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// The implementation of pipeline activity. - /// This similar concept which we have in PowerShell today like Get-Process | Stop-Process. - /// Pipeline activity will make sure the piped execution of its child activities. - /// -#if _NOTARMBUILD_ - [Designer (typeof (PipelineDesigner))] -#endif - public sealed class Pipeline : PipelineEnabledActivity - { - /// - /// Tracks the number of current child activity in the collection. - /// - private Variable lastIndexHint; - - private bool inputValidationFailed; - private bool resultValidationFailed; - - /// - /// Maintain intermediate outflow of data from child activity. - /// - private Variable> OutputStream; - - /// - /// Maintain intermediate inflow of data into child activity. - /// - private Variable> InputStream; - - /// - /// Get activities. - /// - [RequiredArgument] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to support assignment via workflow.")] - public Collection Activities { get; set; } - - /// - /// Default constructor - /// - public Pipeline() - : base() - { - this.lastIndexHint = new Variable(); - this.Activities = new Collection(); - - this.inputValidationFailed = false; - this.resultValidationFailed = false; - } - - /// - /// Validate the required number of activities of pipeline activity. - /// Setup the cachemetadata with variables and activities. - /// - /// - protected override void CacheMetadata(NativeActivityMetadata metadata) - { - int count = 0; - - if (this.Activities != null) - { - count = this.Activities.Count; - } - - if (count == 0) - { - metadata.AddValidationError(new ValidationError(ActivityResources.NoChildPipeline, true)); - return; - } - - //BUGBUG: As written, the following checks cause error in scenarios where they should not. - // They are left in for the time being but disabled until we verify that there are no - // scenarios where we need to check for two variables being assigned. -#if false - if (Input != null && Input.Expression != null && this.Activities[0].Input != null && this.Activities[0].Input.Expression != null) - { - metadata.AddValidationError(new ValidationError(ActivityResources.DuplicateInputDefinedInPipeline, true)); - this.inputValidationFailed = true; - return; - } - - if (Result != null && Result.Expression != null && this.Activities[count - 1].Result != null && this.Activities[count - 1].Result.Expression != null) - { - metadata.AddValidationError(new ValidationError(ActivityResources.DuplicateResultDefinedInPipeline, true)); - this.resultValidationFailed = true; - return; - } -#endif - // Adding variables into the CacheMetadata of pipeline activity. - metadata.AddImplementationVariable(this.lastIndexHint); - - // We use a GUID here to make this name hard to guess. It's not a security issue, - // it just prevents code from accidentally taking a dependency on it. - this.OutputStream = new Variable>(Guid.NewGuid().ToString().Replace("-","_")); - this.InputStream = new Variable>(Guid.NewGuid().ToString().Replace("-","_")); - - metadata.AddVariable(this.OutputStream); - metadata.AddVariable(this.InputStream); - - bool appendOutput = false; - if ((this.AppendOutput != null) && (this.AppendOutput.Value)) - { - appendOutput = true; - } - - // Adding activities into the CacheMetadata of pipeline activity. - if (count == 1) - { - - if (Input != null && Input.Expression != null) - { - this.Activities[0].Input = this.Input; - } - - if (Result != null && Result.Expression != null) - { - this.Activities[0].Result = this.Result; - } - - if (appendOutput) - { - this.Activities[0].AppendOutput = true; - } - - metadata.AddChild(this.Activities[0]); - } - else - { - - if (Input != null && Input.Expression != null) - { - this.Activities[0].Input = this.Input; - } - - // Connecting child activities with temporary input and out streams. - this.Activities[0].Result = this.OutputStream; - metadata.AddChild(this.Activities[0]); - - for (int i = 1; i < (count - 1); i++) - { - this.Activities[i].Input = this.InputStream; - this.Activities[i].Result = this.OutputStream; - - metadata.AddChild(this.Activities[i]); - } - - if (Result != null && Result.Expression != null) - { - this.Activities[count - 1].Result = this.Result; - } - - if (appendOutput) - { - this.Activities[count - 1].AppendOutput = true; - } - - this.Activities[count - 1].Input = this.InputStream; - metadata.AddChild(this.Activities[count - 1]); - } - } - - /// - /// Executes the first child activity - /// - /// The execution context of pipeline activity. - protected override void Execute(NativeActivityContext executionContext) - { - int count = 0; - - if (this.Activities != null) - { - count = this.Activities.Count; - } - - if (count == 0) - { - throw new ArgumentException(ActivityResources.NoChildPipeline); - } - - if (this.inputValidationFailed && Input != null && Input.Expression != null && this.Activities[0].Input != null && this.Activities[0].Input.Expression != null) - { - throw new ArgumentException(ActivityResources.DuplicateInputDefinedInPipeline); - } - - if (this.resultValidationFailed && Result != null && Result.Expression != null && this.Activities[count - 1].Result != null && this.Activities[count - 1].Result.Expression != null) - { - throw new ArgumentException(ActivityResources.DuplicateResultDefinedInPipeline); - } - - //Executing the first child activity. - PipelineEnabledActivity firstChild = this.Activities[0]; - executionContext.ScheduleActivity(firstChild, new CompletionCallback(InternalExecute)); - } - - /// - /// Get results from previous activity and schedule the execution of next activity. - /// - /// The execution context of pipeline activity. - /// The activity instance of completed child activity. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - private void InternalExecute(NativeActivityContext executionContext, ActivityInstance completedInstance) - { - int completedInstanceIndex; - - // Reading the value of pipeline activity variables from the context. - completedInstanceIndex = this.lastIndexHint.Get(executionContext); - - PSDataCollection outValue = this.GetData(executionContext, this.OutputStream); - PSDataCollection inValue = this.GetData(executionContext, this.InputStream); - - // Re-checking the index of the the child activity, which has just completed its execution. - if (completedInstanceIndex >= this.Activities.Count || this.Activities[completedInstanceIndex] != completedInstance.Activity) - { - completedInstanceIndex = this.Activities.IndexOf((PSActivity) completedInstance.Activity); - } - - // Calculating next child activity. - int nextChildIndex = completedInstanceIndex + 1; - - // Checking for pipeline activity completion. - if (nextChildIndex == this.Activities.Count) - { - if (inValue != null) inValue.Dispose(); - if (outValue != null) outValue.Dispose(); - return; - } - - // Setting up the environment for next child activity to run. - if (outValue != null) outValue.Complete(); - if (inValue != null) inValue.Dispose(); - - inValue = outValue; - outValue = new PSDataCollection(); - - // The pipeline is complete if there is no input - // PS > function foo { $input | Write-Output "Hello" } - // PS > foo - // PS > - if ((inValue == null) || (inValue.Count == 0)) - { - if (outValue != null) outValue.Dispose(); - return; - } - - this.SetData(executionContext, this.OutputStream, outValue); - this.SetData(executionContext, this.InputStream, inValue); - - // Executing the next child activity. - PipelineEnabledActivity nextChild = this.Activities[nextChildIndex]; - - executionContext.ScheduleActivity(nextChild, new CompletionCallback(InternalExecute)); - - this.lastIndexHint.Set(executionContext, nextChildIndex); - } - - /// - /// Get the data from the pipeline variable. - /// - /// The activity context. - /// The variable which value to get. - /// Returns the value of the variable. - private PSDataCollection GetData(ActivityContext context, Variable> variable) - { - PropertyDescriptor prop = context.DataContext.GetProperties()[variable.Name]; - return (PSDataCollection)prop.GetValue(context.DataContext); - } - - /// - /// Set the data to the pipeline variable. - /// - /// The activity context. - /// The variable which needs to set. - /// The value for the variable. - private void SetData(ActivityContext context, Variable> variable, PSDataCollection value) - { - PropertyDescriptor prop = context.DataContext.GetProperties()[variable.Name]; - prop.SetValue(context.DataContext, value); - } - - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/PipelineDesigner.xaml.cs b/src/Microsoft.PowerShell.Activities/Activities/PipelineDesigner.xaml.cs deleted file mode 100644 index 713ef39e890..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/PipelineDesigner.xaml.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -#if _NOTARMBUILD_ -namespace Microsoft.PowerShell.Activities -{ - /// - /// Interaction logic for PipelineDesigner.xaml - /// - public partial class PipelineDesigner - { - /// - /// Default Constructor. - /// - public PipelineDesigner() - { - InitializeComponent(); - } - - } -} -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Activities/Activities/PowerShellValue.cs b/src/Microsoft.PowerShell.Activities/Activities/PowerShellValue.cs deleted file mode 100644 index 9c1e7cf46f1..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/PowerShellValue.cs +++ /dev/null @@ -1,564 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Concurrent; -using System.ComponentModel; -using System.Globalization; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Activities; -using System.Diagnostics; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.Management.Automation.Runspaces; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Evaluate the Powershell expression and return the value of type T. - /// - public sealed class PowerShellValue : NativeActivity - { - /// - /// The PowerShell expression, which will be evaluated and retuned a type of T value. - /// - [RequiredArgument] - public string Expression { get; set; } - - /// - /// Determines whether to connect the input stream for this activity. - /// - [DefaultValue(false)] - public bool UseDefaultInput - { - get; - set; - } - - /// - /// Validates the syntax of the script text for this activity. - /// - /// Activity metadata for this activity - protected override void CacheMetadata(NativeActivityMetadata metadata) - { - base.CacheMetadata(metadata); - - if (!string.IsNullOrWhiteSpace(Expression)) - { - var errors = new Collection(); - PSParser.Tokenize(Expression, out errors); - if (errors != null && errors.Count > 0) - { - string compositeErrorString = ""; - foreach (var e in errors) - { - // Format and add each error message... - compositeErrorString += string.Format(CultureInfo.InvariantCulture, - "[{0}, {1}]: {2}\n", e.Token.StartLine, e.Token.StartColumn, e.Message); - } - metadata.AddValidationError(compositeErrorString); - } - } - } - - /// - /// Get the scriptblock for this activity, caching it once it's compiled. - /// - private ScriptBlock ExpressionScriptBlock - { - get - { - if (_expressionScriptBlock == null) - { - lock (syncroot) - { - if (_expressionScriptBlock == null) - { - // The guard check for a null expression string is done in Execute() instead - // of in this property. It's also done in the validation check for CacheMetadata - string updatedExpression = Expression; - - // Hack to make sure the $input *does* get unrolled... - if (string.Equals("$input", Expression.Trim(), StringComparison.OrdinalIgnoreCase)) - { - updatedExpression = "$(" + updatedExpression + "\n)"; - } - else - { - Token[] tokens; - ParseError[] errors; - ScriptBlockAst exprAst = Parser.ParseInput(updatedExpression, out tokens, out errors); - if (errors.Length > 0) - { - throw new ParseException(errors); - } - - if (exprAst.BeginBlock == null && exprAst.ProcessBlock == null && exprAst.EndBlock != null) - { - var statements = exprAst.EndBlock.Statements; - if (statements != null && statements.Count == 1) - { - PipelineAst pipeline = statements[0] as PipelineAst; - if (pipeline != null && pipeline.GetPureExpression() != null) - { - // It is very difficult to get equivalent expression semantics in workflow because the engine - // APIs get in the way necessitating a lot of fiddling with the actual expression as well as post-processing - // the result of the expression. - // We wrap a pure expression in an array so that PowerShell's loop unrolling doesn't impact our - // ability to return collections. We also add a trap/break so that terminating errors in expressions - // are turned into exceptions for the PowerShell object. The trap and closing ')' go on their own line - // for the XAML designer case where the expression might have a trailing '#' making the rest of the - // line into a comment. - updatedExpression = ",(" + updatedExpression + "\n); trap { break }"; - } - } - } - } - - _expressionScriptBlock = ScriptBlock.Create(updatedExpression); - } - } - } - return _expressionScriptBlock; - } - } - ScriptBlock _expressionScriptBlock; - - /// - /// Check to see if the expression only uses elements of the restricted language - /// as well as only using the allowed commands and variables. - /// - /// - /// List of command names to allow in the expression - /// - /// - /// List of variable names to allow in the expression. If the collection contains a single - /// element "*", all variables will be allowed including environment variables - /// functions, etc. - /// - /// - /// If true, environment variables are allowed even if the allowedVariables list is empty. - /// - public void ValidateExpressionConstraints(IEnumerable allowedCommands, IEnumerable allowedVariables, bool allowEnvironmentVariables) - { - ExpressionScriptBlock.CheckRestrictedLanguage(allowedCommands, allowedVariables, allowEnvironmentVariables); - } - - /// - /// Execution of PowerShell value activity. - /// PowerShell expression will be evaluated using PowerShell runspace and the value of Type T will be returned. - /// - /// - protected override void Execute(NativeActivityContext context) - { - Token[] tokens; - ParseError[] errors; - ScriptBlockAst exprAst = Parser.ParseInput(Expression, out tokens, out errors); - - bool hasErrorActionPreference = false; - bool hasWarningPreference = false; - bool hasInformationPreference = false; - - // Custom activity participant tracker for updating debugger with current variables and sequence stop points. - // Regex looks for debugger sequence points like: Expression="'3:5:WFFunc1'". - // We specifically disallow TimeSpan values that look like sequence points: Expression="'00:00:01'". - bool isDebugSequencePoint = (!string.IsNullOrEmpty(Expression) && (System.Text.RegularExpressions.Regex.IsMatch(Expression, @"^'\d+:\d+:\S+'$")) && - (typeof(T) != typeof(System.TimeSpan))); - var dataProperties = context.DataContext.GetProperties(); - if (isDebugSequencePoint || (dataProperties.Count > 0)) - { - System.Activities.Tracking.CustomTrackingRecord customRecord = new System.Activities.Tracking.CustomTrackingRecord("PSWorkflowCustomUpdateDebugVariablesTrackingRecord"); - foreach (System.ComponentModel.PropertyDescriptor property in dataProperties) - { - if (String.Equals(property.Name, "ParameterDefaults", StringComparison.OrdinalIgnoreCase)) { continue; } - - Object value = property.GetValue(context.DataContext); - if (value != null) - { - object tempValue = value; - - PSDataCollection collectionObject = value as PSDataCollection; - if (collectionObject != null && collectionObject.Count == 1) - { - tempValue = collectionObject[0]; - } - - customRecord.Data.Add(property.Name, tempValue); - } - } - if (isDebugSequencePoint) - { - customRecord.Data.Add("DebugSequencePoint", Expression); - } - context.Track(customRecord); - } - - if (tokens != null) - { - foreach(Token token in tokens) - { - VariableToken variable = token as VariableToken; - - if (variable != null) - { - if (variable.Name.Equals("ErrorActionPreference", StringComparison.OrdinalIgnoreCase)) - { - hasErrorActionPreference = true; - } - else if (variable.Name.Equals("WarningPreference", StringComparison.OrdinalIgnoreCase)) - { - hasWarningPreference = true; - } - else if (variable.Name.Equals("InformationPreference", StringComparison.OrdinalIgnoreCase)) - { - hasInformationPreference = true; - } - } - } - } - - if (string.IsNullOrEmpty(Expression)) - { - throw new ArgumentException(ActivityResources.NullArgumentExpression); - } - - - if (_ci == null) - { - lock (syncroot) - { - // Invoke using the CommandInfo for Invoke-Command directly, rather than going through - // command discovery (which is very slow). - if (_ci == null) - { - _ci = new CmdletInfo("Invoke-Command", typeof(Microsoft.PowerShell.Commands.InvokeCommandCommand)); - } - } - } - - Collection returnedvalue; - Runspace runspace = null; - bool borrowedRunspace = false; - PSWorkflowHost workflowHost = null; - - if (typeof(ScriptBlock).IsAssignableFrom(typeof(T))) - { - Result.Set(context, ScriptBlock.Create(Expression)); - return; - } - else if (typeof(ScriptBlock[]).IsAssignableFrom(typeof(T))) - { - Result.Set(context, new ScriptBlock[] { ScriptBlock.Create(Expression) }); - return; - } - - PropertyDescriptorCollection col = context.DataContext.GetProperties(); - HostParameterDefaults hostValues = context.GetExtension(); - - // Borrow a runspace from the host if we're not trying to create a ScriptBlock. - // If we are trying to create one, we need to keep it around so that it can be - // invoked multiple times. - if (hostValues != null) - { - workflowHost = hostValues.Runtime; - try - { - runspace = workflowHost.UnboundedLocalRunspaceProvider.GetRunspace(null, 0, 0); - borrowedRunspace = true; - } - catch (Exception) - { - // it is fine to catch generic exception here - // if the local runspace provider does not give us - // a runspace we will create one locally (fallback) - } - } - - if (runspace == null) - { - // Not running with the PowerShell workflow host so directly create the runspace... - runspace = RunspaceFactory.CreateRunspace(InitialSessionState.CreateDefault2()); - runspace.Open(); - } - - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - try - { - ps.Runspace = runspace; - - // Subscribe to DataAdding on the error stream so that we can add position tracking information - if (hostValues != null) - { - HostSettingCommandMetadata sourceCommandMetadata = hostValues.HostCommandMetadata; - - CommandMetadataTable.TryAdd(ps.InstanceId, sourceCommandMetadata); - ps.Streams.Error.DataAdding += HandleErrorDataAdding; - } - - // First, set the variables from the host defaults - if ((hostValues != null) && (hostValues.Parameters != null)) - { - if (hostValues.Parameters.ContainsKey("PSCurrentDirectory")) - { - string path = hostValues.Parameters["PSCurrentDirectory"] as string; - if (path != null) - { - ps.Runspace.SessionStateProxy.Path.SetLocation(path); - } - } - - foreach (string hostDefault in hostValues.Parameters.Keys) - { - string mappedHostDefault = hostDefault; - - if (hostDefault.Equals("ErrorAction", StringComparison.OrdinalIgnoreCase)) - { - if (hasErrorActionPreference) - { - mappedHostDefault = "ErrorActionPreference"; - } - else - { - continue; - } - } - else if (hostDefault.Equals("WarningAction", StringComparison.OrdinalIgnoreCase)) - { - if (hasWarningPreference) - { - mappedHostDefault = "WarningPreference"; - } - else - { - continue; - } - } - else if (hostDefault.Equals("InformationAction", StringComparison.OrdinalIgnoreCase)) - { - if (hasInformationPreference) - { - mappedHostDefault = "InformationPreference"; - } - else - { - continue; - } - } - - object propertyValue = hostValues.Parameters[hostDefault]; - if (propertyValue != null) - { - ps.Runspace.SessionStateProxy.PSVariable.Set(mappedHostDefault, propertyValue); - } - } - } - - // Then, set the variables from the workflow - foreach (PropertyDescriptor p in col) - { - string name = p.Name; - object value = p.GetValue(context.DataContext); - - if (value != null) - { - object tempValue = value; - - PSDataCollection collectionObject = value as PSDataCollection; - - if (collectionObject != null && collectionObject.Count == 1) - { - tempValue = collectionObject[0]; - } - - ps.Runspace.SessionStateProxy.PSVariable.Set(name, tempValue); - } - } - - ps.AddCommand(_ci).AddParameter("NoNewScope").AddParameter("ScriptBlock", ExpressionScriptBlock); - - - // If this needs to consume input, take it from the host stream. - PSDataCollection inputStream = null; - if (UseDefaultInput) - { - // Retrieve our host overrides - hostValues = context.GetExtension(); - - if (hostValues != null) - { - Dictionary incomingArguments = hostValues.Parameters; - if (incomingArguments.ContainsKey("Input")) - { - inputStream = incomingArguments["Input"] as PSDataCollection; - } - } - } - - // Now invoke the pipeline - try - { - if (inputStream != null) - { - returnedvalue = ps.Invoke(inputStream); - inputStream.Clear(); - } - else - { - returnedvalue = ps.Invoke(); - } - } - catch (CmdletInvocationException cie) - { - if (cie.ErrorRecord != null && cie.ErrorRecord.Exception != null) - { - throw cie.InnerException; - } - else - { - throw; - } - } - - } - finally - { - if (hostValues != null) - { - ps.Streams.Error.DataAdding -= HandleErrorDataAdding; - HostSettingCommandMetadata removedValue; - CommandMetadataTable.TryRemove(ps.InstanceId, out removedValue); - } - - if (borrowedRunspace) - { - workflowHost.UnboundedLocalRunspaceProvider.ReleaseRunspace(runspace); - } - else - { - // This will be disposed when the command is done with it. - runspace.Dispose(); - runspace = null; - } - } - - - if (ps.Streams.Error != null && ps.Streams.Error.Count > 0) - { - PSDataCollection errorStream = null; - - // Retrieve our host overrides - hostValues = context.GetExtension(); - - if (hostValues != null) - { - Dictionary incomingArguments = hostValues.Parameters; - if (incomingArguments.ContainsKey("PSError")) - { - errorStream = incomingArguments["PSError"] as PSDataCollection; - } - } - - if (errorStream != null && errorStream.IsOpen) - { - foreach (ErrorRecord record in ps.Streams.Error) - { - errorStream.Add(record); - } - } - } - - T valueToReturn = default(T); - if (returnedvalue != null && returnedvalue.Count > 0) - { - try - { - if (returnedvalue.Count == 1) - { - if (returnedvalue[0] != null) - { - Object result = returnedvalue[0]; - Object baseObject = ((PSObject)result).BaseObject; - if (! (baseObject is PSCustomObject)) - { - result = baseObject; - } - - // Try regular PowerShell conversion - valueToReturn = LanguagePrimitives.ConvertTo( result ); - } - } - else - { - valueToReturn = LanguagePrimitives.ConvertTo(returnedvalue); - } - } - catch (PSInvalidCastException) - { - // Handle the special case of emitting a PSDataCollection - use its array constructor. - // This special case is why we aren't using PowerShell.Invoke - if (typeof(T) == typeof(PSDataCollection)) - { - Object tempValueToReturn = new PSDataCollection( - new List { LanguagePrimitives.ConvertTo(returnedvalue[0]) }); - valueToReturn = (T)tempValueToReturn; - } - else - { - throw; - } - } - - Result.Set(context, valueToReturn); - } - } - } - - private static void HandleErrorDataAdding(object sender, DataAddingEventArgs e) - { - HostSettingCommandMetadata commandMetadata; - CommandMetadataTable.TryGetValue(e.PowerShellInstanceId, out commandMetadata); - - if (commandMetadata != null) - { - PowerShellInvocation_ErrorAdding(sender, e, commandMetadata); - } - } - - private static readonly ConcurrentDictionary CommandMetadataTable = - new ConcurrentDictionary(); - - private static void PowerShellInvocation_ErrorAdding(object sender, DataAddingEventArgs e, HostSettingCommandMetadata commandMetadata) - { - ErrorRecord errorRecord = e.ItemAdded as ErrorRecord; - - if (errorRecord != null) - { - if (commandMetadata != null) - { - ScriptPosition scriptStart = new ScriptPosition( - commandMetadata.CommandName, - commandMetadata.StartLineNumber, - commandMetadata.StartColumnNumber, - null); - ScriptPosition scriptEnd = new ScriptPosition( - commandMetadata.CommandName, - commandMetadata.EndLineNumber, - commandMetadata.EndColumnNumber, - null); - ScriptExtent extent = new ScriptExtent(scriptStart, scriptEnd); - - if (errorRecord.InvocationInfo != null) - { - errorRecord.InvocationInfo.DisplayScriptPosition = extent; - } - } - } - } - - static CommandInfo _ci; - static object syncroot = new object(); - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/RemoveCimInstanceActivity.cs b/src/Microsoft.PowerShell.Activities/Activities/RemoveCimInstanceActivity.cs deleted file mode 100644 index 44e6fc1694b..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/RemoveCimInstanceActivity.cs +++ /dev/null @@ -1,120 +0,0 @@ - -using Microsoft.PowerShell.Activities; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Activity to invoke the CimCmdlets\Remove-CimInstance command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class RemoveCimInstance : GenericCimCmdletActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public RemoveCimInstance() - { - this.DisplayName = "Remove-CimInstance"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "CimCmdlets\\Remove-CimInstance"; } } - - /// - /// The .NET type implementing the cmdlet to invoke. - /// - public override System.Type TypeImplementingCmdlet { get { return typeof(Microsoft.Management.Infrastructure.CimCmdlets.RemoveCimInstanceCommand); } } - - /// - /// Provides access to the Namespace parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Namespace { get; set; } - - /// - /// Provides access to the OperationTimeoutSec parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OperationTimeoutSec { get; set; } - - /// - /// Provides access to the InputObject parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument InputObject { get; set; } - - /// - /// Provides access to the Query parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Query { get; set; } - - /// - /// Provides access to the QueryDialect parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument QueryDialect { get; set; } - - - /// - /// Script module contents for this activity`n/// - protected override string PSDefiningModule { get { return null; } } - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(Namespace.Expression != null) - { - targetCommand.AddParameter("Namespace", Namespace.Get(context)); - } - - if(OperationTimeoutSec.Expression != null) - { - targetCommand.AddParameter("OperationTimeoutSec", OperationTimeoutSec.Get(context)); - } - - if (InputObject.Expression != null) - { - targetCommand.AddParameter("InputObject", InputObject.Get(context)); - } - - if(Query.Expression != null) - { - targetCommand.AddParameter("Query", Query.Get(context)); - } - - if(QueryDialect.Expression != null) - { - targetCommand.AddParameter("QueryDialect", QueryDialect.Get(context)); - } - - if (ResourceUri != null) - { - targetCommand.AddParameter("ResourceUri", ResourceUri.Get(context)); - } - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/SetCimInstanceActivity.cs b/src/Microsoft.PowerShell.Activities/Activities/SetCimInstanceActivity.cs deleted file mode 100644 index 2e90f2ae249..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/SetCimInstanceActivity.cs +++ /dev/null @@ -1,148 +0,0 @@ - -using Microsoft.PowerShell.Activities; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace cimcmdlets.Activities -{ - /// - /// Activity to invoke the CimCmdlets\Set-CimInstance command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class SetCimInstance : GenericCimCmdletActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public SetCimInstance() - { - this.DisplayName = "Set-CimInstance"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "CimCmdlets\\Set-CimInstance"; } } - - /// - /// The .NET type implementing the cmdlet to invoke. - /// - public override System.Type TypeImplementingCmdlet { get { return typeof(Microsoft.Management.Infrastructure.CimCmdlets.SetCimInstanceCommand); } } - - /// - /// Provides access to the Namespace parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Namespace { get; set; } - - /// - /// Provides access to the OperationTimeoutSec parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OperationTimeoutSec { get; set; } - - /// - /// Provides access to the InputObject parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument InputObject { get; set; } - - /// - /// Provides access to the Query parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Query { get; set; } - - /// - /// Provides access to the QueryDialect parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument QueryDialect { get; set; } - - /// - /// Provides access to the Property parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Property { get; set; } - - /// - /// Provides access to the PassThru parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument PassThru { get; set; } - - /// - /// Module defining this command - /// Script module contents for this activity - /// - protected override string PSDefiningModule { get { return "CimCmdlets"; } } - - // Additional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(Namespace.Expression != null) - { - targetCommand.AddParameter("Namespace", Namespace.Get(context)); - } - - if(OperationTimeoutSec.Expression != null) - { - targetCommand.AddParameter("OperationTimeoutSec", OperationTimeoutSec.Get(context)); - } - - if (InputObject.Expression != null) - { - targetCommand.AddParameter("InputObject", InputObject.Get(context)); - } - - if(Query.Expression != null) - { - targetCommand.AddParameter("Query", Query.Get(context)); - } - - if(QueryDialect.Expression != null) - { - targetCommand.AddParameter("QueryDialect", QueryDialect.Get(context)); - } - - if(Property.Expression != null) - { - targetCommand.AddParameter("Property", Property.Get(context)); - } - - if(PassThru.Expression != null) - { - targetCommand.AddParameter("PassThru", PassThru.Get(context)); - } - - if (ResourceUri != null) - { - targetCommand.AddParameter("ResourceUri", ResourceUri.Get(context)); - } - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/SetHostValue.cs b/src/Microsoft.PowerShell.Activities/Activities/SetHostValue.cs deleted file mode 100644 index 42b26f9c681..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/SetHostValue.cs +++ /dev/null @@ -1,602 +0,0 @@ -using System; -using System.Reflection; -using System.Activities; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Globalization; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; -using System.Management.Automation.Remoting; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Tracing; -using System.ComponentModel; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Activity to set a host value in a Workflow. - /// - public sealed class SetPSWorkflowData : NativeActivity - { - /// - /// The variable to set, if not included in the PSWorkflowRuntimeVariable enum. - /// - [DefaultValue(null)] - public InArgument OtherVariableName - { - get; - set; - } - - /// - /// - /// - [DefaultValue(null)] - public InArgument Value - { - get; - set; - } - - /// - /// Defines the remoting behavior to use when invoking this activity. - /// - [ConnectivityCategory] - [DefaultValue(RemotingBehavior.PowerShell)] - public InArgument PSRemotingBehavior { get; set; } - - /// - /// Defines the number of retries that the activity will make to connect to a remote - /// machine when it encounters an error. The default is to not retry. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSConnectionRetryCount - { - get; - set; - } - - /// - /// Defines the delay, in seconds, between connection retry attempts. - /// The default is one second. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSConnectionRetryIntervalSec - { - get; - set; - } - - /// - /// The Input stream for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument> Input - { - get; - set; - } - - /// - /// Determines whether to connect the input stream for this activity. - /// - [InputAndOutputCategory] - [DefaultValue(false)] - public bool UseDefaultInput - { - get; - set; - } - - /// - /// The output stream from the activity - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> Result - { - get; - set; - } - - /// - /// Determines whether to append output to Result. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public bool? AppendOutput - { - get; - set; - } - - /// - /// The Error stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSError - { - get; - set; - } - - /// - /// The Progress stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSProgress - { - get; - set; - } - - /// - /// The Verbose stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSVerbose - { - get; - set; - } - - /// - /// The Debug stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSDebug - { - get; - set; - } - - /// - /// The Warning stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSWarning - { - get; - set; - } - - /// - /// The Information stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSInformation - { - get; - set; - } - - /// - /// The computer name to invoke this activity on. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSComputerName - { - get; - set; - } - - /// - /// Defines the credential to use in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCredential - { - get; - set; - } - - /// - /// Forces the activity to return non-serialized objects. Resulting objects - /// have functional methods and properties (as opposed to serialized versions - /// of them), but will not survive persistence when the Workflow crashes or is - /// persisted. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSDisableSerialization - { - get; - set; - } - - /// - /// Forces the activity to not call the persist functionality, which will be responsible for - /// persisting the workflow state onto the disk. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSPersist - { - get; - set; - } - - /// - /// Determines whether to merge error data to the output stream - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument MergeErrorToOutput - { - get; - set; - } - - /// - /// Defines the maximum amount of time, in seconds, that this activity may run. - /// The default is unlimited. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSActionRunningTimeoutSec - { - get; - set; - } - - /// - /// Defines the maximum amount of time that the workflow engine should wait for a bookmark - /// to be resumed. - /// The default is unlimited. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSBookmarkTimeoutSec - { - get; - set; - } - - /// - /// This the list of module names (or paths) that are required to run this Activity successfully. - /// The default is null. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSRequiredModules - { - get; - set; - } - - /// - /// Defines the number of retries that the activity will make when it encounters - /// an error during execution of its action. The default is to not retry. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSActionRetryCount - { - get; - set; - } - - /// - /// Defines the delay, in seconds, between action retry attempts. - /// The default is one second. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSActionRetryIntervalSec - { - get; - set; - } - - /// - /// The port to use in a remote connection attempt. The default is: - /// HTTP: 5985, HTTPS: 5986. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSPort { get; set; } - - /// - /// Determines whether to use SSL in the connection attempt. The default is false. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSUseSsl { get; set; } - - /// - /// Determines whether to allow redirection by the remote computer. The default is false. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSAllowRedirection { get; set; } - - /// - /// Defines the remote application name to connect to. The default is "wsman". - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSApplicationName { get; set; } - - /// - /// Defines the remote configuration name to connect to. The default is "Microsoft.PowerShell". - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSConfigurationName { get; set; } - - /// - /// Defines the fully-qualified remote URI to connect to. When specified, the PSComputerName, - /// PSApplicationName, PSConfigurationName, and PSPort are not used. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSConnectionUri { get; set; } - - /// - /// Defines the authentication type to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSAuthentication { get; set; } - - /// - /// Defines the certificate thumbprint to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCertificateThumbprint { get; set; } - - /// - /// Defines any session options to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSSessionOption { get; set; } - - - /// - /// Execute the logic for this activity... - /// - /// - protected override void Execute(NativeActivityContext context) - { - // Retrieve our host overrides - HostParameterDefaults hostValues = context.GetExtension(); - SetHostValuesByVariableName(context, hostValues); - SetHostValuesByProperty(context, hostValues); - } - - private void SetHostValuesByProperty(NativeActivityContext context, HostParameterDefaults hostValues) - { - Type currentType = this.GetType(); - - // Populate any of our parameters into the host defaults. - foreach (PropertyInfo field in currentType.GetProperties()) - { - // See if it's an argument - if (typeof(Argument).IsAssignableFrom(field.PropertyType)) - { - // Skip the ones that are specific to this activity - if (String.Equals("VariableToSet", field.Name, StringComparison.OrdinalIgnoreCase) || - String.Equals("OtherVariableName", field.Name, StringComparison.OrdinalIgnoreCase) || - String.Equals("Value", field.Name, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - // Handle Bookmark timeouts, but don't set them as a host default. - if (String.Equals("PSBookmarkTimeoutSec", field.Name, StringComparison.OrdinalIgnoreCase)) - { - // See if this is trying to change the bookmark timeout - if (PSBookmarkTimeoutSec.Get(context).HasValue) - { - SafelySetResumeBookmarkTimeout(TimeSpan.FromSeconds(PSBookmarkTimeoutSec.Get(context).Value)); - } - else - { - SafelySetResumeBookmarkTimeout(TimeSpan.FromSeconds(0)); - } - - continue; - } - - // Get the argument - Argument currentArgument = (Argument)field.GetValue(this, null); - if (currentArgument.Expression != null) - { - if (currentArgument.Get(context) == null) - { - hostValues.Parameters.Remove(field.Name); - } - else - { - hostValues.Parameters[field.Name] = currentArgument.Get(context); - } - } - } - } - } - - private void SetHostValuesByVariableName(NativeActivityContext context, HostParameterDefaults hostValues) - { - // Set the Command / host metadata - string variableName = null; - - if (OtherVariableName.Get(context) != null) - { - if (OtherVariableName.Expression != null) - { - string value = OtherVariableName.Get(context); - - if (!string.IsNullOrWhiteSpace(value)) - { - variableName = value; - } - } - - if (String.Equals(variableName, "Position", StringComparison.OrdinalIgnoreCase)) - { - HostSettingCommandMetadata metadata = hostValues.HostCommandMetadata; - - // The position should come in as line:column:command - string positionMessage = (string)Value.Get(context); - string[] positionElements = positionMessage.Split(new char[] { ':' }, 3); - - string line = positionElements[0].Trim(); - string column = positionElements[1].Trim(); - string commandName = positionElements[2].Trim(); - - if (!String.IsNullOrEmpty(line)) - { - metadata.StartLineNumber = Int32.Parse(line, CultureInfo.InvariantCulture); - } - - if (!String.IsNullOrEmpty(column)) - { - metadata.StartColumnNumber = Int32.Parse(line, CultureInfo.InvariantCulture); - } - - if (!String.IsNullOrEmpty(commandName)) - { - metadata.CommandName = commandName; - } - } - else - { - if (Value.Get(context) == null) - { - hostValues.Parameters.Remove(variableName); - } - else - { - hostValues.Parameters[variableName] = Value.Get(context); - } - } - } - } - - /// - /// Internal reflection call to set the ResumeBookmarkTimeout property, which - /// controls how much time Workflow allows an activity to run while a bookmark is - /// being resumed. The workflow default is 30 seconds, which can be exceeded - /// on heavily loaded systems especially on parallel workflows. - /// There is not a public property for this value, so the implementation below is - /// the one recommended by the workflow team. - /// - /// How long to wait. - private static void SafelySetResumeBookmarkTimeout(TimeSpan timeout) - { - Type activityDefaults = Type.GetType("System.Activities.ActivityDefaults, System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); - if (activityDefaults != null) - { - FieldInfo resumeBookmarkTimeout = activityDefaults.GetField("ResumeBookmarkTimeout"); - if (resumeBookmarkTimeout != null) - { - // This is an attempt to reset the workflow default. - if (timeout.TotalSeconds == 0) - { - // First see if it's been explicitly set. If so, don't reset it. - TimeSpan currentTimeout = (TimeSpan)resumeBookmarkTimeout.GetValue(null); - if (currentTimeout.TotalSeconds == 30) - { - resumeBookmarkTimeout.SetValue(null, TimeSpan.MaxValue); - } - } - else - { - // They've specified a value. Use it. - resumeBookmarkTimeout.SetValue(null, timeout); - } - } - else - { - System.Diagnostics.Debug.Fail("Could not find ResumeBookmarkTimeout property"); - } - } - else - { - System.Diagnostics.Debug.Fail("Could not find ResumeBookmarkTimeout type"); - } - } - } -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/ThrottledParallelForeach.cs b/src/Microsoft.PowerShell.Activities/Activities/ThrottledParallelForeach.cs deleted file mode 100644 index a197ca0ca85..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/ThrottledParallelForeach.cs +++ /dev/null @@ -1,130 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -//----------------------------------------------------------------------------- - -using System; -using System.Activities; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Windows.Markup; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Implements the equivalent of the ParallelForeach activity, but supports throttling - /// as well. Taken from the Workflow SDK: http://www.microsoft.com/en-us/download/details.aspx?id=21459 - /// - /// - [ContentProperty("Body")] - public sealed class ThrottledParallelForEach : NativeActivity - { - Variable hasCompleted; - Variable> valueEnumerator; - CompletionCallback onBodyComplete; - - /// - /// Creates a new instance of the ThrottledParallelForeach activity - /// - public ThrottledParallelForEach() - : base() - { - } - - /// - /// Gets or sets the actions to be invoked in parallel - /// - [Browsable(false)] - [DefaultValue(null)] - public ActivityAction Body { get; set; } - - /// - /// Gets or sets the number of activities that may be scheduled simultaneously - /// - public InArgument ThrottleLimit { get; set; } - - /// - /// Gets or sets the values to be iterated over - /// - [RequiredArgument] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument> Values { get; set; } - - /// - /// Store implementation variables - /// - /// - protected override void CacheMetadata(NativeActivityMetadata metadata) - { - // add the arguments to the argument collection - metadata.AddArgument(new RuntimeArgument("Values", typeof(IEnumerable), ArgumentDirection.In, true)); - metadata.AddArgument(new RuntimeArgument("ThrottleLimit", typeof(int), ArgumentDirection.In)); - - // initialize the hasCompleted and valueEnumerator and add it to the list of private variables - this.hasCompleted = new Variable(); - metadata.AddImplementationVariable(this.hasCompleted); - - this.valueEnumerator = new Variable>(); - metadata.AddImplementationVariable(this.valueEnumerator); - - // add the body to the delegates collection - metadata.AddDelegate(this.Body); - } - - /// - /// Invoke the activity's actions - /// - /// - protected override void Execute(NativeActivityContext context) - { - // get the list of value to iterate through - IEnumerable values = this.Values.Get(context); - if (values == null) - { - throw new ApplicationException("ParallelForEach requires a non null Values collection"); - } - - // get the enumerator - this.valueEnumerator.Set(context, values.GetEnumerator()); - - // initialize the values for creating the execution window (max and runningCount) - int max = this.ThrottleLimit.Get(context); - if (max < 1) max = int.MaxValue; - int runningCount = 0; - - // initialize the value of the completion variable - this.hasCompleted.Set(context, false); - - // cache the completion callback - onBodyComplete = new CompletionCallback(OnBodyComplete); - - // iterate while there are items available and we didn't exceed the throttle factor - while (runningCount < max && valueEnumerator.Get(context).MoveNext()) - { - // increase the running instances counter - runningCount++; - - if (this.Body != null) - { - context.ScheduleAction(this.Body, valueEnumerator.Get(context).Current, onBodyComplete); - } - } - } - - void OnBodyComplete(NativeActivityContext context, ActivityInstance completedInstance) - { - if (!this.hasCompleted.Get(context)) - { - // get the next child and schedule it! - IEnumerator enumerator = this.valueEnumerator.Get(context); - if (this.valueEnumerator.Get(context).MoveNext()) - { - context.ScheduleAction(this.Body, this.valueEnumerator.Get(context).Current, onBodyComplete); - } - } - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Activities/Activities/WmiActivities.cs b/src/Microsoft.PowerShell.Activities/Activities/WmiActivities.cs deleted file mode 100644 index bb11f097842..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/WmiActivities.cs +++ /dev/null @@ -1,242 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -#pragma warning disable 1634, 1691 - -using System; -using System.ComponentModel; -using System.ComponentModel.Design; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Activities; -using System.Management; -using System.Management.Automation; -using System.Management.Automation.Host; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Tracing; -using System.IO; -using System.Diagnostics.CodeAnalysis; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Globalization; - -namespace Microsoft.PowerShell.Activities -{ - - /// - /// Workflow activity wrapping the Get-Wmiobject cmdlet - /// - public sealed class GetWmiObject : WmiActivity - { - /// - /// Sets the default display name of the activity - /// - public GetWmiObject() - { - this.DisplayName = "Get-WmiObject"; - } - - /// - /// Specifies the name of a WMI class. When this parameter is used, the cmdlet - /// retrieves instances of the WMI class. - /// summary> - [BehaviorCategory] - [DefaultValue(null)] - [OverloadGroup("Class")] - public InArgument Class { get; set; } - - /// - /// Specifies the WMI class property or set of properties to retrieve. - /// - [BehaviorCategory] - [DefaultValue(null)] - public InArgument Property { get; set; } - - /// - /// Specifies a Where clause to use as a filter. Uses the syntax of the WMI Query Language (WQL). - /// - [BehaviorCategory] - [DefaultValue(null)] - [OverloadGroup("Class")] - public InArgument Filter { get; set; } - - /// - /// Specifies a WMI Query Language (WQL) statement to run. Event queries are not supported by this parameter. - /// - [BehaviorCategory] - [DefaultValue(null)] - [OverloadGroup("Query")] - public InArgument Query { get; set; } - - /// - /// Indicates whether the objects that are returned from WMI should contain amended - /// information. Typically, amended information is localizable information, such as object - /// and property descriptions, that is attached to the WMI object. - /// - [BehaviorCategory] - [DefaultValue(null)] - public bool Amended { get; set; } - - /// - /// Specifies whether direct access to the WMI provider is requested for the specified - /// class without any regard to its base class or to its derived classes. - /// - [BehaviorCategory] - [DefaultValue(null)] - public bool DirectRead { get; set; } - - /// - /// Execute the logic for the activity - /// - /// The native activity context to use in this activity - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell command; - command = GetWmiCommandCore(context, "Get-WmiObject"); - if (Class.Get(context) != null) - { - command.AddParameter("Class", Class.Get(context)); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Class", Class.Get(context))); - } - - if (Property.Get(context) != null) - { - command.AddParameter("Property", Property.Get(context)); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Property", Property.Get(context))); - - } - if (Filter.Get(context) != null) - { - command.AddParameter("Filter", Filter.Get(context)); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Filter", Filter.Get(context))); - - } - if (Amended) - { - command.AddParameter("Amended", Amended); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Amended", Amended)); - - } - if (DirectRead) - { - command.AddParameter("DirectRead", DirectRead); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "DirectRead", DirectRead)); - - } - if (Query.Get(context) != null) - { - command.AddParameter("Query", Query.Get(context)); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Query", Query.Get(context))); - } - - return new ActivityImplementationContext() { PowerShellInstance = command }; - } - } - - /// - /// Wraps the Invoke-WmiMethod cmdlet - /// - public sealed class InvokeWmiMethod : WmiActivity - { - /// - /// Sets the default display name of the activity - /// - public InvokeWmiMethod() - { - this.DisplayName = "Invoke-WmiMethod"; - } - - /// - /// A WMI path specification which should be of the form "Win32_Printer.DeviceID='TestPrinter'" - /// this will select instances of objects on which to call the method. - /// - [BehaviorCategory] - [DefaultValue(null)] - [OverloadGroup("path")] - public InArgument Path { get; set; } - - /// - /// The name of the WMI class to use for when static methods. - /// - [BehaviorCategory] - [DefaultValue(null)] - [OverloadGroup("class")] - public InArgument Class { get; set; } - - /// - /// THe name of the instance or static method to call - /// - [BehaviorCategory] - [DefaultValue(null)] - public InArgument Name { get; set; } - - /// - /// The collection of arguments to use when calling the method - /// - [BehaviorCategory] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument> ArgumentList { get; set; } - - /// - /// Implements the logic of this activity - /// - /// The activity context to refer to - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell command; - command = GetWmiCommandCore(context, "Invoke-WmiMethod"); - - if (!String.IsNullOrEmpty(Path.Get(context))) - { - command.AddParameter("Path", Path.Get(context)); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Path", Path.Get(context))); - - } - - if (!String.IsNullOrEmpty(Class.Get(context))) - { - command.AddParameter("Class", Class.Get(context)); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Class", Class.Get(context))); - - } - - if (!String.IsNullOrEmpty(Name.Get(context))) - { - command.AddParameter("Name", Name.Get(context)); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Name", Name.Get(context))); - } - - if (ArgumentList.Get(context) != null) - { - Collection argCollection = ArgumentList.Get(context).ReadAll(); - if (argCollection.Count > 0) - { - command.AddParameter("ArgumentList", argCollection); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Setting parameter {1} to '{2}'.", - context.ActivityInstanceId, "ArgumentList", string.Join("','", argCollection))); - } - } - - return new ActivityImplementationContext() { PowerShellInstance = command }; - } - } - -} diff --git a/src/Microsoft.PowerShell.Activities/Activities/WorkflowJobConverter.cs b/src/Microsoft.PowerShell.Activities/Activities/WorkflowJobConverter.cs deleted file mode 100644 index 72b81219daf..00000000000 --- a/src/Microsoft.PowerShell.Activities/Activities/WorkflowJobConverter.cs +++ /dev/null @@ -1,8151 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation.Runspaces; -using System.Reflection; -using System.Activities; -using System.Activities.Expressions; -using Microsoft.PowerShell.Activities; -using Microsoft.PowerShell.Activities.Internal; -using System.Activities.Statements; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Management.Automation; -using System.Collections.ObjectModel; -using System.Management.Automation.Internal; -using System.Management.Automation.Language; -using CoreRunspaces = System.Management.Automation.Runspaces; -using System.Management.Automation.Tracing; -using System.Text; -using System.Text.RegularExpressions; -using System.Linq; -using System.Xaml; -using System.Xml; -using System.Activities.XamlIntegration; -using Microsoft.PowerShell.Commands; -using Pipeline = Microsoft.PowerShell.Activities.Pipeline; -using System.Runtime.Serialization; -using System.Runtime.CompilerServices; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// Exception thrown to return from workflow similar to a function return. - /// - [SerializableAttribute] - public class WorkflowReturnException : WorkflowTerminatedException - { - /// - /// Generic constructor - /// - public WorkflowReturnException() : base() - { } - - /// - /// Initializes a new WorkflowReturnException instance with a given message string. - /// - /// Exception message - public WorkflowReturnException(string message) - : base(message) - { } - - /// - /// Initializes a new WorkflowReturnException instance with a message and inner exception. - /// - /// Exception message - /// Inner Exception - public WorkflowReturnException(string message, Exception innerException) : - base(message, innerException) - { } - - /// - /// Initializes a new WorkflowReturnException with serialization info and streaming context - /// - /// Serialization Info - /// Streaming context - protected WorkflowReturnException(SerializationInfo info, StreamingContext context) : - base(info, context) - { } - } - - internal class WorkflowInfoComparer : System.Collections.Generic.IEqualityComparer - { - #region IEqualityComparer Members - - public bool Equals(WorkflowInfo x, WorkflowInfo y) - { - return string.Equals(x.XamlDefinition, y.XamlDefinition, StringComparison.OrdinalIgnoreCase); - } - - public int GetHashCode(WorkflowInfo obj) - { - return obj.XamlDefinition.GetHashCode(); - } - - #endregion - } - - /// - /// Converts a PowerShell AST into a function that invokes the corresponding - /// script as a workflow job. - /// - public sealed class AstToWorkflowConverter : IAstToWorkflowConverter - { - private static readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// Provides the opportunity for job converters to validate the semantics of - /// the AST before compilation. This stage should be light-weight and as efficient - /// as possible. - /// - /// The PowerShell AST corresponding to the job's definition. - /// A collection of PSParseErrors corresponding to any semantic issues in the AST. - public List ValidateAst(FunctionDefinitionAst ast) - { - return AstToXamlConverter.Validate(ast); - } - - /// - /// Converts a PowerShell AST into a script block that represents - /// the workflow to run. - /// - /// The PowerShell AST corresponding to the job's definition. - /// The module that is defining this command (if any) - /// - /// A PowerShell script block that invokes an underlying job, - /// based on the definition provided by this script block. - /// - public List CompileWorkflows(ScriptBlockAst ast, PSModuleInfo definingModule) - { - ParseException parsingException = null; - - var result = CompileWorkflows(ast, definingModule, null, out parsingException, null); - - if (parsingException.Errors != null) - { - throw parsingException; - } - - return result; - } - - - /// - /// Converts a PowerShell AST into a script block that represents - /// the workflow to run. - /// - /// The PowerShell AST corresponding to the job's definition. - /// The module that is defining this command (if any) - /// Only root Workflow will be compiled - /// - /// A PowerShell script block that invokes an underlying job, - /// based on the definition provided by this script block. - /// - public List CompileWorkflows(ScriptBlockAst ast, PSModuleInfo definingModule, string rootWorkflowName) - { - ParseException parsingException = null; - - var result = CompileWorkflows(ast, definingModule, null, out parsingException, rootWorkflowName); - - if (parsingException.Errors != null) - { - throw parsingException; - } - - return result; - } - - /// - /// Converts a PowerShell AST into a script block that represents - /// the workflow to run. - /// - /// The PowerShell AST corresponding to the job's definition. - /// The module that is defining this command (if any) - /// The initial session state of a runspace. - /// parsing errors - /// - /// A PowerShell script block that invokes an underlying job, - /// based on the definition provided by this script block. - /// - public List CompileWorkflows(ScriptBlockAst ast, PSModuleInfo definingModule, InitialSessionState initialSessionState, out ParseException parsingErrors) - { - - var result = CompileWorkflows(ast, definingModule, initialSessionState, out parsingErrors, null); - - if (parsingErrors.Errors != null) - { - throw parsingErrors; - } - - return result; - } - - /// - /// Converts a PowerShell AST into a script block that represents - /// the workflow to run. - /// - /// The PowerShell AST corresponding to the job's definition. - /// The module that is defining this command (if any) - /// The initial session state of a runspace. - /// parsing errors - /// Optional, once assigned, only root Workflow will be compiled - /// - /// A PowerShell script block that invokes an underlying job, - /// based on the definition provided by this script block. - /// - public List CompileWorkflows(ScriptBlockAst ast, PSModuleInfo definingModule, InitialSessionState initialSessionState, out ParseException parsingErrors, string rootWorkflowName) - { - return CompileWorkflowsImpl(ast, definingModule, initialSessionState, null, out parsingErrors, rootWorkflowName); - } - - /// - /// Converts a PowerShell AST into a script block that represents - /// the workflow to run. - /// - /// The PowerShell AST corresponding to the job's definition. - /// The module that is defining this command (if any). - /// The initial session state of a runspace. - /// Language mode of source that is creating the workflow. - /// Optional, once assigned, only root Workflow will be compiled. - /// - /// A PowerShell script block that invokes an underlying job, - /// based on the definition provided by this script block. - /// - public List CompileWorkflows(ScriptBlockAst ast, PSModuleInfo definingModule, InitialSessionState initialSessionState, PSLanguageMode? sourceLanguageMode, out ParseException parsingErrors) - { - return CompileWorkflowsImpl(ast, definingModule, initialSessionState, sourceLanguageMode, out parsingErrors, null); - } - - /// - /// Converts a PowerShell AST into a script block that represents - /// the workflow to run. - /// - /// The PowerShell AST corresponding to the job's definition. - /// The module that is defining this command (if any) - /// The initial session state of a runspace. - /// Language mode of source that is creating the workflow. - /// parsing errors - /// Optional, once assigned, only root Workflow will be compiled - /// - /// A PowerShell script block that invokes an underlying job, - /// based on the definition provided by this script block. - /// - private List CompileWorkflowsImpl(ScriptBlockAst ast, PSModuleInfo definingModule, InitialSessionState initialSessionState, PSLanguageMode? sourceLanguageMode, out ParseException parsingErrors, string rootWorkflowName) - { - List errorList = new List(); - - if (ast == null) - { - throw new PSArgumentNullException("ast"); - } - - // if user specifies rootWorkflowName, we will check if it exists in the given ast. - if (rootWorkflowName != null) - { - var methods = ast.FindAll(a => a is FunctionDefinitionAst, true); - bool isWFNameMatch = false; - foreach (FunctionDefinitionAst method in methods) - { - if (method.Name == rootWorkflowName) - { - isWFNameMatch = true; - break; - } - } - - if (!isWFNameMatch) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.InvalidRootWorkflowName, rootWorkflowName); - throw new PSArgumentException(error); - } - } - - var dependencies = new Dictionary(); - var scope = BuildSymbolTable(ast, null, dependencies); - foreach (var scopeEntry in scope.functionDefinitions.Values) - { - AnalyzeFunctionBody(scopeEntry, scope, dependencies); - } - - // Now do a topological sort. - var outputList = new List(); - var readyList = dependencies.Values.Where(n => n.outgoingCalls.Count == 0).Select(node => node.scopeEntry).ToList(); - - while (readyList.Count > 0) - { - var entry = readyList[0]; - outputList.Add(entry); - readyList.RemoveAt(0); - - var node = dependencies[entry.functionDefinition]; - foreach (var caller in node.incomingCallers) - { - var nodeCaller = dependencies[caller.functionDefinition]; - nodeCaller.outgoingCalls.Remove(entry); - if (nodeCaller.outgoingCalls.Count == 0) - readyList.Add(nodeCaller.scopeEntry); - } - } - - if (outputList.Count != dependencies.Count) - { - // There must be a cycle. Workflows can't be recursive, so generate an error. - var error = new ParseError(ast.Extent, "RecursiveWorkflowNotSupported", ActivityResources.RecursiveWorkflowNotSupported); - errorList.Add(error); - } - - Ast parentAst = ast; - while (parentAst.Parent != null) - { - parentAst = parentAst.Parent; - } - var requirements = ((ScriptBlockAst)parentAst).ScriptRequirements; - - System.Management.Automation.PowerShell invoker; - bool useCurrentRunspace = false; - - HashSet processedActivityLibraries; - Dictionary activityMap; - var requiredAssemblies = new Collection(); - - if (requirements != null) - { - foreach (var reqAssembly in requirements.RequiredAssemblies) - requiredAssemblies.Add(reqAssembly); - } - - if (initialSessionState != null) - { - invoker = System.Management.Automation.PowerShell.Create(initialSessionState); - var scopeFromIss = GetScopeFromIss(initialSessionState, invoker, out processedActivityLibraries, out activityMap); - - // Add functionDefinitions, if any, from scopeFromIss to parent scope, so that they will be available for all FunctionDefinitionAsts - foreach(var entry in scopeFromIss.functionDefinitions) - { - scope.functionDefinitions.Add(entry.Key, entry.Value); - } - - // Add assemblies from initialSessionState to requiredAssemblies - foreach (var ssae in initialSessionState.Assemblies) - { - if (!string.IsNullOrEmpty(ssae.FileName)) - { - requiredAssemblies.Add(ssae.FileName); - } - else if (!string.IsNullOrEmpty(ssae.Name)) - { - requiredAssemblies.Add(ssae.Name); - } - } - } - else - { - - useCurrentRunspace = Runspace.CanUseDefaultRunspace; - invoker = System.Management.Automation.PowerShell.Create(useCurrentRunspace - ? RunspaceMode.CurrentRunspace - : RunspaceMode.NewRunspace); - - activityMap = AstToXamlConverter.GetActivityMap(requiredAssemblies, out processedActivityLibraries); - } - - var result = new List(); - try - { - foreach (var entry in outputList) - { - var func = entry.functionDefinition; - if (!func.IsWorkflow) - continue; - - try - { - entry.workflowInfo = CompileSingleWorkflow(entry.scope, func, scriptBlockTokenCache, definingModule, requiredAssemblies, activityMap, processedActivityLibraries, invoker, sourceLanguageMode, rootWorkflowName); - result.Add(entry.workflowInfo); - } - catch (ParseException e) - { - errorList.AddRange(e.Errors); - } - } - } - finally - { - if (!useCurrentRunspace) - { - invoker.Dispose(); - } - } - - if (errorList.Count > 0) - { - parsingErrors = new ParseException(errorList.ToArray()); - } - else - { - parsingErrors = new ParseException(); - } - - return result; - } - Dictionary scriptBlockTokenCache = new Dictionary(); - - private static WorkflowInfo CompileSingleWorkflow(Scope scope, - FunctionDefinitionAst func, - Dictionary scriptBlockTokenCache, - PSModuleInfo definingModule, - IEnumerable assemblyList, - Dictionary activityMap, - HashSet processedActivityLibraries, - System.Management.Automation.PowerShell invoker, - PSLanguageMode? sourceLanguageMode = (PSLanguageMode?)null, - string rootWorkflowName = null) - { - Dictionary parameterValidation; - WorkflowInfo[] calledWorkflows; - Dictionary referencedAssemblies; - string workflowAttributes; - - var xaml = AstToXamlConverter.Convert(func, scope, definingModule, activityMap, processedActivityLibraries, - out parameterValidation, out calledWorkflows, out referencedAssemblies, out workflowAttributes, - assemblyList, invoker); - - // This step does two major things: - // - it takes all of the dependent workflows and compiles them into in-memory dlls. - // - it synthesizes the text for user-callable powershell function from the workflow XAML definition - string modulePath = null; - if (definingModule != null) - { - modulePath = definingModule.ModuleBase; - } - else if (!String.IsNullOrEmpty(func.Extent.File)) - { - modulePath = Path.GetDirectoryName(func.Extent.File); - } - - // Get the topmost parent AST for the workflow function. - Ast parentAst = func; - while (parentAst.Parent != null) - { - parentAst = parentAst.Parent; - } - - // Pass either the workflow script file path if available or the full source otherwise. - string scriptFile = parentAst.Extent.File; - string scriptSource = string.IsNullOrEmpty(scriptFile) ? parentAst.Extent.StartScriptPosition.GetFullScript() : null; - ReadOnlyCollection attributeAstCollection = (func.Body.ParamBlock != null) ? func.Body.ParamBlock.Attributes : null; - var functionDefinition = ImportWorkflowCommand.CreateFunctionFromXaml(func.Name, xaml, - referencedAssemblies, calledWorkflows.Select(wfi => wfi.NestedXamlDefinition).ToArray(), - null, parameterValidation, modulePath, true, workflowAttributes, - scriptFile, scriptSource, rootWorkflowName, sourceLanguageMode, attributeAstCollection); - - var helpContent = func.GetHelpContent(scriptBlockTokenCache); - if (helpContent != null) - { - functionDefinition = helpContent.GetCommentBlock() + functionDefinition; - } - - var sb = ScriptBlock.Create(functionDefinition); - sb.DebuggerHidden = true; - - var defnText = func.Body.Extent.Text; - defnText = defnText.Substring(1, defnText.Length - 2); - return new WorkflowInfo(func.Name, defnText, sb, xaml, calledWorkflows, definingModule); - } - - static internal IEnumerable GetRequiredAssembliesFromInitialSessionState( - InitialSessionState initialSessionState, - System.Management.Automation.PowerShell invoker) - { - var getModuleCommand = new CmdletInfo("Get-Module", typeof(GetModuleCommand)); - invoker.Commands.Clear(); - invoker.AddCommand(getModuleCommand) - .AddParameter("ErrorAction", ActionPreference.Ignore); - var modules = invoker.Invoke(); - - var modulesToProcess = new Stack(modules); - while (modulesToProcess.Count > 0) - { - var module = modulesToProcess.Pop(); - - foreach (var assem in module.RequiredAssemblies) - { - yield return assem; - } - - foreach (var nestedModule in module.NestedModules) - { - modulesToProcess.Push(nestedModule); - } - } - - // All required assemblies have been loaded now, so we can iterate through - // the app domain and match up assemblies from iss. - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - if (assembly.IsDynamic) - { - continue; - } - - foreach (var ssae in initialSessionState.Assemblies) - { - if (!string.IsNullOrEmpty(ssae.Name)) - { - // Compare against full name and partial name - if (assembly.FullName.Equals(ssae.Name, StringComparison.OrdinalIgnoreCase) - || assembly.GetName().Name.Equals(ssae.Name, StringComparison.OrdinalIgnoreCase)) - { - yield return assembly.FullName; - continue; - } - } - if (!string.IsNullOrEmpty(ssae.FileName)) - { - if (assembly.Location.Equals(ssae.FileName, StringComparison.OrdinalIgnoreCase)) - { - yield return ssae.FileName; - continue; - } - } - } - } - } - - internal static Scope GetScopeFromIss(InitialSessionState iss, - System.Management.Automation.PowerShell invoker, - out HashSet processedActivityLibraries, - out Dictionary activityMap) - { - var scope = new Scope - { - functionDefinitions = new Dictionary(StringComparer.OrdinalIgnoreCase) - }; - - activityMap = AstToXamlConverter.GetActivityMap(GetRequiredAssembliesFromInitialSessionState(iss, invoker), out processedActivityLibraries); - - foreach (var sswe in iss.Commands.OfType()) - { - var issFn = AstToXamlConverter.GetScriptAsFunction(sswe.Name, sswe.Definition, isWorkflow: true); - var entry = new Scope.Entry - { - functionDefinition = issFn, - scope = new Scope - { - functionDefinitions = new Dictionary(StringComparer.OrdinalIgnoreCase) - }, - workflowInfo = (new AstToWorkflowConverter()).CompileWorkflow(sswe.Name, sswe.Definition, scope, processedActivityLibraries, activityMap, invoker) - }; - scope.functionDefinitions.Add(sswe.Name, entry); - } - - return scope; - } - - /// - /// Compile a single workflow from it's definition as a string. - /// - /// - /// - /// - /// - public WorkflowInfo CompileWorkflow(string name, string definition, InitialSessionState initialSessionState) - { - if (name == null) - { - throw new PSArgumentNullException("name"); - } - - if (definition == null) - { - throw new PSArgumentNullException("definition"); - } - - if (initialSessionState == null) - { - throw new PSArgumentNullException("initialSessionState"); - } - - var fnDefn = AstToXamlConverter.GetScriptAsFunction(name, definition, isWorkflow: true); - - var invoker = System.Management.Automation.PowerShell.Create(initialSessionState); - try - { - HashSet processedActivityLibraries; - Dictionary activityMap; - - var scope = GetScopeFromIss(initialSessionState, invoker, out processedActivityLibraries, out activityMap); - return CompileSingleWorkflow(scope, fnDefn, scriptBlockTokenCache, null, null, activityMap, processedActivityLibraries, invoker); - } - finally - { - invoker.Dispose(); - } - } - - internal WorkflowInfo CompileWorkflow(string name, - string definition, - Scope scope, - HashSet processedActivityLibraries, - Dictionary activityMap, - System.Management.Automation.PowerShell invoker) - { - var fnDefn = AstToXamlConverter.GetScriptAsFunction(name, definition, isWorkflow: true); - return CompileSingleWorkflow(scope, fnDefn, scriptBlockTokenCache, null, null, activityMap, processedActivityLibraries, invoker); - } - - /// - /// Returns the parameters of the activity called by the . - /// - /// The ast representing the command called - /// The parameters with their corresponding types, or null if the parameters cannot be found. - public static Dictionary GetActivityParameters(CommandAst commandAst) - { - CommandInfo command; - - Ast workflowRootAst = commandAst; - while (!(workflowRootAst is FunctionDefinitionAst)) - { - workflowRootAst = workflowRootAst.Parent; - } - var scope = new Scope - { - functionDefinitions = new Dictionary(StringComparer.OrdinalIgnoreCase) - }; - - bool useCurrentRunspace = Runspace.CanUseDefaultRunspace; - var invoker = System.Management.Automation.PowerShell.Create(useCurrentRunspace - ? RunspaceMode.CurrentRunspace - : RunspaceMode.NewRunspace); - try - { - HashSet processedActivityLibraries; - var activityMap = AstToXamlConverter.GetActivityMap(null, out processedActivityLibraries); - var converter = new AstToXamlConverter(null, (FunctionDefinitionAst)workflowRootAst, null, activityMap, processedActivityLibraries, true, scope, invoker); - - string commandName = commandAst.GetCommandName(); - AstToXamlConverter.ActivityKind activityKind = converter.ResolveActivityKindBasedOnCommandName(commandName, commandAst, out command, true); - - switch (activityKind) - { - case AstToXamlConverter.ActivityKind.InlineScript: - return AstToXamlConverter.GetAvailableProperties(typeof(InlineScript), null); - - case AstToXamlConverter.ActivityKind.Persist: - // The checkpoint-workflow activity accepts no parameters - return null; - - case AstToXamlConverter.ActivityKind.Suspend: - // The suspend-workflow activity accepts only one optional parameter, the syntax is: - // Suspend-Workflow [-Label ] - return new Dictionary() {{ "Label", typeof(string) }}; - - case AstToXamlConverter.ActivityKind.InvokeExpression: - // The real Invoke-Expression activity has the common parameters but the Language parameter is - // not available in the real activity. To encourage the expected use of Invoke-Expression, - // we'll only return -Language and -Command. - return new Dictionary() {{"Language", typeof (string)}, {"Command", typeof (string)}}; - - case AstToXamlConverter.ActivityKind.Delay: - return new Dictionary {{"Seconds", typeof (int)}, {"Milliseconds", typeof (int)}}; - - case AstToXamlConverter.ActivityKind.NewObject: - return new Dictionary {{"TypeName", typeof (string)}}; - - case AstToXamlConverter.ActivityKind.RegularCommand: - // If the command resolved to a script, the command metadata has all of it's parameters, - if ((command != null) && (command.CommandType & (CommandTypes.ExternalScript | CommandTypes.Workflow | CommandTypes.Function | CommandTypes.Filter | CommandTypes.Configuration)) != 0) - { - return null; - } - - CommandInfo resolvedCommand; - Type[] genericTypes; - - // Find the activity for this name - Type activityType = converter.ResolveActivityType(commandName, commandAst, true, out resolvedCommand, out genericTypes); - - if (activityType == null || resolvedCommand == null) - { - return null; - } - - Dictionary availableProperties = AstToXamlConverter.GetAvailableProperties(activityType, null); - Dictionary virtualProperties = AstToXamlConverter.GetVirtualProperties(activityType, null); - if (virtualProperties != null) - { - foreach (KeyValuePair virtualProperty in virtualProperties) - { - availableProperties.Add(virtualProperty.Key, virtualProperty.Value); - } - } - - return availableProperties; - } - } - finally - { - if (!useCurrentRunspace) - { - invoker.Dispose(); - } - } - - return null; - } - - #region Dependency graph - - class DependencyGraphNode - { - internal Scope.Entry scopeEntry; - internal List outgoingCalls = new List(); - internal List incomingCallers = new List(); - } - - private static void AnalyzeFunctionBody(Scope.Entry defnEntry, Scope parentScope, Dictionary dependencies) - { - var functionDefinitionAst = defnEntry.functionDefinition; - var scriptBlockAst = functionDefinitionAst.Body; - var currentNode = dependencies[functionDefinitionAst]; - var innerScope = BuildSymbolTable(scriptBlockAst, parentScope, dependencies); - defnEntry.scope = innerScope; - foreach (var commandAst in scriptBlockAst.FindAll(ast => true, searchNestedScriptBlocks: false).OfType()) - { - var commandName = commandAst.GetCommandName(); - if (!string.IsNullOrEmpty(commandName)) - { - var scopeEntry = innerScope.LookupCommand(commandName); - if (scopeEntry != null && !currentNode.outgoingCalls.Contains(scopeEntry)) - { - currentNode.outgoingCalls.Add(scopeEntry); - dependencies[scopeEntry.functionDefinition].incomingCallers.Add(defnEntry); - } - } - } - - foreach (var scopeEntry in innerScope.functionDefinitions.Values) - { - AnalyzeFunctionBody(scopeEntry, innerScope, dependencies); - } - } - - static Scope BuildSymbolTable(ScriptBlockAst scriptBlockAst, Scope parentScope, Dictionary dependencies) - { - var table = new Dictionary(StringComparer.OrdinalIgnoreCase); - var scope = new Scope { parent = parentScope, functionDefinitions = table }; - - foreach (var defn in scriptBlockAst.FindAll(ast => true, searchNestedScriptBlocks: false).OfType()) - { - if (table.ContainsKey(defn.Name)) - { - var errorMsg = String.Format(CultureInfo.InvariantCulture, ActivityResources.FunctionRedefinitionNotAllowed, defn.Name); - var error = new ParseError(defn.Extent, "FunctionRedefinitionNotAllowed", errorMsg); - throw new ParseException(new [] {error}); - } - var entry = new Scope.Entry {functionDefinition = defn}; - table.Add(defn.Name, entry); - dependencies.Add(defn, new DependencyGraphNode {scopeEntry = entry}); - } - - return scope; - } - - #endregion Dependency graph - } - - internal class Scope - { - internal class Entry - { - internal WorkflowInfo workflowInfo; - internal FunctionDefinitionAst functionDefinition; - internal Scope scope; - } - - internal Scope parent; - internal Dictionary functionDefinitions; - - internal Entry LookupCommand(string name) - { - Scope currentScope = this; - - while (currentScope != null) - { - Entry result; - if (currentScope.functionDefinitions.TryGetValue(name, out result)) - { - return result; - } - currentScope = currentScope.parent; - } - - return null; - } - } - - /// - /// Converts a PowerShell AST into the workflow XAML that represents it. - /// - public class AstToXamlConverter : ICustomAstVisitor - { - /// - /// Creates a new PowerShellXaml converter - /// - /// The name of the command being converted - /// The AST that is the root of the PowerShell script to convert - /// The module that is defining this command (if any) - /// The dictionary mapping activities to their types. - /// A hashset of activity libraries that the workflow depends on - /// Only do validation. - /// Scope chain used to resolve commands lexically - /// - internal AstToXamlConverter(string name, - FunctionDefinitionAst workflowAstRoot, - PSModuleInfo definingModule, - Dictionary activityMap, - HashSet processedActivityLibraries, - bool validateOnly, - Scope scope, - System.Management.Automation.PowerShell invoker) - { - this.name = name; - this.scriptWorkflowAstRoot = workflowAstRoot; - this.activityMap = activityMap; - this.processedActivityLibraries = processedActivityLibraries; - this.validateOnly = validateOnly; - this.definingModule = definingModule; - this.scope = scope; - this.invoker = invoker; - } - - static AstToXamlConverter() - { - PopulateActivityStaticMap(); - - List supportedCommonParameters = new List() { "Verbose", "Debug", "ErrorAction", "WarningAction", "InformationAction" }; - ignoredParameters = new List(Cmdlet.CommonParameters.Concat(Cmdlet.OptionalCommonParameters)); - ignoredParameters.RemoveAll( item => supportedCommonParameters.Contains(item, StringComparer.OrdinalIgnoreCase) ); - } - - /// - /// Any parameter validation attributes associated with this script block. - /// - internal Dictionary ParameterValidation - { - get { return parameterValidation; } - } - private Dictionary parameterValidation = new Dictionary(StringComparer.OrdinalIgnoreCase); - - private bool disableSymbolGeneration = false; - private string name = null; - private PSModuleInfo definingModule = null; - private readonly Ast scriptWorkflowAstRoot; - private int _currentIndentLevel; - private Scope scope; - private System.Management.Automation.PowerShell invoker; - - // Remember the assemblies we've processed, as these should - // correspond to the module names of commands we're processing. - // If the user ever tries to call a command from a module that we've - // processed - but that command is not found - then we generate an - // error because that activity was probably intentionally excluded. - private HashSet processedActivityLibraries; - private Dictionary activityMap; - private static HashSet staticProcessedActivityLibraries = new HashSet(StringComparer.OrdinalIgnoreCase); - private static Dictionary staticActivityMap = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // Bool to identify if the workflow uses return / exit. - // If so, we need to generate a try / catch statement to wrap this control flow. - bool hasControlFlowException = false; - - // Indicate whether to merge error stream for a specific CommandAst or CommandExpressionAst - private bool mergeErrorToOutput = false; - - // XAML elements - const string xamlHeader = @" -"; - private const string AppendOutputTemplate = @" AppendOutput = ""True"""; - private const string GenericTypesKey = @"Activity-GenericTypes"; - private const string MemberTemplate = @""; - private const string M3PKeyForThrowStatement = @"__Microsoft.PowerShell.Activities.Throw"; - - // PseudoCommands that only work in the script workflow - // Please keep in sync with the System.Management.Automation.CompletionCompleter.PseudoCommands - private const string CheckpointWorkflow = "Checkpoint-Workflow"; - private const string SuspendWorkflow = "Suspend-Workflow"; - - private int namespaceCount = 0; - private Dictionary namespaces = new Dictionary(StringComparer.OrdinalIgnoreCase); - private List namespaceDefinitions = new List(); - private List bodyElements = new List(); - private Stack scopes = new Stack(); - private Stack resultVariables = new Stack(); - private Dictionary members = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary memberDefaults = new Dictionary(StringComparer.OrdinalIgnoreCase); - private bool isVisitingPipeline = false; - private bool isVisitingIterativePipeline = false; - - /// - /// Converts a PowerShell AST into the XAML that represents it, also returning the cmdlet attribute string - /// for the workflow. - /// - /// The PowerShell AST to convert - /// The module that is defining this command (if any) - /// The list of additional assemblies to search for workflow activities. - /// Any parameter validation applied to the parameters in the provided AST. - /// Any nested workflows required by this PowerShell AST. - /// All assemblies, including provided at API or provided in workflow definition, required by this PowerShell Workflow. - /// The attribute string for the workflow if these is one. - public static string Convert(FunctionDefinitionAst ast, - PSModuleInfo definingModule, - List referencedAssemblies, - out Dictionary parameterValidation, - out WorkflowInfo[] nestedWorkflows, - out Dictionary requiredAssemblies, - out string workflowAttributes) - { - var scope = new Scope - { - functionDefinitions = new Dictionary(StringComparer.OrdinalIgnoreCase) - }; - - HashSet processedActivityLibraries; - var activityMap = AstToXamlConverter.GetActivityMap(referencedAssemblies, out processedActivityLibraries); - - bool useCurrentRunspace = Runspace.CanUseDefaultRunspace; - var invoker = System.Management.Automation.PowerShell.Create(useCurrentRunspace - ? RunspaceMode.CurrentRunspace - : RunspaceMode.NewRunspace); - - try - { - return Convert(ast, scope, definingModule, activityMap, processedActivityLibraries, out parameterValidation, out nestedWorkflows, - out requiredAssemblies, out workflowAttributes, referencedAssemblies, invoker); - } - finally - { - if (!useCurrentRunspace) - { - invoker.Dispose(); - } - } - } - - internal static FunctionDefinitionAst GetScriptAsFunction(string name, string definition, bool isWorkflow) - { - Token[] tokens; - ParseError[] errors; - var block = Parser.ParseInput( - string.Format(CultureInfo.InvariantCulture, "{0} {1} {{ {2} }}", - isWorkflow ? "workflow" : "function", name, definition), - out tokens, out errors); - if (errors.Count() > 0) - { - throw new ParseException(errors); - } - return (FunctionDefinitionAst)block.EndBlock.Statements[0]; - } - - /// - /// - /// - /// - /// - /// - /// - public static string Convert(string name, string definition, InitialSessionState initialSessionState) - { - if (name == null) - { - throw new PSArgumentNullException("name"); - } - - if (definition == null) - { - throw new PSArgumentNullException("definition"); - } - - if (initialSessionState == null) - { - throw new PSArgumentNullException("initialSessionState"); - } - - var fnDefn = GetScriptAsFunction(name, definition, true); - - var invoker = System.Management.Automation.PowerShell.Create(initialSessionState); - - try - { - HashSet processedActivityLibraries; - Dictionary activityMap; - - var scope = AstToWorkflowConverter.GetScopeFromIss(initialSessionState, invoker, out processedActivityLibraries, out activityMap); - - Dictionary parameterValidation; - WorkflowInfo[] nestedWorkflows; - Dictionary requiredAssemblies; - string workflowAttributes; - return Convert(fnDefn, scope, null, activityMap, processedActivityLibraries, out parameterValidation, out nestedWorkflows, - out requiredAssemblies, out workflowAttributes, null, invoker); - } - finally - { - invoker.Dispose(); - } - } - - internal static string Convert(FunctionDefinitionAst ast, - Scope scope, - PSModuleInfo definingModule, - Dictionary activityMap, - HashSet processedActivityLibraries, - out Dictionary parameterValidation, - out WorkflowInfo[] nestedWorkflows, - out Dictionary requiredAssemblies, - out string workflowAttributes, - IEnumerable assemblyList, - System.Management.Automation.PowerShell invoker) - { - AstToXamlConverter converter = new AstToXamlConverter(ast.Name, ast, definingModule, activityMap, processedActivityLibraries, false, scope, invoker); - - ast.Visit(converter); - parameterValidation = converter.ParameterValidation; - nestedWorkflows = converter.NestedWorkflows.ToArray(); - - requiredAssemblies = new Dictionary(StringComparer.OrdinalIgnoreCase); - if (assemblyList != null) - { - - foreach (string filePath in assemblyList) - { - - if (!string.IsNullOrEmpty(filePath)) - { - string fileName; - - // To avoid situation like "System.Management.Automation -> System.Management" - if (string.Equals(Path.GetExtension(filePath),".dll",StringComparison.OrdinalIgnoreCase)) - { - fileName = Path.GetFileNameWithoutExtension(filePath); - } - else - { - fileName = filePath; - } - - requiredAssemblies.Add(fileName, filePath); - } - } - } - - // Pop the attribute off of the stack - workflowAttributes = converter.CmdletAttributeText; - - string result = converter.ToString().Trim(); - return result; - } - - /// - /// Validates a PowerShell AST as a valid workflow. - /// - /// The PowerShell AST to convert - public static List Validate(FunctionDefinitionAst ast) - { - var scope = new Scope - { - functionDefinitions = new Dictionary(StringComparer.OrdinalIgnoreCase) - }; - - bool useCurrentRunspace = Runspace.CanUseDefaultRunspace; - var invoker = System.Management.Automation.PowerShell.Create(useCurrentRunspace - ? RunspaceMode.CurrentRunspace - : RunspaceMode.NewRunspace); - - try - { - // Guard access to private static variables. IEnumerable use is not thread safe. - lock (staticProcessedActivityLibraries) - { - var converter = new AstToXamlConverter(null, ast, null, staticActivityMap, staticProcessedActivityLibraries, true, scope, invoker); - - try - { - ast.Visit(converter); - } - catch (Exception) - { - // If we are reporting a parse error, catch all exceptions during validation - // as we probably tried to continue past a parse error. True code issues will - // be caught during final compilation. - if (converter.ParseErrors.Count == 0) - { - throw; - } - } - - return converter.ParseErrors; - } - } - finally - { - if (!useCurrentRunspace) - { - invoker.Dispose(); - } - } - } - - /// - /// Returns the XAML result of the AST compilation - /// - /// The XAML result of the AST compilation - public override string ToString() - { - StringBuilder result = new StringBuilder(); - - // Add in the initial header - string actualActivityName = string.Format(CultureInfo.InvariantCulture, "Activity_{0}", Math.Abs(scriptWorkflowAstRoot.ToString().GetHashCode())); - string formattedXamlHeader = String.Format( - CultureInfo.InvariantCulture, - xamlHeader, - actualActivityName); - result.AppendLine(formattedXamlHeader); - - if (hasControlFlowException) - { - // Add exception namespace to list so we can use the friendly name - GetFriendlyName(null, typeof(Microsoft.PowerShell.Workflow.WorkflowReturnException)); - } - - // Add in the namespaces - foreach (string namespaceDeclaration in namespaceDefinitions) - { - result.AppendLine(namespaceDeclaration); - } - - // Add in the defaults - foreach (string memberName in memberDefaults.Keys) - { - result.AppendLine(String.Format( - CultureInfo.InvariantCulture, " local:{0}.{1} = \"{2}\"", - actualActivityName, - memberName, - memberDefaults[memberName])); - } - - // Close the Activity tag - result.AppendLine(" >"); - - IndentLevel(); - - // Add the members - if (members.Count > 0) - { - result.AppendLine(GetIndentedLine("")); - - IndentLevel(); - foreach (VariableDefinition member in members.Values) - { - result.AppendLine(GetIndentedLine(member.XamlDefinition)); - } - UnindentLevel(); - - result.AppendLine(GetIndentedLine("")); - } - - // Add the wrapping try / catch to support return and exit - if (hasControlFlowException) - { - result.AppendLine(GetIndentedLine("")); - IndentLevel(); - result.AppendLine(GetIndentedLine("")); - IndentLevel(); - } - - // Add the body elements: ... - foreach (string element in bodyElements) - { - result.AppendLine(GetIndentedLine(element)); - } - - // Close up the wrapping try / catch to support return and exit - if (hasControlFlowException) - { - string friendlyTypeName = GetConvertedTypeName(typeof(Microsoft.PowerShell.Workflow.WorkflowReturnException)); - UnindentLevel(); - result.AppendLine(GetIndentedLine("")); - result.AppendLine(GetIndentedLine("")); - IndentLevel(); - result.AppendLine(GetIndentedLine(@"")); - IndentLevel(); - result.AppendLine(GetIndentedLine(@"")); - IndentLevel(); - result.AppendLine(GetIndentedLine(@"")); - IndentLevel(); - result.AppendLine(GetIndentedLine(@"")); - UnindentLevel(); - result.AppendLine(GetIndentedLine(@"")); - UnindentLevel(); - result.AppendLine(GetIndentedLine(@"")); - UnindentLevel(); - result.AppendLine(GetIndentedLine(@"")); - - UnindentLevel(); - result.AppendLine(GetIndentedLine("")); - UnindentLevel(); - result.AppendLine(GetIndentedLine("")); - } - - UnindentLevel(); - result.AppendLine(GetIndentedLine(xamlFooter)); - - return result.ToString(); - } - - /// - /// Set to True if workflow conversion should be done in validation mode. - /// - internal bool ValidateOnly - { - get { return validateOnly; } - } - private bool validateOnly = false; - - /// - /// Returns the list of errors found during validation / compilation - /// - internal List ParseErrors - { - get { return _parseErrors; } - } - List _parseErrors = new List(); - - /// - /// Returns all nested workflows used by this command - /// - internal HashSet NestedWorkflows - { - get { return nestedWorkflows; } - } - private HashSet nestedWorkflows = new HashSet(new WorkflowInfoComparer()); - - private string GetIndentedLine(string line) - { - if (_currentIndentLevel > 0) - { - StringBuilder indentation = new StringBuilder(); - indentation.Append(' ', 4 * _currentIndentLevel); - indentation.Append(line); - - line = indentation.ToString(); - } - - return line; - } - - private void WriteLine(string line) - { - bodyElements.Add(GetIndentedLine(line)); - } - - // Convert a type name to something with namespaces shortened down to XML namespaces (i.e.: ns0:Dictionary) - // Also adds the XML namespaces to the list of namespaces in the activity itself. - private string GetConvertedTypeName(Type requiredType) - { - string convertedName = GetFriendlyName(null, requiredType); - - // Process generic arguments - if (requiredType.IsGenericType) - { - convertedName += "("; - - Type[] genericArguments = requiredType.GetGenericArguments(); - string[] convertedGenericArguments = new string[genericArguments.Length]; - - for (int counter = 0; counter < genericArguments.Length; counter++) - { - convertedGenericArguments[counter] = GetConvertedTypeName(genericArguments[counter]); - } - - convertedName += String.Join(", ", convertedGenericArguments); - - convertedName += ")"; - } - - return convertedName; - } - - private string GetFriendlyName(string invocationName, Type requiredType) - { - // Generate an error if they're trying to use a parameter type / etc - // from a dynamically-loaded assembly. - if (String.IsNullOrEmpty(requiredType.Assembly.Location)) - { - throw new NotSupportedException( - String.Format(CultureInfo.InvariantCulture, ActivityResources.TypeFromDynamicAssembly, requiredType.FullName)); - } - - string typeKey = requiredType.Namespace + "|" + requiredType.Assembly.FullName; - - // Add a namespace alias if required - if (!namespaces.ContainsKey(typeKey)) - { - string namespaceName = "ns" + namespaceCount; - namespaces[typeKey] = namespaceName; - - namespaceDefinitions.Add( - String.Format(CultureInfo.InvariantCulture, @" xmlns:ns{0}=""clr-namespace:{1};assembly={2}""", - namespaceCount, requiredType.Namespace, requiredType.Module.Name.Replace(".dll", ""))); - namespaceCount++; - } - - string namespaceMapping = namespaces[typeKey]; - - if (invocationName == null) - { - invocationName = requiredType.Name; - } - - string friendlyName = GetNonGenericName(invocationName); - - if (typeof(DynamicActivity).IsAssignableFrom(requiredType)) - { - friendlyName = friendlyName.Replace("Microsoft.PowerShell.DynamicActivities.", ""); - namespaceMapping = "local"; - } - - return String.Format(CultureInfo.InvariantCulture, "{0}:{1}", namespaceMapping, friendlyName); - } - - private string GetNonGenericName(string genericName) - { - int genericIndex = genericName.IndexOf('`'); - if (genericIndex >= 0) - { - genericName = genericName.Substring(0, genericIndex); - } - return genericName; - } - - private void IndentLevel() - { - ++_currentIndentLevel; - } - - private void UnindentLevel() - { - --_currentIndentLevel; - if (_currentIndentLevel < 0) - { - throw new InvalidOperationException(); - } - } - - object ICustomAstVisitor.VisitErrorStatement(ErrorStatementAst errorStatementAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitErrorExpression(ErrorExpressionAst errorExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitScriptBlock(ScriptBlockAst scriptBlockAst) - { - ValidateScriptBlock(scriptBlockAst); - - // We've already processed a sequence - if (bodyElements.Count != 0) - { - ReportError("CannotProcessMoreThanOneScriptBlock", ActivityResources.CannotProcessMoreThanOneScriptBlock, scriptBlockAst.Extent); - } - - WriteLine(""); - IndentLevel(); - EnterScope(); - - try - { - if (scriptBlockAst.ParamBlock != null) - { - scriptBlockAst.ParamBlock.Visit(this); - } - - // Initialize the 'result' reference parameter with empty collection, without this result parameter is generating - // wrong results if it is first used in a += operation in Powershell value activity. - if(members.ContainsKey("result") && typeof(PSDataCollection).IsAssignableFrom(members["result"].Type)) - { - GeneratePowerShellValue(typeof(PSDataCollection), "@()", false, "result"); - } - - if (scriptBlockAst.EndBlock != null) - { - scriptBlockAst.EndBlock.Visit(this); - } - } - finally - { - DumpVariables("Sequence"); - LeaveScope(); - - UnindentLevel(); - WriteLine(""); - } - - return null; - } - - private void ValidateScriptBlock(ScriptBlockAst scriptBlockAst) - { - if (scriptBlockAst.DynamicParamBlock != null) - { - ReportError("DynamicParametersNotSupported", ActivityResources.DynamicParametersNotSupported, scriptBlockAst.DynamicParamBlock.Extent); - } - if (scriptBlockAst.BeginBlock != null) - { - ReportError("BeginNotSupported", ActivityResources.BeginProcessNotSupported, scriptBlockAst.BeginBlock.Extent); - } - if (scriptBlockAst.ProcessBlock != null) - { - ReportError("ProcessNotSupported", ActivityResources.BeginProcessNotSupported, scriptBlockAst.ProcessBlock.Extent); - } - } - - object ICustomAstVisitor.VisitTypeConstraint(TypeConstraintAst typeConstraintAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitAttribute(AttributeAst attributeAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitParameter(ParameterAst parameterAst) - { - Type parameterType = parameterAst.StaticType; - string parameterName = parameterAst.Name.VariablePath.ToString(); - string actualParameterName; - if (!IsValidMemberName(parameterName, out actualParameterName, true)) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.InvalidMemberName, parameterName); - ReportError("MemberNameNotValid", error, parameterAst.Extent); - } - - // If we've already seen the parameter, it's an error, but that error should have been reported by the parser. - if (members.ContainsKey(parameterName)) - { - return null; - } - - // Store any parameter validation - if ((parameterAst.Attributes.Count > 0) || - (parameterAst.DefaultValue != null)) - { - if(! parameterValidation.ContainsKey(parameterName)) - { - parameterValidation[parameterName] = parameterAst; - } - } - - // If the parameter is not typed and they've given a default - // value, then use the static type from the default. - if (parameterType == typeof(System.Object)) - { - if (parameterAst.DefaultValue != null) - { - bool constrainedToObject = false; - - // Check if it was constrained that way - if (parameterAst.Attributes.Count > 0) - { - foreach (AttributeBaseAst attribute in parameterAst.Attributes) - { - if (attribute is TypeConstraintAst) - { - constrainedToObject = true; - break; - } - } - } - - if (!constrainedToObject) - { - parameterType = parameterAst.DefaultValue.StaticType; - } - } - } - - string memberTemplate = @""; - - // If it's a reference type, we need to generate an OutArgument - if (parameterType == typeof(System.Management.Automation.PSReference)) - { - // Unfortunately, you can't type constrain these. Determine the - // type through flow analysis. (Good version TBD!) - parameterType = DetectType(parameterName, true, scriptWorkflowAstRoot); - memberTemplate = @""; - } - - // If they've specified a default value, save that so we can add it to the - // workflow. - if (parameterAst.DefaultValue != null) - { - bool areParameterAndDefaultCompatible = - (parameterAst.StaticType == typeof(object)) || - (parameterAst.StaticType == typeof(bool)) || - (parameterAst.StaticType == parameterAst.DefaultValue.StaticType); - - bool isSupportedDefaultType = - areParameterAndDefaultCompatible && - ( - parameterAst.DefaultValue.StaticType.IsPrimitive || - (parameterAst.DefaultValue.StaticType == typeof(string)) || - parameterAst.StaticType == typeof(bool) - ); - - if (!isSupportedDefaultType) - { - ReportError("OnlySimpleParameterDefaultsSupported", ActivityResources.OnlySimpleParameterDefaultsSupported, parameterAst.DefaultValue.Extent); - return null; - } - - ConstantExpressionAst parameterValue = parameterAst.DefaultValue as ConstantExpressionAst; - - if (parameterValue != null) - { - this.memberDefaults[parameterName] = EncodeStringArgument(parameterValue.Value.ToString(), false); - } - else - { - string valueText = parameterAst.DefaultValue.Extent.Text; - - // Do some hand tweaking for booleans, which actually come as variable expressions - valueText = GetEquivalentVBTextForLiteralValue(parameterAst.StaticType, valueText); - if (valueText == null) - { - ReportError("OnlySimpleParameterDefaultsSupported", ActivityResources.OnlySimpleParameterDefaultsSupported, parameterAst.DefaultValue.Extent); - return null; - } - - this.memberDefaults[parameterName] = EncodeStringArgument(valueText, false); - } - } - - string xamlDefinition = String.Format(CultureInfo.InvariantCulture, memberTemplate, parameterName, GetConvertedTypeName(parameterType)); - VariableDefinition member = new VariableDefinition() { Name = parameterName, Type = parameterType, XamlDefinition = xamlDefinition }; - members.Add(parameterName, member); - - return null; - } - - private string GetEquivalentVBTextForLiteralValue(Type argumentType, string valueText) - { - string result = null; - - if (argumentType == typeof(bool)) - { - if (String.Equals(valueText, "$true", StringComparison.OrdinalIgnoreCase) || - String.Equals(valueText, "$false", StringComparison.OrdinalIgnoreCase)) - { - result = valueText.Substring(1); - } - } - - return result; - } - - private bool IsValidMemberName(string name, out string actualVariableName, bool isParameter) - { - actualVariableName = name; - if (name == null) { return false; } - - if (!isParameter) - { - // Allow the "WORKFLOW:" scope qualifier in nested scopes - if (name.IndexOf(':') >= 0) - { - if ((name.StartsWith("WORKFLOW:", StringComparison.OrdinalIgnoreCase)) && - (scopes.Count > 1)) - { - name = name.Remove(0, "WORKFLOW:".Length); - actualVariableName = name; - } - else - { - return false; - } - } - } - - // Alphabetic to start, alphabetic plus numbers, dash, and underscore for the rest. - return Regex.IsMatch(name, "^[a-zA-Z][a-zA-Z0-9-_]*$"); - } - - private Type DetectType(string parameterName, bool isReference, Ast scriptRoot) - { - // Currently only does enough to detect simple derivation of [ref] types: - // $Variable.Value = Expression - Func assignmentSearcher = (ast) => - { - AssignmentStatementAst assignment = ast as AssignmentStatementAst; - UnaryExpressionAst unaryExpression = ast as UnaryExpressionAst; - - if ((assignment == null) && (unaryExpression == null)) - { - return false; - } - - // Check if this is a unary assignment - if (unaryExpression != null) - { - VariableExpressionAst referenceVariable = unaryExpression.Child as VariableExpressionAst; - if (referenceVariable != null && String.Equals(referenceVariable.VariablePath.UserPath, parameterName, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - // Check if this is a regular assignment - if (assignment == null) { return false; } - - if (isReference) - { - MemberExpressionAst member = assignment.Left as MemberExpressionAst; - if (member == null) { return false; } - - VariableExpressionAst variable = member.Expression as VariableExpressionAst; - if (variable == null) { return false; } - - // See if it's the variable we're looking for - // Variable.Value = - if ( - (String.Equals(variable.VariablePath.ToString(), parameterName, StringComparison.OrdinalIgnoreCase)) && - (String.Equals(member.Member.ToString(), "Value", StringComparison.OrdinalIgnoreCase)) - ) - { - CommandExpressionAst value = assignment.Right as CommandExpressionAst; - if (value == null) { return false; } - - return true; - } - } - else - { - // Capture $x = 10 - VariableExpressionAst variableExpression = assignment.Left as VariableExpressionAst; - string detectedVariableName = null; - - // Don't count PlusEquals for type detection, as we enforce that during assignment - // itself - if (assignment.Operator == TokenKind.PlusEquals) - { - return false; - } - - if (variableExpression == null) - { - // Capture [int] $x = 10 - ConvertExpressionAst convertExpression = assignment.Left as ConvertExpressionAst; - if (convertExpression != null) - { - variableExpression = convertExpression.Child as VariableExpressionAst; - } - } - - if (variableExpression != null) - { - detectedVariableName = variableExpression.VariablePath.UserPath; - } - else - { - return false; - } - - // See if it's the variable we're looking for - // Variable = - string workingVariableName = detectedVariableName; - - // Allow the "WORKFLOW:" scope qualifier in nested scopes - if (workingVariableName.StartsWith("WORKFLOW:", StringComparison.OrdinalIgnoreCase)) - { - workingVariableName = workingVariableName.Remove(0, "WORKFLOW:".Length); - } - - if (String.Equals(workingVariableName, parameterName, StringComparison.OrdinalIgnoreCase)) - { - // Ignore assignments to parallel script blocks (Variable = [parallel()] { ... }) - var value = assignment.Right as CommandExpressionAst; - if (value != null) - { - return true; - } - - // Ignore statements like foreach -parallel, parallel and sequence block - return !(assignment.Right is ForEachStatementAst) && !(assignment.Right is BlockStatementAst); - } - } - - return false; - }; - - List resultNodes = scriptRoot.FindAll(assignmentSearcher, searchNestedScriptBlocks: true).ToList(); - - // We couldn't detect the type. Assume PSDataCollection) - if (resultNodes.Count == 0) - { - return typeof(PSDataCollection); - } - - HashSet detectedTypes = new HashSet(); - foreach (Ast result in resultNodes) - { - AssignmentStatementAst assignmentStatement = result as AssignmentStatementAst; - if (assignmentStatement == null) - { - continue; - } - - ConvertExpressionAst convertExpression = assignmentStatement.Left as ConvertExpressionAst; - if (convertExpression != null) - { - Type detectedType = convertExpression.StaticType; - detectedTypes.Add(detectedType); - continue; - } - - PipelineAst invocationExpression = assignmentStatement.Right as PipelineAst; - if (invocationExpression != null) - { - if (invocationExpression.PipelineElements.Count == 1 && invocationExpression.PipelineElements[0] is CommandAst) - { - var commandAst = (CommandAst)invocationExpression.PipelineElements[0]; - string commandName = commandAst.GetCommandName(); - CommandInfo command; - - bool searchSessionState = !this.ValidateOnly; - ActivityKind activityKind = ResolveActivityKindBasedOnCommandName(commandName, commandAst, out command, searchSessionState); - - if (activityKind == ActivityKind.NewObject) - { - Dictionary parameters = GetAndResolveParameters(commandAst, true); - - if (parameters.ContainsKey("TypeName")) - { - string paramValue = parameters["TypeName"].OriginalValue.ToString(); - Type actualResultType = ResolveTypeFromParameterValue(commandAst, paramValue); - - if (actualResultType != null) - { - detectedTypes.Add(actualResultType); - } - continue; - } - } - else if ( - // For generic activities, the CommandInfo should be resolved to null - activityKind == ActivityKind.RegularCommand && command == null) - { - // Skip this expensive step if we are in parse mode - if (!this.ValidateOnly) - { - CommandInfo unusedCommandInfo; - Type[] genericTypes; - Type activityType = ResolveActivityType(commandName, commandAst, false, out unusedCommandInfo, out genericTypes); - - // By convention, the 'TResult' must be the last item in 'genericArgumentTypes' - Type[] genericArgumentTypes = null; - if (activityType != null && activityType.IsGenericType && genericTypes != null && - IsAssignableFromGenericType(typeof(Activity<>), activityType, out genericArgumentTypes)) - { - var genericTypeMap = GetGenericTypeMap(activityType, genericTypes); - var actualResultType = GetActualPropertyType(genericArgumentTypes[genericArgumentTypes.Length - 1], genericTypeMap, "Result", commandAst.Extent); - detectedTypes.Add(actualResultType); - continue; - } - } - } - } - - detectedTypes.Add(typeof(PSDataCollection)); - } - } - - // We detected an unambiguous type - if (detectedTypes.Count == 1) - { - return detectedTypes.ElementAt(0); - } - else if (detectedTypes.Contains(typeof(PSDataCollection))) - { - // When we see that a variable is storing the result of an activity call, - // we can't have the detected type be an Object, as Workflow doesn't allow that. - // So we just return PSDataCollection in that case. - return typeof(PSDataCollection); - } - else - { - // It was ambiguous, or of several types - return typeof(Object); - } - } - - internal ActivityKind ResolveActivityKindBasedOnCommandName(string commandName, CommandAst commandAst, out CommandInfo command, bool searchSessionState) - { - command = null; - - if (String.IsNullOrEmpty(commandName)) - { - return ActivityKind.RegularCommand; - } - - // Check if this is InlineScript activity - if (String.Equals(commandName, "InlineScript", StringComparison.OrdinalIgnoreCase)) - { - return ActivityKind.InlineScript; - } - // Check if this is a persist activity - else if ( - String.Equals(commandName, CheckpointWorkflow, StringComparison.OrdinalIgnoreCase) || - String.Equals(commandName, "persist", StringComparison.OrdinalIgnoreCase) - ) - { - return ActivityKind.Persist; - } - // Check if this is a suspend activity - else if (String.Equals(commandName, SuspendWorkflow, StringComparison.OrdinalIgnoreCase)) - { - return ActivityKind.Suspend; - } - // Check if this is inline XAML - else if ( - String.Equals(commandName, "Invoke-Expression", StringComparison.OrdinalIgnoreCase) || - String.Equals(commandName, "iex", StringComparison.OrdinalIgnoreCase) - ) - { - StaticBindingResult bindingResult = null; - if (commandAst != null) - { - bindingResult = StaticParameterBinder.BindCommand(commandAst, false, new string[] { "Language" }); - } - - // If they've specified the "-Language" parameter, invoke the - // built-in Invoke-Expression support for inline XAML - if ((bindingResult != null) && (bindingResult.BoundParameters.ContainsKey("Language"))) - { - return ActivityKind.InvokeExpression; - } - else - { - if (searchSessionState) - { - command = ResolveCommand(commandName); - } - - // Otherwise, use the Invoke-Expression activity - return ActivityKind.RegularCommand; - } - } - // Check if this is a delay activity - else if ( - String.Equals(commandName, "Start-Sleep", StringComparison.OrdinalIgnoreCase) || - String.Equals(commandName, "sleep", StringComparison.OrdinalIgnoreCase) - ) - { - return ActivityKind.Delay; - } - // Check if this is a New-Object activity - else if (String.Equals(commandName, "New-Object", StringComparison.OrdinalIgnoreCase)) - { - if (searchSessionState) - { - command = ResolveCommand(commandName); - } - - return ActivityKind.NewObject; - } - // This is another command name - else - { - var entry = scope.LookupCommand(commandName); - if (entry != null) - { - command = entry.workflowInfo; - } - - if (searchSessionState && command == null) - { - command = ResolveCommand(commandName); - } - - return ActivityKind.RegularCommand; - } - } - - /// - /// Block variable scope prefix like "$GLOBAL:" and "$SCRIPT:". In script workflow, - /// the only valid scope prefix is "$WORKFLOW:". When generating expression for the - /// PowerShellValue activity, we need to remove the $WORKFLOW part. Otherwise it will - /// generate error during execution, because the prefix "WORKFLOW" is not actually - /// supported in the PowerShell. - /// - /// - /// - private string GetPowerShellValueExpression(Ast expression) - { - Func variableSearcher = (ast) => - { - var variableExpr = ast as VariableExpressionAst; - if (variableExpr == null) - { - return false; - } - - string variableName = variableExpr.VariablePath.ToString(); - if (variableName.IndexOf(':') != -1 && !variableName.StartsWith("ENV:", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - return false; - }; - - List resultNodes = expression.FindAll(variableSearcher, searchNestedScriptBlocks: true).ToList(); - if (resultNodes.Count == 0) - { - return expression.Extent.Text; - } - - string valueExpression = expression.Extent.ToString(); - foreach (Ast node in resultNodes) - { - var variableNode = node as VariableExpressionAst; - if (variableNode == null) - { - continue; - } - - string variableName = variableNode.VariablePath.ToString(); - string variableSign = variableNode.Splatted ? "@" : "$"; - if (variableName.IndexOf(':') != -1) - { - if (!variableName.StartsWith("WORKFLOW:", StringComparison.OrdinalIgnoreCase)) - { - ReportError("InvalidScopePrefixInWorkflow", ActivityResources.InvalidScopePrefixInWorkflow, variableNode.Extent); - } - else - { - string actualVariableName = variableName.Remove(0, "WORKFLOW:".Length); - string oldValue = variableSign + variableName; - string newValue = variableSign + actualVariableName; - valueExpression = valueExpression.Replace(oldValue, newValue); - } - } - } - - return valueExpression; - } - - object ICustomAstVisitor.VisitTypeExpression(TypeExpressionAst typeExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) - { - // Process top-level workflow to generate the callable function, nested functions must be handled by the caller. - if (functionDefinitionAst != scriptWorkflowAstRoot) return null; - - // Generate the parameters applied to the function itself - if (functionDefinitionAst.Parameters != null) - { - foreach (ParameterAst parameter in functionDefinitionAst.Parameters) - { - parameter.Visit(this); - } - } - - // Extract the cmdlet binding attribute and save it to add to the generated function text... - bool foundCmdletBinding = false; - if (functionDefinitionAst.Body.ParamBlock != null && functionDefinitionAst.Body.ParamBlock.Attributes != null) - { - foreach (var attribute in functionDefinitionAst.Body.ParamBlock.Attributes) - { - if (typeof(CmdletBindingAttribute) == attribute.TypeName.GetReflectionAttributeType()) - { - bool error = false; - - if (attribute.PositionalArguments.Count != 0) - { - ReportError("InvalidCmdletBindingAttribute", ActivityResources.InvalidCmdletBindingAttribute, functionDefinitionAst.Extent); - error = true; - } - else - { - foreach(NamedAttributeArgumentAst namedArg in attribute.NamedArguments) - { - if(!namedArg.ArgumentName.Equals("DefaultParameterSetName", StringComparison.OrdinalIgnoreCase) && - !namedArg.ArgumentName.Equals("ConfirmImpact", StringComparison.OrdinalIgnoreCase) && - !namedArg.ArgumentName.Equals("HelpUri", StringComparison.OrdinalIgnoreCase) && - !namedArg.ArgumentName.Equals("PositionalBinding", StringComparison.OrdinalIgnoreCase)) - { - ReportError("InvalidCmdletBindingAttribute", ActivityResources.InvalidCmdletBindingAttribute, functionDefinitionAst.Extent); - error = true; - } - } - } - - if (!error) - { - foundCmdletBinding = true; - - if (String.IsNullOrEmpty(this.CmdletAttributeText)) - { - this.CmdletAttributeText = attribute.ToString(); - } - else - { - this.CmdletAttributeText += "\r\n" + attribute.ToString(); - } - } - } - - if ((typeof(OutputTypeAttribute) == attribute.TypeName.GetReflectionAttributeType()) || - (typeof(AliasAttribute) == attribute.TypeName.GetReflectionAttributeType())) - { - if (String.IsNullOrEmpty(this.CmdletAttributeText)) - { - this.CmdletAttributeText = attribute.ToString(); - } - else - { - this.CmdletAttributeText += "\r\n" + attribute.ToString(); - } - } - } - } - - if (!foundCmdletBinding) - { - if (String.IsNullOrEmpty(this.CmdletAttributeText)) - { - this.CmdletAttributeText = "[CmdletBinding()]"; - } - else - { - this.CmdletAttributeText += "\r\n[CmdletBinding()]"; - } - } - - functionDefinitionAst.Body.Visit(this); - - return null; - } - - /// - /// Used to hold the CmdletBinding attribute string specified in the script workflow text. - /// This needs to be propagated to the synthesized driver function . - /// - internal string CmdletAttributeText { get; set; } - - object ICustomAstVisitor.VisitParamBlock(ParamBlockAst paramBlockAst) - { - foreach (ParameterAst parameter in paramBlockAst.Parameters) - { - parameter.Visit(this); - } - - return null; - } - - object ICustomAstVisitor.VisitNamedBlock(NamedBlockAst namedBlockAst) - { - if (namedBlockAst.BlockKind != TokenKind.End) - { - ReportError("BeginProcessNotSupported", ActivityResources.BeginProcessNotSupported, namedBlockAst.Extent); - } - - if (namedBlockAst.Traps != null) - { - foreach(TrapStatementAst ast in namedBlockAst.Traps) - { - ast.Visit(this); - } - } - - DefineVariable("WorkflowCommandName", typeof(string), namedBlockAst.Extent, this.name); - - if (namedBlockAst.Statements != null) - { - foreach (StatementAst ast in namedBlockAst.Statements) - { - ast.Visit(this); - } - } - - return null; - } - - object ICustomAstVisitor.VisitStatementBlock(StatementBlockAst statementBlockAst) - { - // If the statement block is the body of a parallel block statement, we enclose each - // statement in a Try/Catch block, so that: - // 1. the activities generated for each statement will be executed sequentially. - // 2. the terminating exception thrown from one statement will not terminate other statements that - // are running in parallel. - var blockStatement = statementBlockAst.Parent as BlockStatementAst; - bool needToEncloseStatementInTryCatchBlock = blockStatement != null - && blockStatement.Kind.Text.Equals(TokenKind.Parallel.Text(), StringComparison.OrdinalIgnoreCase); - - foreach (StatementAst statement in statementBlockAst.Statements) - { - try - { - if (needToEncloseStatementInTryCatchBlock) - { - // Start to add the Try/Catch block - AddTryCatchForParallelStart(); - - WriteLine(""); - IndentLevel(); - } - statement.Visit(this); - } - finally - { - if (needToEncloseStatementInTryCatchBlock) - { - UnindentLevel(); - WriteLine(""); - - // Finish the Try/Catch block - AddTryCatchForParallelEnd(); - } - } - } - - return null; - } - - object ICustomAstVisitor.VisitIfStatement(IfStatementAst ifStmtAst) - { - Collection> ifClauses = new Collection>(); - foreach (Tuple ifClause in ifStmtAst.Clauses) - { - ifClauses.Add(ifClause); - } - GenerateIf(ifClauses, ifStmtAst.ElseClause); - - return null; - } - - private void GenerateIf(Collection> ifClauses, StatementBlockAst elseClause) - { - Tuple ifClause = ifClauses[0]; - ifClauses.RemoveAt(0); - - // Generate a temporary variable for the if condition - string tempVarName = GenerateUniqueVariableName("IfCondition"); - Type conditionType = DetectType(tempVarName, false, ifClause.Item1); - DefineVariable(tempVarName, conditionType, ifClause.Item1.Extent, null); - - // Generate the assignment of the the clause to the temporary variable - string conditionExpression = GetPowerShellValueExpression(ifClause.Item1); - GenerateAssignment(tempVarName, ifClause.Item1.Extent, TokenKind.Equals, ifClause.Item1, conditionExpression); - - // Note that symbols are generated from the above GenerateAssignment call. - WriteLine(""); - IndentLevel(); - - // Generate the "If" - WriteLine(""); - IndentLevel(); - - // Convert the results of the condition to a boolean via PowerShellValue - string boolFriendlyName = GetConvertedTypeName(typeof(bool)); - WriteLine(@""); - IndentLevel(); - - GeneratePowerShellValue(typeof(bool), "$" + tempVarName, false, false); - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - - // Generate the "Then" - WriteLine(""); - IndentLevel(); - WriteLine(""); - IndentLevel(); - - ifClause.Item2.Visit(this); - - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(""); - - // Generate the "Else" - if ((ifClauses.Count > 0) || (elseClause != null)) - { - WriteLine(""); - IndentLevel(); - - WriteLine(""); - IndentLevel(); - - // If we had an "ElseIf", then it's an "If" statement nested in an Else statement - if (ifClauses.Count > 0) - { - GenerateIf(ifClauses, elseClause); - } - else - { - if (elseClause != null) - { - GenerateSymbolicInformation(elseClause.Extent); - elseClause.Visit(this); - } - } - - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(""); - } - - UnindentLevel(); - WriteLine(""); - } - - object ICustomAstVisitor.VisitTrap(TrapStatementAst trapStatementAst) - { - ReportError("TrapNotSupported", ActivityResources.TrapNotSupported, trapStatementAst.Extent); - return null; - } - - object ICustomAstVisitor.VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst) - { - StatementAst right = null; - string variableName = null; - VariableExpressionAst leftExpressionAst = null; - - AttributedExpressionAst leftAttributeAst = assignmentStatementAst.Left as AttributedExpressionAst; - if (leftAttributeAst != null) - { - leftExpressionAst = leftAttributeAst.Child as VariableExpressionAst; - } - else - { - leftExpressionAst = assignmentStatementAst.Left as VariableExpressionAst; - } - - // This was neither a variable assignment, nor a strongly-typed variable - if (leftExpressionAst == null) - { - ReportError("AssignmentNotSupported", ActivityResources.AssignmentNotSupported, assignmentStatementAst.Left.Extent); - return null; - } - - // check if the left-hand itself has any side effects - string nameOfUnSupportedVariableFound = null; - if (CheckIfExpressionHasUnsupportedVariableOrHasSideEffects(null, leftExpressionAst, out nameOfUnSupportedVariableFound)) - { - if (!string.IsNullOrEmpty(nameOfUnSupportedVariableFound)) - { - string error = String.Format(CultureInfo.InvariantCulture, - ActivityResources.VariableNotSupportedInWorkflow, - nameOfUnSupportedVariableFound); - ReportError("VariableNotSupportedInWorkflow", error, leftExpressionAst.Extent); - } - } - - variableName = leftExpressionAst.VariablePath.ToString(); - right = assignmentStatementAst.Right; - - string expression = GetPowerShellValueExpression(assignmentStatementAst); - GenerateAssignment(variableName, leftExpressionAst.Extent, assignmentStatementAst.Operator, right, expression); - - return null; - } - - private void GenerateAssignment(string variableName, IScriptExtent errorExtent, TokenKind assignmentOperator, Ast value, string expression) - { - // Give a good error message specifically for environment variable names - if (variableName.StartsWith("env:", StringComparison.OrdinalIgnoreCase)) - { - ReportError("EnvironmentVariableAssignmentNotSupported", ActivityResources.EnvironmentVariableAssignmentNotSupported, errorExtent); - } - - string actualVariableName; - if (!IsValidMemberName(variableName, out actualVariableName, false)) - { - // This was an error with variable scoping - if (variableName.IndexOf(':') > 0) - { - ReportError("WorkflowScopeOnlyValidInParallelOrSequenceBlock", ActivityResources.WorkflowScopeOnlyValidInParallelOrSequenceBlock, errorExtent); - } - else - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.InvalidMemberName, variableName); - ReportError("VariableNameNotValid", error, errorExtent); - } - } - - // Give a good error message if they've used a reserved variable name - if (IsReservedVariableName(actualVariableName)) - { - Dictionary propsCanBeSet = GetAvailableProperties(typeof(SetPSWorkflowData), null); - if (propsCanBeSet.ContainsKey(actualVariableName)) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.VariableNameReserved, variableName); - ReportError("VariableNameReserved", error, errorExtent); - } - else - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.VariableNameReadOnly, variableName); - ReportError("VariableNameReadOnly", error, errorExtent); - } - } - - // Check if the value itself contains side-effects. If so, generate an error. - string nameOfUnSupportedVariableFound = null; - if (CheckIfExpressionHasUnsupportedVariableOrHasSideEffects(variableName, value, out nameOfUnSupportedVariableFound)) - { - if (string.IsNullOrEmpty(nameOfUnSupportedVariableFound)) - { - string errorTemplate = ActivityResources.CannotStoreResultsInUnsupportedElement; - ReportError("CannotStoreResultsInUnsupportedElement", - ActivityResources.CannotStoreResultsInUnsupportedElement, value.Extent); - return; - } - else - { - string error = String.Format(CultureInfo.InvariantCulture, - ActivityResources.VariableNotSupportedInWorkflow, - nameOfUnSupportedVariableFound); - ReportError("VariableNotSupportedInWorkflow", error, value.Extent); - } - } - - Type variableType = null; - if(members.ContainsKey(actualVariableName)) - { - variableType = members[actualVariableName].Type; - } - - if (variableType == null) - { - VariableDefinition variable = GetVariableDefinition(actualVariableName); - if (variable != null) - { - variableType = variable.Type; - } - } - - if (variableType == null) - { - variableType = DetectType(actualVariableName, false, scriptWorkflowAstRoot); - } - - // Create the variable if it hasn't been created - if ((!VariableDefinedInCurrentScope(variableName)) && - (!members.ContainsKey(actualVariableName))) - { - DefineVariable(variableName, variableType, errorExtent, null); - } - - // Create the variable assignment. If this is of type PSActivity, then we can - // set the variable as the result. - if (LocalVariableAlreadyExisting()) - { - string errorTemplate = ActivityResources.CannotStoreResultsInVariable; - string error = String.Format(CultureInfo.InvariantCulture, errorTemplate, variableName, resultVariables.Peek().VariableName); - - ReportError("CannotStoreResultsInVariable", error, errorExtent); - return; - } - - // Should not override a data collecting variable - if (IsDataAggregatingVariable(actualVariableName)) - { - string errorMessage = String.Format(CultureInfo.InvariantCulture, ActivityResources.CannotUseDataCollectingVariable, actualVariableName); - ReportError("CannotUseDataCollectingVariable", errorMessage, errorExtent); - } - - bool isAggregatingVariable = false; - if (assignmentOperator == TokenKind.PlusEquals) - { - isAggregatingVariable = true; - } - - // Generate debug symbol information for variable assignments. - GenerateSymbolicInformation(value.Extent); - - // Visit the right-hand side of the expression for activities - PipelineAst rightPipeline = value as PipelineAst; - if (rightPipeline != null) - { - try - { - EnterStorage(actualVariableName, isAggregatingVariable); - value.Visit(this); - } - finally - { - LeaveStorage(); - } - } - else if (value is BlockStatementAst || value is ForEachStatementAst) - { - try - { - EnterStorage(actualVariableName, true); - value.Visit(this); - } - finally - { - LeaveStorage(); - } - } - else - { - // Support simple assignment expressions, such as hashtables, ranges, etc. - // Specifically exclude subexpressions, as they are supported by InlineScript. - CommandExpressionAst rightExpression = value as CommandExpressionAst; - UnaryExpressionAst unaryExpression = value as UnaryExpressionAst; - - if (( - (rightExpression != null) && (!(rightExpression.Expression is SubExpressionAst)) && - ((rightExpression.Expression is ConvertExpressionAst) || !(rightExpression.Expression is AttributedExpressionAst)) - ) || - (unaryExpression != null) - ) - { - try - { - EnterStorage(actualVariableName, false); - - // We rewrite an assignment such as: "$x = $x + 1" to "$x = $x + 1; $x" so that - // PowerShell returns the new value after assignment. This is especially required - // for statements such as $x++, which normally have no output. - string assignmentExpression = null; - - if (assignmentOperator != TokenKind.Equals) - { - // We rewrite an assignment such as: "$x = $x + 1" to "$x = $x + 1; $x" so that - // PowerShell returns the new value after assignment. This is required - // for statements such as $x++, which normally have no output. - assignmentExpression = expression + "; ,($" + actualVariableName + ")"; - } - else - { - assignmentExpression = GetPowerShellValueExpression(rightExpression); - } - - GeneratePowerShellValue(variableType, assignmentExpression, false, true); - } - finally - { - LeaveStorage(); - } - } - else - { - ReportError("CannotStoreResultsInUnsupportedElement", ActivityResources.CannotStoreResultsInUnsupportedElement, value.Extent); - } - } - } - - private bool IsReservedVariableName(string variable) - { - PSWorkflowRuntimeVariable unused; - return Enum.TryParse(variable, true, out unused); - } - - private void DefineVariable(string name, Type variableType, IScriptExtent extent, string defaultValue) - { - VariableScope scopeToUse = scopes.Peek(); - if (name.StartsWith("WORKFLOW:", StringComparison.OrdinalIgnoreCase)) - { - scopeToUse = scopes.Last(); - name = name.Remove(0, "WORKFLOW:".Length); - } - - // Check that it's not already defined - foreach (VariableScope scope in scopes) - { - if (scope.Variables.ContainsKey(name)) - { - string errorMessage = String.Format(CultureInfo.InvariantCulture, ActivityResources.VariableAlreadyDefined, name); - ReportError("VariableAlreadyDefined", errorMessage, extent); - } - } - - string convertedTypeName = GetConvertedTypeName(variableType); - - string defaultValueTemplate = "Default = \"{0}\" "; - string defaultValueElement = String.Empty; - if (!String.IsNullOrEmpty(defaultValue)) - { - defaultValueElement = String.Format(CultureInfo.InvariantCulture, defaultValueTemplate, EncodeStringArgument(defaultValue, false)); - } - - string xamlDefinition = String.Format(CultureInfo.InvariantCulture, MemberTemplate, name, convertedTypeName, defaultValueElement); - VariableDefinition variable = new VariableDefinition() { Name = name, Type = variableType, XamlDefinition = xamlDefinition }; - - scopeToUse.Variables[name] = variable; - } - - private bool VariableDefined(string variableName) - { - if (variableName.StartsWith("WORKFLOW:", StringComparison.OrdinalIgnoreCase)) - { - variableName = variableName.Remove(0, "WORKFLOW:".Length); - } - - // Check that it's not already defined - foreach (VariableScope scope in scopes) - { - if (scope.Variables.ContainsKey(variableName)) - { - return true; - } - } - - return false; - } - - private bool VariableDefinedInCurrentScope(string variableName) - { - VariableScope scopeToCheck = scopes.Peek(); - - if (variableName.StartsWith("WORKFLOW:", StringComparison.OrdinalIgnoreCase)) - { - scopeToCheck = scopes.Last(); - variableName = variableName.Remove(0, "WORKFLOW:".Length); - } - - return scopeToCheck.Variables.ContainsKey(variableName); - } - - - private VariableDefinition GetVariableDefinition(string variableName) - { - return (from scope in scopes - where scope.Variables.ContainsKey(variableName) - select scope.Variables[variableName]).FirstOrDefault(); - } - - private void DumpVariables(string scopeType) - { - VariableScope currentScope = scopes.Peek(); - - if (currentScope.Variables.Count > 0) - { - WriteLine(String.Format(CultureInfo.InvariantCulture, "<{0}.Variables>", scopeType)); - IndentLevel(); - - foreach (VariableDefinition variable in currentScope.Variables.Values) - { - WriteLine(variable.XamlDefinition); - } - - UnindentLevel(); - WriteLine(String.Format(CultureInfo.InvariantCulture, "", scopeType)); - } - } - - private void EnterScope() - { - VariableScope newScope = new VariableScope(); - scopes.Push(newScope); - } - - private void LeaveScope() - { - scopes.Pop(); - } - - private void EnterStorage(string variable, bool isAggregatingVariable) - { - StorageVariable newStorage = new StorageVariable(variable, isAggregatingVariable); - resultVariables.Push(newStorage); - } - - private void LeaveStorage() - { - resultVariables.Pop(); - } - - - private StorageVariable GetVariableToUse() - { - if (resultVariables != null && resultVariables.Count > 0) - { - return resultVariables.Peek(); - } - - return null; - } - - private bool LocalVariableAlreadyExisting() - { - if (resultVariables != null && resultVariables.Count > 0) - { - return !resultVariables.Peek().IsAggregatingVariable; - } - return false; - } - - private bool IsDataAggregatingVariable(string actualVariableName) - { - // Check that it's not already defined - return resultVariables.Any(scope => scope.IsAggregatingVariable && scope.VariableName.Equals(actualVariableName, StringComparison.OrdinalIgnoreCase)); - } - - private string RemoveScriptBlockBraces(string expression) - { - string trimmedExpression = expression.Trim(); - - if (trimmedExpression.StartsWith("{", StringComparison.OrdinalIgnoreCase) && - trimmedExpression.EndsWith("}", StringComparison.OrdinalIgnoreCase)) - { - trimmedExpression = trimmedExpression.Remove(0, 1); - trimmedExpression = trimmedExpression.Remove(trimmedExpression.Length - 1, 1); - } - - return trimmedExpression; - } - - - private void GeneratePowerShellValue(Type outputType, string expression, bool isLiteral, string resultVariable) - { - if ((outputType == typeof(ScriptBlock)) || - (outputType == typeof(ScriptBlock[]))) - { - expression = RemoveScriptBlockBraces(expression); - } - - string convertedTypeName = GetConvertedTypeName(outputType); - string powerShellValueFriendlyName = GetFriendlyName(null, typeof(PowerShellValue)); - - bool useDefaultInput = System.Text.RegularExpressions.Regex.IsMatch(expression, "\\$input", RegexOptions.IgnoreCase); - - String valueLine = null; - - valueLine = String.Format(CultureInfo.InvariantCulture, - @"<" + powerShellValueFriendlyName + @" x:TypeArguments=""{0}"" Expression=""{1}""", - convertedTypeName, - EncodeStringNonArgument(expression, isLiteral) - ); - - if(! String.IsNullOrEmpty(resultVariable)) - { - valueLine += String.Format(CultureInfo.InvariantCulture, " Result=\"[{0}]\"", resultVariable); - } - - if (useDefaultInput) - { - valueLine += " UseDefaultInput=\"true\""; - } - - valueLine += " />"; - - WriteLine(valueLine); - } - - private void GeneratePowerShellValue(Type outputType, string expression, bool isLiteral, bool storeResults) - { - string resultVariable = null; - - if ((LocalVariableAlreadyExisting()) && (storeResults)) - { - resultVariable = resultVariables.Peek().VariableName; - } - - GeneratePowerShellValue(outputType, expression, isLiteral, resultVariable); - } - - - private bool CheckIfExpressionHasUnsupportedVariableOrHasSideEffects(string variableName, Ast root, out string nameOfUnSupportedVariableFound) - { - nameOfUnSupportedVariableFound = null; - if (root == null) { return false; } - - ExpressionHasUnsupportedVariableOrSideEffectsVisitor visitor = new ExpressionHasUnsupportedVariableOrSideEffectsVisitor(variableName); - root.Visit(visitor); - nameOfUnSupportedVariableFound = visitor.NameOfUnsupportedVariableFound; - - return visitor.ExpressionHasSideEffects; - } - - object ICustomAstVisitor.VisitPipeline(PipelineAst pipelineAst) - { - // If it's one command, generate the call alone - if (pipelineAst.PipelineElements.Count == 1) - { - pipelineAst.PipelineElements[0].Visit(this); - } - else - { - // It's a pipeline. Look for any elements that use -DisplayName or -PipelineVariable, as they become - // an inline foreach and simulate the -PipelineVariable concept in PowerShell. - // - // When used without Foreach -Sequence, - // - // A | B | C -PipelineVariable C2 | D -DisplayName D2 | E | F | G - // - // becomes - // - // foreach($C2 in A | B |C) - // { - // foreach($D2 in $C2 | D) - // { - // $D2 | E | F | G - // } - // } - // - // The pipeline variable becomes the foreach variable - // The commands up to (and including) the command with the pipeline variable - // become the foreach condition - // The commands after the command with the pipeline variable become the foreach - // body. - // - // When used with Foreach -Sequence, we create an iteration-style pipeline. - // This captures output state in hashtable elements, and restores the state during - // each iteration. - // - // A -PipelineVariable A2 | Foreach-Object -Sequence { Command1 } -PipelineVariable B1 | - // Foreach-Object -Sequence { Command2 } -PipelineVariable C1 - // - // becomes - // - // foreach($a2 in A) - // { - // $psPipelineResults = @( @{} ) - // - // $psPipelineResults = foreach($item in $psPipelineResults) - // { - // foreach($output in Command1) - // { - // $result = $item.Clone() - // $result["B1"] = $output - // $result["_"] = $output - // $result - // } - // } - // - // $psPipelineResults = foreach($item in $psPipelineResults) - // { - // foreach($output in Command2) - // { - // $B1 = $item["B1"] - // $PSItem = $item["_"] - // - // $result = $item.Clone() - // $result["C1"] = $output - // $result["_"] = $output - // $result - // } - // } - // - // foreach($item in $psPipelineResults) { $item["_"] } - // } - - List prePipelineElements = new List(); - List postPipelineElements = new List(); - List iterativePipelineElements = new List(); - - string currentPipelineVariable = null; - string nonIterativePipelineVariable = null; - List iterativePipelineVariables = new List(); - bool isIterativePipeline = false; - bool isIterativeCommand = false; - bool processedPipelineVariable = false; - - foreach (CommandBaseAst commandBase in pipelineAst.PipelineElements) - { - // If this defines a DisplayName or PipelineVariable, then we need to split this - // pipeline into two segments. - CommandAst command = commandBase as CommandAst; - isIterativeCommand = false; - - if (command != null) - { - StaticBindingResult bindingResult = StaticParameterBinder.BindCommand(command, false); - IScriptExtent errorExtent = null; - - // Get the argument for the PipelineVariable parameter - last one wins. - currentPipelineVariable = null; - string boundParameter = null; - string[] parametersToCheck = new string[] { "DisplayName", "PV", "PipelineVariable" }; - - foreach (string parameterName in parametersToCheck) - { - if (GetArgumentAndExtentForParameter(parameterName, ref currentPipelineVariable, bindingResult, ref errorExtent)) - { - // Ensure it wasn't treated like a switch statement - if (String.Equals(currentPipelineVariable, "-" + parameterName, StringComparison.OrdinalIgnoreCase)) - { - currentPipelineVariable = ""; - } - - boundParameter = parameterName; - - // DisplayName can also be used as a Activity.DisplayName in that case DisplayName param value is not treated as a PipelineVariable - if (String.Equals(parameterName, "DisplayName", StringComparison.OrdinalIgnoreCase) && - !Regex.IsMatch(currentPipelineVariable, "^[a-zA-Z][a-zA-Z0-9-_]*$")) - { - currentPipelineVariable = null; - } - } - } - - if (currentPipelineVariable != null) - { - if(String.IsNullOrEmpty(currentPipelineVariable)) - { - string errorMsg = String.Format(CultureInfo.InvariantCulture, ActivityResources.MissingValueForParameter, boundParameter); - ReportError("MissingValueForParameter", errorMsg, errorExtent); - return null; - } - - ValidateVariableName(currentPipelineVariable, errorExtent); - } - - ScriptBlockExpressionAst sequenceParameter = null; - ScriptBlockExpressionAst beginParameter = null; - ScriptBlockExpressionAst endParameter = null; - - // If this is Foreach-Object, check if it's using the "-Sequence" flag. If so, we have an iterative pipeline. - string commandName = command.GetCommandName(); - IterativeCommands iterativeCommandType = IterativeCommands.None; - if (string.Equals("Foreach-Object", commandName, StringComparison.OrdinalIgnoreCase) || - string.Equals("%", commandName, StringComparison.OrdinalIgnoreCase)) - { - if (bindingResult.BoundParameters.ContainsKey("Sequence")) - { - iterativeCommandType = IterativeCommands.ForEachSequence; - - // Detect foreach-object in the wrong pipeline position (where there are no - // pre-pipeline elements) - if (prePipelineElements.Count == 0) - { - ReportError("InvalidForeachSequencePipelinePosition", ActivityResources.InvalidForeachSequencePipelinePosition, command.Extent); - return null; - } - - isIterativeCommand = true; - isIterativePipeline = true; - - List supportedParameters = new List() { "PipelineVariable", "PV", "DisplayName", "Begin", "Sequence", "End" }; - foreach(string parameter in bindingResult.BoundParameters.Keys) - { - if(! supportedParameters.Contains(parameter, StringComparer.OrdinalIgnoreCase)) - { - ReportError("InvalidForeachSequenceParameter", ActivityResources.InvalidForeachSequenceParameter, command.Extent); - } - } - - // Get the argument for the Sequence parameter, if it exists. - sequenceParameter = bindingResult.BoundParameters["Sequence"].Value as ScriptBlockExpressionAst; - if ((sequenceParameter == null) || (sequenceParameter.ScriptBlock.EndBlock.Statements.Count == 0)) - { - string errorMsg = String.Format(CultureInfo.InvariantCulture, ActivityResources.MissingValueForParameter, "Sequence"); - ReportError("MissingValueForParameter", errorMsg, bindingResult.BoundParameters["Sequence"].Value.Extent); - return null; - } - - // Get the argument for the Begin parameter, if it exists. - if (bindingResult.BoundParameters.ContainsKey("Begin")) - { - beginParameter = bindingResult.BoundParameters["Begin"].Value as ScriptBlockExpressionAst; - if ((beginParameter == null) || (beginParameter.ScriptBlock.EndBlock.Statements.Count == 0)) - { - string errorMsg = String.Format(CultureInfo.InvariantCulture, ActivityResources.MissingValueForParameter, "Begin"); - ReportError("MissingValueForParameter", errorMsg, bindingResult.BoundParameters["Begin"].Value.Extent); - return null; - } - } - - // Get the argument for the End parameter, if it exists. - if (bindingResult.BoundParameters.ContainsKey("End")) - { - endParameter = bindingResult.BoundParameters["End"].Value as ScriptBlockExpressionAst; - if ((endParameter == null) || (endParameter.ScriptBlock.EndBlock.Statements.Count == 0)) - { - string errorMsg = String.Format(CultureInfo.InvariantCulture, ActivityResources.MissingValueForParameter, "End"); - ReportError("MissingValueForParameter", errorMsg, bindingResult.BoundParameters["End"].Value.Extent); - return null; - } - } - - // It's now an iterative pipeline, remember the nonIterativePipelineVariable as one - // that needs to be set - if ((!String.IsNullOrEmpty(nonIterativePipelineVariable)) && - (! iterativePipelineVariables.Contains(nonIterativePipelineVariable, StringComparer.OrdinalIgnoreCase))) - { - iterativePipelineVariables.Add(nonIterativePipelineVariable); - } - - } - } - else if ((string.Equals("Where-Object", commandName, StringComparison.OrdinalIgnoreCase) || - string.Equals("?", commandName, StringComparison.OrdinalIgnoreCase) || - string.Equals("where", commandName, StringComparison.OrdinalIgnoreCase)) && - (bindingResult.BoundParameters.ContainsKey("Sequence"))) - { - iterativeCommandType = IterativeCommands.WhereSequence; - - // Detect Where-Object with -Sequence in the wrong pipeline position (where there are no - // pre-pipeline elements) - if (prePipelineElements.Count == 0) - { - ReportError("InvalidWhereSequencePipelinePosition", ActivityResources.InvalidWhereSequencePipelinePosition, command.Extent); - return null; - } - - isIterativeCommand = true; - isIterativePipeline = true; - - // Check supported parameters - List supportedParameters = new List() { "PipelineVariable", "PV", "Sequence" }; - foreach (string parameter in bindingResult.BoundParameters.Keys) - { - if (!supportedParameters.Contains(parameter, StringComparer.OrdinalIgnoreCase)) - { - ReportError("InvalidWhereSequenceParameter", ActivityResources.InvalidWhereSequenceParameter, command.Extent); - } - } - - // Get Sequence parameter argument. - sequenceParameter = bindingResult.BoundParameters["Sequence"].Value as ScriptBlockExpressionAst; - if ((sequenceParameter == null) || (sequenceParameter.ScriptBlock.EndBlock.Statements.Count == 0)) - { - string errorMsg = String.Format(CultureInfo.InvariantCulture, ActivityResources.MissingValueForParameter, "Sequence"); - ReportError("MissingValueForParameter", errorMsg, bindingResult.BoundParameters["Sequence"].Value.Extent); - return null; - } - - // This is now an interactive pipeline, remember the nonIterativePipelineVariable as one that needs to be set. - if (!String.IsNullOrEmpty(nonIterativePipelineVariable) && - !iterativePipelineVariables.Contains(nonIterativePipelineVariable, StringComparer.OrdinalIgnoreCase)) - { - iterativePipelineVariables.Add(nonIterativePipelineVariable); - } - } - - // Verify that once they've started an interactive pipeline, all commands are - // Foreach-Object -Sequence or Where-Object -Sequence. - if (isIterativePipeline && (! isIterativeCommand)) - { - ReportError("EntirePipelineMustUseForeachSequence", ActivityResources.EntirePipelineMustUseForeachSequence, command.Extent); - return null; - } - - // If this is the first command with the pipelineVariable, - // it is now the pipeline variable for the first portion of - // the pipeline. - if ((currentPipelineVariable != null) && (! processedPipelineVariable)) - { - nonIterativePipelineVariable = currentPipelineVariable; - } - - // Generate the iterative pipeline element if we got one. - if (isIterativePipeline) - { - switch (iterativeCommandType) - { - case IterativeCommands.ForEachSequence: - GenerateIterativePipelineElementForForEach(iterativePipelineElements, currentPipelineVariable, iterativePipelineVariables, beginParameter, sequenceParameter, endParameter); - break; - - case IterativeCommands.WhereSequence: - GenerateIterativePipelineElementForWhere(iterativePipelineElements, currentPipelineVariable, iterativePipelineVariables, sequenceParameter); - break; - } - - if (! String.IsNullOrEmpty(currentPipelineVariable)) - { - iterativePipelineVariables.Add(currentPipelineVariable); - } - } - } - - // Otherwise, remember the non-iterative command. - // If we haven't processed the command with the pipeline variable yet, then it goes - // into the prePipeline. Otherwise, it goes into the post pipeline. - if(! isIterativePipeline) - { - if (! processedPipelineVariable) - { - prePipelineElements.Add((CommandBaseAst)commandBase.Copy()); - - if (nonIterativePipelineVariable != null) - { - processedPipelineVariable = true; - } - } - else - { - postPipelineElements.Add(commandBase); - } - } - } - - // Generate the outputter if we had an iterative pipeline - if (isIterativePipeline) - { - string outputter = "foreach($item in $PSPipelineVariableContext) { $item['_'] }"; - Token[] unusedTokens = null; - ParseError[] unusedParseErrors = null; - Ast pipelineElementAst = Parser.ParseInput(outputter, out unusedTokens, out unusedParseErrors); - - iterativePipelineElements.AddRange(((ScriptBlockAst)pipelineElementAst).EndBlock.Statements); - } - - // See if this is a generated pipeline segment used to support a pipeline variable. - // If it is, don't process any further. - bool isInGeneratedPipeline = - (pipelineAst.Parent) != null && - (pipelineAst.Parent is ForEachStatementAst) && - (pipelineAst.Parent.Parent == null); - - // If we got a pipeline variable, generate the appropriate style of pipeline. - if ((!isInGeneratedPipeline) && (isIterativePipeline || processedPipelineVariable)) - { - bool savedSymbolGeneration = this.disableSymbolGeneration; - - try - { - GenerateSymbolicInformation(pipelineAst.Extent); - this.disableSymbolGeneration = true; - - // This is an iterative pipeline - if (isIterativePipeline) - { - if (isVisitingIterativePipeline) - { - ReportError("CannotNestIterativePipeline", ActivityResources.CannotNestIterativePipeline, pipelineAst.Extent); - return null; - } - - try - { - this.isVisitingIterativePipeline = true; - GenerateIterativePipeline(prePipelineElements, iterativePipelineElements, nonIterativePipelineVariable); - } - finally - { - this.isVisitingIterativePipeline = false; - } - } - else - { - // This is a regular pipeline with a pipeline variable - GeneratePipelineVariablePipeline(pipelineAst, prePipelineElements, postPipelineElements, nonIterativePipelineVariable); - } - } - finally - { - this.disableSymbolGeneration = savedSymbolGeneration; - } - } - else - { - // We didn't get a pipeline variable at all. It's just a regular pipeline. - PipelineAst completePipeline = new PipelineAst(pipelineAst.Extent, prePipelineElements); - GeneratePipelineCall(completePipeline); - } - } - - return null; - } - - private void ValidateVariableName(string variableName, IScriptExtent errorExtent) - { - string actualVariableNameUnused; - if (!IsValidMemberName(variableName, out actualVariableNameUnused, false)) - { - // This was an error with variable scoping - if (variableName.IndexOf(':') > 0) - { - ReportError("WorkflowScopeOnlyValidInParallelOrSequenceBlock", ActivityResources.WorkflowScopeOnlyValidInParallelOrSequenceBlock, errorExtent); - } - else - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.InvalidMemberName, variableName); - ReportError("VariableNameNotValid", error, errorExtent); - } - } - } - - private void GenerateIterativePipelineElementForForEach(List iterativePipelineElements, string currentPipelineVariable, List iterativePipelineVariables, - ScriptBlockExpressionAst beginParameter, ScriptBlockExpressionAst sequenceParameter, ScriptBlockExpressionAst endParameter) - { - // Define the variable that will be used to hold the pipeline iteration count - if ((beginParameter != null) || (endParameter != null)) - { - VariableDefinition pipelineIteration = GetVariableDefinition("PSPipelineIteration"); - if (pipelineIteration == null) - { - DefineVariable("PSPipelineIteration", typeof(Int32), null, "0"); - } - - VariableDefinition pipelineCount = GetVariableDefinition("PSPipelineLength"); - if (pipelineIteration == null) - { - DefineVariable("PSPipelineLength", typeof(Int32), null, "0"); - } - } - - StringBuilder iterativePipelineElement = new StringBuilder(); - - // Set up start of foreach loop to go over all output collected so far - iterativePipelineElement.AppendLine("$PSPipelineIteration = 0"); - SetUpIterativePipelineLoop(iterativePipelineElement, iterativePipelineVariables); - - // If they specified "-Begin {}", then generate the script to invoke that code before repeated invocation of the -Sequence {} - if (beginParameter != null) - { - iterativePipelineElement.AppendLine(" if($PSPipelineIteration -eq 0)"); - iterativePipelineElement.AppendLine(" {"); - iterativePipelineElement.AppendLine(" [System.Management.Automation.PSDataCollection[PSObject]] $PSSequenceOutput = $null"); - iterativePipelineElement.AppendLine(" $PSSequenceOutput = sequence " + beginParameter.Extent.Text); - - // Handle PSSequenceOutput output - HandlePSSequenceOutput(iterativePipelineElement, currentPipelineVariable); - - iterativePipelineElement.AppendLine(" }"); - } - - // Now take all the output from invoking the -Sequence {} portion, and create new context frames to hold - // the output. These get stored in PSPipelineVariableContext by the foreach loop above. - iterativePipelineElement.AppendLine(" [System.Management.Automation.PSDataCollection[PSObject]] $PSSequenceOutput = $null"); - iterativePipelineElement.AppendLine(" $PSSequenceOutput = sequence " + sequenceParameter.Extent.Text); - - // Handle PSSequenceOutput output - HandlePSSequenceOutput(iterativePipelineElement, currentPipelineVariable); - - // If they specified "-End {}", then generate the script to invoke that code after repeated invocation of the -Sequence {} - if (endParameter != null) - { - iterativePipelineElement.AppendLine(" if($PSPipelineIteration -eq ($PSPipelineLength- 1))"); - iterativePipelineElement.AppendLine(" {"); - iterativePipelineElement.AppendLine(" [System.Management.Automation.PSDataCollection[PSObject]] $PSSequenceOutput = $null"); - iterativePipelineElement.AppendLine(" $PSSequenceOutput = sequence " + endParameter.Extent.Text); - - // Handle PSSequenceOutput output - HandlePSSequenceOutput(iterativePipelineElement, currentPipelineVariable); - - iterativePipelineElement.AppendLine(" }"); - } - - // Finish iterative pipeline loop. - FinishIterativePipelineLoop(iterativePipelineElement, iterativePipelineVariables, true); - - // Parse the iterative pipeline element and update the pipeline elements list. - ParseAndUpdatePipelineElements(iterativePipelineElement, iterativePipelineElements); - } - - private void GenerateIterativePipelineElementForWhere(List iterativePipelineElements, string currentPipelineVariable, List iterativePipelineVariables, - ScriptBlockExpressionAst sequenceParameter) - { - StringBuilder iterativePipelineElement = new StringBuilder(); - - // Set up start of foreach loop to go over all output collected so far - SetUpIterativePipelineLoop(iterativePipelineElement, iterativePipelineVariables); - - // Generate output with the Where-Object command and provided sequence script block. - iterativePipelineElement.AppendLine(" [System.Management.Automation.PSDataCollection[PSObject]] $PSSequenceOutput = $null"); - iterativePipelineElement.AppendLine(" $PSSequenceOutput = Where-Object -InputObject $PSItem -FilterScript " + sequenceParameter.Extent.Text); - - // Handle PSSequenceOutput output - HandlePSSequenceOutput(iterativePipelineElement, currentPipelineVariable); - - // Finish the iterative pipeline loop. - FinishIterativePipelineLoop(iterativePipelineElement, iterativePipelineVariables, false); - - // Parse the iterative pipeline element and update the pipeline elements list. - ParseAndUpdatePipelineElements(iterativePipelineElement, iterativePipelineElements); - } - - private void SetUpIterativePipelineLoop(StringBuilder iterativePipelineElement, List iterativePipelineVariables) - { - // Use a foreach loop to go over all output collected so far - iterativePipelineElement.AppendLine("$PSPipelineLength = @($PSPipelineVariableContext).Count"); - iterativePipelineElement.AppendLine("$PSPipelineVariableContext = foreach($PSPipelineItem in $PSPipelineVariableContext)"); - iterativePipelineElement.AppendLine("{"); - iterativePipelineElement.AppendLine(" $PSItem = $PSPipelineItem['_']"); - - // Create virtual variables that represent the variables used in the iterative pipeline, with - // their values taken from the context frame (PSPipelineItem). - foreach (string previousPipelineVariable in iterativePipelineVariables) - { - iterativePipelineElement.AppendLine(" $" + previousPipelineVariable + " = $PSPipelineItem['" + previousPipelineVariable + "']"); - } - } - - private void HandlePSSequenceOutput(StringBuilder iterativePipelineElement, string currentPipelineVariable) - { - // Take all the output from invoking -Sequence {} portion and create new context frames to hold - // the output. These are stored in PSPipelineVariableContext by the foreach loop above. - iterativePipelineElement.AppendLine(" foreach($output in $PSSequenceOutput)"); - iterativePipelineElement.AppendLine(" {"); - iterativePipelineElement.AppendLine(" PowerShellValue[Object] -Expression '"); - iterativePipelineElement.AppendLine(" $PSPipelineItem = $PSPipelineItem.Clone();"); - iterativePipelineElement.AppendLine(" $PSPipelineItem[\"_\"] = $output;"); - iterativePipelineElement.AppendLine(" $PSPipelineItem[\"" + currentPipelineVariable + "\"] = $output;"); - iterativePipelineElement.AppendLine(" $PSPipelineItem'"); - iterativePipelineElement.AppendLine(" }"); - } - - private void FinishIterativePipelineLoop(StringBuilder iterativePipelineElement, List iterativePipelineVariables, bool incrementIterationCount) - { - // Clean out the iterative pipeline variables so that they don't have values when the iteration - // completes - foreach (string previousPipelineVariable in iterativePipelineVariables) - { - iterativePipelineElement.AppendLine(" $" + previousPipelineVariable + " = $null"); - } - - // Finish script. - if (incrementIterationCount) - { - iterativePipelineElement.AppendLine(" $PSPipelineIteration = $PSPipelineIteration + 1"); - } - iterativePipelineElement.AppendLine("}"); - } - - private void ParseAndUpdatePipelineElements(StringBuilder iterativePipelineElement, List iterativePipelineElements) - { - // We can't use the parse errors, since the parser thinks we are using workflow constructs - // ("sequence") outside of a workflow context. We're only injecting validated pipeline variable names - // and already-parsed text, so that is OK. - Token[] unusedTokens = null; - ParseError[] unusedParseErrors = null; - Ast pipelineElementAst = Parser.ParseInput(iterativePipelineElement.ToString(), out unusedTokens, out unusedParseErrors); - - iterativePipelineElements.AddRange((((ScriptBlockAst)pipelineElementAst).EndBlock.Statements)); - } - - private static bool GetArgumentAndExtentForParameter(string key, ref string pipelineVariable, - StaticBindingResult parameters, ref IScriptExtent errorExtent) - { - if (parameters.BoundParameters.ContainsKey(key)) - { - ParameterBindingResult argument = parameters.BoundParameters[key]; - string stringConstantValue = argument.ConstantValue as string; - - if (stringConstantValue != null) - { - pipelineVariable = stringConstantValue; - } - else - { - pipelineVariable = argument.Value.Extent.Text; - } - - errorExtent = argument.Value.Extent; - return true; - } - - return false; - } - - private void GenerateIterativePipeline(List prePipelineElements, List bodyElements, string pipelineVariable) - { - // Define the context variable that will be used to hold context frames - VariableDefinition pipelineVariableContext = GetVariableDefinition("PSPipelineVariableContext"); - if (pipelineVariableContext == null) - { - DefineVariable("PSPipelineVariableContext", typeof(PSDataCollection), null, null); - } - - // Generate the pipeline initializer - string conditionExtent = ""; - foreach (var element in prePipelineElements) - { - if (!String.IsNullOrEmpty(conditionExtent)) - { - conditionExtent += " | "; - } - - conditionExtent += element.Extent.Text; - } - - StringBuilder iterativePipelineElement = new StringBuilder(); - iterativePipelineElement.AppendLine("[System.Management.Automation.PSDataCollection[PSObject]] $PSPipelineVariableContext = @( @{} )"); - - iterativePipelineElement.AppendLine("[System.Management.Automation.PSDataCollection[PSObject]] $PSSequenceOutput = $null"); - iterativePipelineElement.AppendLine("$PSSequenceOutput = " + conditionExtent); - iterativePipelineElement.AppendLine("$PSPipelineVariableContext = foreach($output in $PSSequenceOutput)"); - iterativePipelineElement.AppendLine("{"); - iterativePipelineElement.AppendLine(" PowerShellValue[Object] -Expression '"); - iterativePipelineElement.AppendLine(" $PSPipelineItem = @{};"); - iterativePipelineElement.AppendLine(" $PSPipelineItem[\"_\"] = $output;"); - iterativePipelineElement.AppendLine(" $PSPipelineItem[\"" + pipelineVariable + "\"] = $output;"); - iterativePipelineElement.AppendLine(" $PSPipelineItem'"); - iterativePipelineElement.AppendLine("}"); - - // We can't use the parse errors, since the parser thinks we are using workflow constructs - // ("sequence") outside of a workflow context. We're only injecting validated pipeline variable names - // and already-parsed text, so that is OK. - Token[] unusedTokens = null; - ParseError[] unusedParseErrors = null; - ScriptBlockAst pipelineElementAst = (ScriptBlockAst) Parser.ParseInput(iterativePipelineElement.ToString(), out unusedTokens, out unusedParseErrors); - - foreach (StatementAst statement in pipelineElementAst.EndBlock.Statements) - { - statement.Visit(this); - } - - foreach (Ast bodyElement in bodyElements) - { - bodyElement.Visit(this); - } - } - - private void GeneratePipelineVariablePipeline(PipelineAst pipelineAst, List prePipelineElements, List bodyElements, string pipelineVariable) - { - // If there's a storage variable, it now needs to become an aggregating variable - var storageVariable = GetVariableToUse(); - if (storageVariable != null) - { - storageVariable.IsAggregatingVariable = true; - } - - Token[] unusedTokens = null; - ParseError[] unusedParseErrors = null; - - // Generate the extent for the variable in the foreach - string extentText = "$" + pipelineVariable; - Ast newAst = Parser.ParseInput(extentText, out unusedTokens, out unusedParseErrors); - IScriptExtent variableExtent = newAst.Extent; - - newAst = Parser.ParseInput(extentText, out unusedTokens, out unusedParseErrors); - IScriptExtent foreachBodyExtent = newAst.Extent; - - List foreachBody = new List(); - foreach (StatementAst bodyElement in bodyElements) - { - foreachBody.Add((StatementAst) bodyElement.Copy()); - } - - // Generate the AST and extent for the foreach condition - string conditionExtent = ""; - foreach (var element in prePipelineElements) - { - if (!String.IsNullOrEmpty(conditionExtent)) - { - conditionExtent += " | "; - } - - conditionExtent += element.Extent.Text; - } - newAst = Parser.ParseInput(conditionExtent, out unusedTokens, out unusedParseErrors); - IScriptExtent foreachConditionExtent = newAst.Extent; - - // Generate the actual foreach AST, and visit it. - ForEachStatementAst foreachStatement = new ForEachStatementAst(pipelineAst.Extent, null, ForEachFlags.None, - new VariableExpressionAst(variableExtent, pipelineVariable, false), - new PipelineAst(foreachConditionExtent, prePipelineElements), - new StatementBlockAst(foreachBodyExtent, foreachBody, null)); - foreachStatement.Visit(this); - } - - private void GeneratePipelineVariablePipeline(PipelineAst pipelineAst, List prePipelineElements, List postPipelineElements, string pipelineVariable) - { - Token[] unusedTokens = null; - ParseError[] unusedParseErrors = null; - - // Generate the extent for the variable in the foreach - string extentText = "$" + pipelineVariable; - Ast newAst = Parser.ParseInput(extentText, out unusedTokens, out unusedParseErrors); - IScriptExtent variableExtent = newAst.Extent; - - // Generate the AST and extent for the foreach body - VariableExpressionAst expressionAst = new VariableExpressionAst(variableExtent, pipelineVariable, false); - CommandExpressionAst commandExpression = new CommandExpressionAst(variableExtent, expressionAst, null); - - foreach (var element in postPipelineElements) - { - extentText += " | " + element.Extent.Text; - } - - postPipelineElements.Insert(0, commandExpression); - - newAst = Parser.ParseInput(extentText, out unusedTokens, out unusedParseErrors); - IScriptExtent foreachBodyExtent = newAst.Extent; - - List bodyStatements = ((ScriptBlockAst)newAst).EndBlock.Statements.ToList(); - GeneratePipelineVariablePipeline(pipelineAst, prePipelineElements, bodyStatements, pipelineVariable); - } - - private void GeneratePipelineCall(PipelineAst pipelineAst) - { - GenerateSymbolicInformation(pipelineAst.Extent); - - // Create an assembly name reference - string friendlyName = GetFriendlyName(null, typeof(Pipeline)); - string typeArguments = GetConvertedTypeName(typeof(PSDataCollection)); - - // Generate the call to the activity - var currentVariable = GetVariableToUse(); - string variableToUse = null; - bool isAggregatingVariable = false; - if (currentVariable != null) - { - variableToUse = currentVariable.VariableName; - isAggregatingVariable = currentVariable.IsAggregatingVariable; - } - - if (string.IsNullOrEmpty(variableToUse)) - { - WriteLine("<" + friendlyName + ">"); - } - else - { - string appendOutput = isAggregatingVariable ? AppendOutputTemplate : string.Empty; - string pipelineCall = String.Format(CultureInfo.InvariantCulture, "<{0} Result=\"[{1}]\"{2}>", friendlyName, variableToUse, appendOutput); - WriteLine(pipelineCall); - } - - IndentLevel(); - WriteLine("<" + friendlyName + ".Activities>"); - IndentLevel(); - - bool savedSymbolGeneration = this.disableSymbolGeneration; - Stack savedStorage = this.resultVariables; - try - { - this.disableSymbolGeneration = true; - this.resultVariables = null; - this.isVisitingPipeline = true; - - foreach (CommandBaseAst pipelineElement in pipelineAst.PipelineElements) - { - pipelineElement.Visit(this); - } - } - finally - { - this.isVisitingPipeline = false; - this.disableSymbolGeneration = savedSymbolGeneration; - this.resultVariables = savedStorage; - } - - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(""); - } - - object ICustomAstVisitor.VisitFileRedirection(FileRedirectionAst fileRedirectionAst) - { - ReportError("OnlySupportErrorStreamRedirection", ActivityResources.OnlySupportErrorStreamRedirection, fileRedirectionAst.Extent); - return null; - } - - object ICustomAstVisitor.VisitMergingRedirection(MergingRedirectionAst mergingRedirectionAst) - { - if (mergingRedirectionAst.FromStream != RedirectionStream.Error || - mergingRedirectionAst.ToStream != RedirectionStream.Output) - { - ReportError("OnlySupportErrorStreamRedirection", ActivityResources.OnlySupportErrorStreamRedirection, mergingRedirectionAst.Extent); - } - - return null; - } - - object ICustomAstVisitor.VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitUnaryExpression(UnaryExpressionAst unaryExpressionAst) - { - VariableExpressionAst referenceVariable = unaryExpressionAst.Child as VariableExpressionAst; - string expression = GetPowerShellValueExpression(unaryExpressionAst); - - switch (unaryExpressionAst.TokenKind) - { - case TokenKind.PlusPlus: - case TokenKind.MinusMinus: - case TokenKind.PostfixPlusPlus: - case TokenKind.PostfixMinusMinus: - if (referenceVariable != null) - { - string variableName = referenceVariable.VariablePath.ToString(); - GenerateAssignment(variableName, unaryExpressionAst.Extent, unaryExpressionAst.TokenKind, unaryExpressionAst, expression); - } - else - { - ReportError("OperatorRequiresVariable", ActivityResources.OperatorRequiresVariable, unaryExpressionAst.Extent); - } - - break; - case TokenKind.Minus: - case TokenKind.Plus: - case TokenKind.Not: - case TokenKind.Bnot: - case TokenKind.Exclaim: - case TokenKind.Comma: - case TokenKind.Csplit: - case TokenKind.Isplit: - case TokenKind.Join: - var currentVariable = GetVariableToUse(); - string variableToUse = null; - bool isAggregatingVariable = false; - if (currentVariable != null) - { - variableToUse = currentVariable.VariableName; - isAggregatingVariable = currentVariable.IsAggregatingVariable; - } - - string writeOutputFriendlyName = GetFriendlyName(null, typeof(Microsoft.PowerShell.Utility.Activities.WriteOutput)); - bool needWriteOutput = isAggregatingVariable || String.IsNullOrEmpty(variableToUse); - Type variableType = null; - - if (needWriteOutput) - { - variableType = typeof(PSObject[]); - GenerateWriteOutputStart(writeOutputFriendlyName, variableToUse, unaryExpressionAst.Extent); - } - else - { - variableType = GetVariableDefinition(variableToUse).Type; - } - - // Evaluate the expression - GeneratePowerShellValue(variableType, expression, false, true); - - if (needWriteOutput) - { - GenerateWriteOutputEnd(writeOutputFriendlyName); - } - - break; - default: - throw new NotSupportedException(); - } - - return null; - } - - object ICustomAstVisitor.VisitConvertExpression(ConvertExpressionAst convertExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitConstantExpression(ConstantExpressionAst constantExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitStringConstantExpression(StringConstantExpressionAst stringConstantExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitSubExpression(SubExpressionAst subExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitUsingExpression(UsingExpressionAst usingExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitCommand(CommandAst commandAst) - { - if (commandAst.InvocationOperator != TokenKind.Unknown) - { - ReportError("AlternateInvocationNotSupported", ActivityResources.AlternateInvocationNotSupported, commandAst.Extent); - } - - if (commandAst.Redirections.Count > 0) - { - foreach (RedirectionAst redirection in commandAst.Redirections) - { - redirection.Visit(this); - } - - this.mergeErrorToOutput = true; - } - - try - { - string commandName = commandAst.GetCommandName(); - CommandInfo command = null; - - bool searchSessionState = !this.ValidateOnly; - ActivityKind activityKind = ResolveActivityKindBasedOnCommandName(commandName, commandAst, out command, searchSessionState); - - switch (activityKind) - { - case ActivityKind.InlineScript: - ProcessInlineScriptAst(commandAst); - return null; - - case ActivityKind.Persist: - GeneratePersist(commandAst); - return null; - - case ActivityKind.Suspend: - GenerateSuspend(commandAst); - return null; - - case ActivityKind.InvokeExpression: - GenerateInvokeExpression(commandAst); - return null; - - case ActivityKind.Delay: - if (this.mergeErrorToOutput) - { - ReportError("CannotRedirectErrorStreamForStartSleep", - ActivityResources.CannotRedirectErrorStreamForStartSleep, - commandAst.Redirections[0].Extent); - } - - GenerateStartSleep(commandAst); - return null; - - case ActivityKind.NewObject: - if (this.mergeErrorToOutput) - { - ReportError("CannotRedirectErrorStreamForNewObject", - ActivityResources.CannotRedirectErrorStreamForNewObject, - commandAst.Redirections[0].Extent); - } - - if (GetVariableToUse() == null) - { - ReportError("NewObjectMustBeAssigned", - ActivityResources.NewObjectMustBeAssigned, - commandAst.Extent); - } - - GenerateNewObject(commandAst); - return null; - - case ActivityKind.RegularCommand: - - // If the command resolved to a workflow, generate that as a nested workflow - if ((command != null) && (command.CommandType == CommandTypes.Workflow)) - { - WorkflowInfo nestedWorkflow = (WorkflowInfo)command; - CreateNestedWorkflow(nestedWorkflow, commandAst, commandAst.Extent); - } - - GenerateCommandCall(commandAst, command); - return null; - - default: - // Should not get to default - break; - } - } - finally - { - this.mergeErrorToOutput = false; - } - - throw new NotSupportedException(); - } - - private void GenerateCommandCall(CommandAst commandAst, CommandInfo command) - { - string commandName = commandAst.GetCommandName(); - - var scopeEntry = scope.LookupCommand(commandName); - - // If the named command maps to a function with workflow binding, then we process that - // function, and create a "workflow calling workflow" scenario.) - if (scopeEntry != null) - { - if (scopeEntry.workflowInfo != null) - { - IScriptExtent extent = null; - if(scopeEntry.functionDefinition != null) - { - extent = scopeEntry.functionDefinition.Extent; - } - CreateNestedWorkflow(scopeEntry.workflowInfo, commandAst, extent); - GenerateWorkflowCallingWorkflowCall(commandAst, scopeEntry.workflowInfo.NestedXamlDefinition, command); - } - else - { - // If the named command maps to a regular PowerShell function, then we compile - // to an inline script. - // If we're in a pipeline, generate an error. The InlineScript wrapper we generate for these - // is not capable of passing along input. - if (this.isVisitingPipeline) - { - ReportError("ActivityNotSupportedInPipeline", - String.Format(CultureInfo.InvariantCulture, ActivityResources.ActivityNotSupportedInPipeline, commandName), commandAst.Extent); - } - - // Although the function may be defined separately from the invocation, - // we generate the InlineScript of: - // function foo($bar) { ... }; foo "Test" - string functionDefinition = scopeEntry.functionDefinition.Extent.Text; - GenerateInlineScriptForFunctionCall(null, functionDefinition, commandAst); - } - } - else if ((command != null) && - (command.CommandType == CommandTypes.Function) && - (((command.Module == null) || ((definingModule != null) && (String.Equals(command.Module.Path, definingModule.Path, StringComparison.OrdinalIgnoreCase)))))) - { - // Extract functions from the session state, as long as they are not defined in a module - // (as they probably have requirements that cannot be met). - - // If we're in a pipeline, generate an error. The InlineScript wrapper we generate for these - // is not capable of passing along input. - if (this.isVisitingPipeline) - { - ReportError("ActivityNotSupportedInPipeline", - String.Format(CultureInfo.InvariantCulture, ActivityResources.ActivityNotSupportedInPipeline, commandName), commandAst.Extent); - } - - GenerateInlineScriptForFunctionCall(command.Name, command.Definition, commandAst); - } - else - { - // Skip this expensive step during the parse phase - if (!this.ValidateOnly) - { - // If the command maps to an activity, then we compile it into that activity - bool activityFound = GenerateActivityCallFromCommand(commandName, commandAst); - if (!activityFound) - { - GenerateNativeCommandCallFromCommand(commandName, commandAst); - } - } - } - } - - private void GenerateWorkflowCallingWorkflowCall(CommandAst commandAst, string xaml, CommandInfo command) - { - DynamicActivity result; - using (StringReader xamlReader = new StringReader(xaml)) - { - result = (DynamicActivity)System.Activities.XamlIntegration.ActivityXamlServices.Load(xamlReader); - } - - ProcessAssembly(result.GetType().Assembly, processedActivityLibraries, activityMap); - - StaticBindingResult bindingResult = StaticParameterBinder.BindCommand(commandAst, false); - Dictionary convertedParameters = ResolveParameters(bindingResult); - - // If we are calling a nested workflow and are also storing the result, we need to create - // a new instance of the storage variable. - var currentVariable = GetVariableToUse(); - string storageVariable = null; - bool isAggregatingVariable = false; - if (currentVariable != null) - { - storageVariable = currentVariable.VariableName; - isAggregatingVariable = currentVariable.IsAggregatingVariable; - } - - if (!String.IsNullOrEmpty(storageVariable) && !isAggregatingVariable) - { - string constructor = "New-Object -Type System.Management.Automation.PSDataCollection[PSObject]"; - GeneratePowerShellValue(typeof(System.Management.Automation.PSDataCollection), constructor, false, true); - } - - // Create Inline Script for parameter validation of the inner workflow. - string paramBlock = GetWorkflowParamBlock(command); - - bool inlineScriptGenerated = false; - if (!string.IsNullOrEmpty(paramBlock)) - { - StringBuilder actualParamBlock = AddAdditionalWorkflowParameters(paramBlock); - - // Add inline script parameters to ensure that validation is run locally - Dictionary inlineScriptParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); - inlineScriptParameters.Add("PSComputerName", new CommandArgumentInfo { Value = "$null" }); - - // Generate Inline Script - GenerateInlineScriptForFunctionCall(command.Name, inlineScriptParameters, actualParamBlock.ToString(), commandAst); - inlineScriptGenerated = true; - } - - // Prevent symbols for this workflow call from being added twice. - bool prevDisableSymbolGeneration = this.disableSymbolGeneration; - try - { - this.disableSymbolGeneration = inlineScriptGenerated || this.disableSymbolGeneration; - GenerateActivityCall(null, result.Name, result.GetType(), result, convertedParameters, bindingResult, null, commandAst.Extent); - } - finally - { - this.disableSymbolGeneration = prevDisableSymbolGeneration; - } - } - - private static StringBuilder AddAdditionalWorkflowParameters(string paramBlock) - { - const string additionalParameters = - @" - , - [System.Collections.ArrayList] $Result, - [System.Collections.ArrayList] $PSError, - [System.Collections.ArrayList] $PSProgress, - [System.Collections.ArrayList] $PSVerbose, - [System.Collections.ArrayList] $PSDebug, - [System.Collections.ArrayList] $PSWarning, - [System.Collections.ArrayList] $PSInformation, - [Nullable[uint32]] $PSActionRetryCount, - [bool] $PSDisableSerialization, - [NUllable[uint32]] $PSActionRunningTimeoutSec, - [NUllable[uint32]] $PSActionRetryIntervalSec, - [string[]] $PSRequiredModules"; - - // Remove the ")" from the end - string newParamBlock = paramBlock.Substring(0, paramBlock.Length - 2); - - StringBuilder actualParamBlock = new StringBuilder(); - actualParamBlock.Append(newParamBlock); - actualParamBlock.Append(additionalParameters); - - // Add the ")" at the end - actualParamBlock.Append("\n)"); - - return actualParamBlock; - } - - private string GetWorkflowParamBlock(CommandInfo command) - { - var paramBlock = string.Empty; - if (command != null) - { - WorkflowInfo workflowInfo = (WorkflowInfo)command; - ScriptBlockAst sb; - if (workflowInfo.ScriptBlock != null) - { - sb = workflowInfo.ScriptBlock.Ast as ScriptBlockAst; - if (sb != null) - { - if (sb.BeginBlock != null && sb.BeginBlock.Statements != null && - sb.BeginBlock.Statements.Count > 0) - { - FunctionDefinitionAst functionAst = sb.BeginBlock.Statements[0] as FunctionDefinitionAst; - if (functionAst != null) - { - if (functionAst.Body != null && functionAst.Body.ParamBlock != null) - { - paramBlock = functionAst.Body.ParamBlock.ToString(); - } - } - } - } - } - } - return paramBlock; - } - - private void GenerateInlineScriptForFunctionCall(string name, Dictionary parameters, string definition, CommandAst commandAst) - { - // We add a trap / break so that the InlineScript actually generates an exception when a terminating - // error happens. - string inlineScript = "trap { break }\r\n"; - string functionCall = AddUsingPrefixToWorkflowVariablesForFunctionCall(commandAst); - - if (!String.IsNullOrEmpty(name)) - { - inlineScript += String.Format( - CultureInfo.InvariantCulture, - "function {0}\r\n{{\r\n{1}\r\n}}\r\n", - name, definition); - } - else - { - inlineScript += definition + "\r\n"; - } - - // Use the function name as the display name of the InlineScript activity - string displayName = name ?? commandAst.GetCommandName(); - - inlineScript += functionCall; - GenerateInlineScript(inlineScript, displayName, parameters, commandAst.Extent, false); - } - - private void GenerateInlineScriptForFunctionCall(string name, string definition, CommandAst commandAst) - { - GenerateInlineScriptForFunctionCall(name, null, definition, commandAst); - } - - // Get all variable expressions, sort them and return - private static IEnumerable GetVariableExpressionAsts(CommandAst command) - { - var list = command.FindAll(ast => ast is VariableExpressionAst, searchNestedScriptBlocks: true).ToList(); - if (list.Count > 1) - { - return list.OrderBy(a => a.Extent.StartOffset); - } - return list; - } - - private static string AddUsingPrefixToWorkflowVariablesForFunctionCall(CommandAst command) - { - string functionCall = command.Extent.Text; - IEnumerable variableExprs = GetVariableExpressionAsts(command); - if (!variableExprs.Any()) { return functionCall; } - - StringBuilder newFunctionCall = null; - int baseOffset = command.Extent.StartOffset; - int commandTextRelativeStartOffset = 0; - - foreach (Ast ast in variableExprs) - { - var variableExpr = ast as VariableExpressionAst; - if (variableExpr == null) { continue; } - if (IsVariableFromUsingExpression(variableExpr, topParent: command)) { continue; } - if (variableExpr.IsConstantVariable()) { continue; } - - string varName = variableExpr.VariablePath.UserPath; - if (varName.IndexOf(':') >= 0) - { - if (varName.StartsWith("WORKFLOW:", StringComparison.OrdinalIgnoreCase)) - { - // If the variable has a 'workflow' scope prefix, we always add 'using' prefix to it - varName = varName.Remove(0, "WORKFLOW:".Length); - } - else - { - continue; // Something like $env:PSModulePath - } - } - else - { - // We add 'using' prefix to all other variables except $input. - // $input doesn't need the 'using' prefix to work within an InlineScript: - // PS\> workflow bar { 1..2 | inlinescript { $input } } - // PS\> bar - // 1 - // 2 - if (String.Equals(varName, "Input", StringComparison.OrdinalIgnoreCase)) - { - continue; - } - } - - // Check if we need to include the using variable in a parenthesis - bool needParen = false; - var indexExprParent = variableExpr.Parent as IndexExpressionAst; - if (indexExprParent != null && indexExprParent.Target == variableExpr) - { - needParen = true; - } - else - { - var memberExprParent = variableExpr.Parent as MemberExpressionAst; - if (memberExprParent != null && memberExprParent.Expression == variableExpr) - { - needParen = true; - } - } - - // We need to alter the function call script by this point - if (newFunctionCall == null) - { - newFunctionCall = new StringBuilder(); - } - - string newVarName = (variableExpr.Splatted ? "@" : "$") + "using:" + varName; - newVarName = needParen ? "(" + newVarName + ")" : newVarName; - int variableRelativeStartOffset = variableExpr.Extent.StartOffset - baseOffset; - int variableRelativeEndOffset = variableExpr.Extent.EndOffset - baseOffset; - - newFunctionCall.Append(functionCall.Substring(commandTextRelativeStartOffset, variableRelativeStartOffset - commandTextRelativeStartOffset)); - newFunctionCall.Append(newVarName); - commandTextRelativeStartOffset = variableRelativeEndOffset; - } - - if (newFunctionCall != null) - { - newFunctionCall.Append(functionCall.Substring(commandTextRelativeStartOffset)); - return newFunctionCall.ToString(); - } - - return functionCall; - } - - private static bool IsVariableFromUsingExpression(VariableExpressionAst variableExpr, Ast topParent) - { - var parent = variableExpr.Parent; - while (parent != topParent) - { - if (parent is UsingExpressionAst) - { - return true; - } - parent = parent.Parent; - } - return false; - } - - private void GenerateNativeCommandCallFromCommand(string commandName, CommandAst commandAst) - { - // If it parses like a command but isn't a regular PowerShell command, then - // it's likely a native command. Convert this into an InlineScript call, since - // we can't do anything intelligent about parameter parsing. - // Use the command name as the display name of the InlineScript activity. - string inlineScript = AddUsingPrefixToWorkflowVariablesForFunctionCall(commandAst); - GenerateInlineScript(inlineScript, commandName, null, commandAst.Extent, false); - } - - // If the named command is in any of the referenced DLLs, then we call it as an activity. - // If the adjusted named command (i.e.: Get-Process -> GetProcessActivity) is in any of the - // referenced DLLs, then we call it as an activity. - private bool GenerateActivityCallFromCommand(string commandName, CommandAst commandAst) - { - CommandInfo resolvedCommand; - Type[] genericTypes; - - // Find the activity for this name - Type activityType = ResolveActivityType(commandName, commandAst, true, out resolvedCommand, out genericTypes); - - // If this didn't map to a specific command, generate an error that tells the user to use an - // InlineScript - if ((activityType == null) && (resolvedCommand == null)) - { - // Do not report this error at validation time, as it will only ever work - // at run-time. - if (!ValidateOnly) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.CommandNotFound, commandName); - ReportError("CommandNotFound", error, commandAst.Extent); - } - } - - if (activityType == null) { return false; } - - // Get the parameters used - StaticBindingResult syntacticResult = StaticParameterBinder.BindCommand(commandAst, false); - StaticBindingResult bindingResult = StaticParameterBinder.BindCommand(commandAst, true); - - // See if there are any errors to report - if (bindingResult.BindingExceptions != null) - { - foreach(KeyValuePair bindingException in bindingResult.BindingExceptions) - { - if (String.Equals("ParameterAlreadyBound", - bindingException.Value.BindingException.ErrorId, StringComparison.OrdinalIgnoreCase)) - { - // throw Duplicate parameters error - ReportError("DuplicateParametersNotAllowed", - string.Format(CultureInfo.CurrentCulture, ActivityResources.DuplicateParametersNotAllowed, bindingException.Key), - bindingException.Value.CommandElement.Extent); - } - else if (String.Equals("NamedParameterNotFound", - bindingException.Value.BindingException.ErrorId, StringComparison.OrdinalIgnoreCase)) - { - // Skip for now, handled during activity generation. - } - else - { - // throw the other error - ReportError( - bindingException.Value.BindingException.ErrorId, - bindingException.Value.BindingException.Message, - bindingException.Value.CommandElement.Extent); - } - } - } - - Dictionary convertedParameters = ResolveParameters(bindingResult); - - if (genericTypes != null && genericTypes.Length > 0) - { - var genericTypeArg = new CommandArgumentInfo() {Value = genericTypes}; - convertedParameters.Add(GenericTypesKey, genericTypeArg); - } - - GenerateActivityCall(resolvedCommand, null, activityType, null, convertedParameters, bindingResult, syntacticResult, commandAst.Extent); - return true; - } - - /// - /// Resolve the activity type based on the given command name - /// - /// - /// - /// Indicate the error action - /// The CommandInfo instance resolved from the command name - /// If it is a generic activity, genericTypes contains the actual types in the order they get declared - /// - internal Type ResolveActivityType(string commandName, CommandAst commandAst, bool throwError, out CommandInfo resolvedCommand, out Type[] genericTypes) - { - genericTypes = null; - - int index = commandName.IndexOf('['); - if (index != -1 && commandName.EndsWith("]", StringComparison.Ordinal)) - { - string typeNames = commandName.Substring(index + 1, commandName.Length - index - 2); - commandName = commandName.Substring(0, index).Trim(); - genericTypes = ResolveGenericTypes(typeNames, commandAst.CommandElements[0]); - } - - // Resolve the command - resolvedCommand = ResolveCommand(commandName); - commandName = resolvedCommand != null ? resolvedCommand.Name : commandName; - - // Replace any dashes, and search for activities that match. - string searchName = commandName.Replace("-", ""); - - // Verify it isn't a built-in command - CommandInfo unused = null; - - bool searchSessionState = !this.ValidateOnly; - ActivityKind activityKind = ResolveActivityKindBasedOnCommandName(searchName, commandAst, out unused, searchSessionState); - if (activityKind != ActivityKind.RegularCommand) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.CommandHandledByKeyword, commandName, searchName); - ReportError("CommandHandledByKeyword", error, commandAst.Extent); - } - - if (genericTypes != null && genericTypes.Length > 0) - { - searchName += String.Format(CultureInfo.InvariantCulture, "`{0}", genericTypes.Length); - } - - if (!activityMap.ContainsKey(searchName)) - { - if ((resolvedCommand != null) && (resolvedCommand.CommandType != CommandTypes.Application)) - { - // Check if it was explicitly excluded. - // - We check for null module names, as this may have come from an activity in SMA (which is a snapin). - // - We check for Microsoft.PowerShell.Host, as that module has only two commands (that are not intended to be activities). - // - We check to see if this command comes from a module that has defined activities for OTHER commands. - string moduleName = resolvedCommand.ModuleName; - if ((String.IsNullOrEmpty(moduleName) || - String.Equals("Microsoft.PowerShell.Host", moduleName, StringComparison.OrdinalIgnoreCase) || - String.Equals("CimCmdlets", moduleName, StringComparison.OrdinalIgnoreCase) || - processedActivityLibraries.Contains(moduleName)) && throwError) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.CommandActivityExcluded, commandName); - ReportError("CommandActivityExcluded", error, commandAst.Extent); - } - } - - return null; - } - - // Find the activity for this name - Type activityType = activityMap[searchName]; - - // The name was cached, but the type is null. That means the command name - // is ambiguous. - if (activityType == null && throwError) - { - String errorMessage = String.Format(CultureInfo.InvariantCulture, - ActivityResources.AmbiguousCommand, - commandName); - ReportError("AmbiguousCommand", errorMessage, commandAst.Extent); - } - - return activityType; - } - - private Type[] ResolveGenericTypes(string typeNames, Ast targetAst) - { - string[] names = typeNames.Split(','); - var result = new List(); - - foreach (string typeName in names) - { - try - { - var outputType = LanguagePrimitives.ConvertTo(typeName.Trim()); - result.Add(outputType); - } - catch (PSInvalidCastException) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.NewObjectCouldNotFindType, typeName); - ReportError("GenericTypeNotFound", error, targetAst.Extent); - } - } - - return result.ToArray(); - } - - private void CreateNestedWorkflow(WorkflowInfo command, CommandAst commandAst, IScriptExtent extent) - { - AddWorkflowCommonParameters(command, commandAst, extent); - - // Register the conversion of the outer workflow - var entry = scope.LookupCommand(command.Name); - if (entry == null) - { - entry = new Scope.Entry { workflowInfo = command, scope = scope }; - scope.functionDefinitions.Add(command.Name, entry); - } - - nestedWorkflows.Add(command); - - foreach (var nested in command.WorkflowsCalled) - { - nestedWorkflows.Add(nested); - } - } - - private void AddWorkflowCommonParameters(WorkflowInfo workflowInfo, CommandAst commandAst, IScriptExtent extent) - { - // Verify it doesn't use any advanced validation - foreach (string parameter in workflowInfo.Parameters.Keys) - { - // Skip validation on WF-common parameters - if (parameter.StartsWith("PS", StringComparison.OrdinalIgnoreCase) || Cmdlet.CommonParameters.Contains(parameter)) - { - continue; - } - - ParameterMetadata metadata = workflowInfo.Parameters[parameter]; - foreach (Attribute validationAttribute in metadata.Attributes) - { - if ((validationAttribute is ValidateArgumentsAttribute) || - (validationAttribute is AliasAttribute)) - { - ReportError("ParameterValidationNotSupportedOnNestedWorkflows", - ActivityResources.ParameterValidationNotSupportedOnNestedWorkflows, - extent); - } - } - - } - - - if (this.ValidateOnly) { return; } - - - string xaml = workflowInfo.XamlDefinition; - string nestedXaml = workflowInfo.NestedXamlDefinition; - - // See if it's already been compiled to contain the wrapper properties. If so, just exit. - // If we haven't compiled this workflow before (or it's never been called with the - // ubiquitous parameters), then keep on processing. - if (!String.IsNullOrEmpty(nestedXaml)) - { - if (Regex.IsMatch(nestedXaml, "(]*>)")) - { - return; - } - } - - // We can only add the common parameters to workflows that don't - // depend on any others. We don't generate an error here, as these - // nested workflows can still be useful without these common parameters. - // - // Given workflow A calling B, calling C, (...), calling Z, we can only support workflow - // common parameters on Z. This is due to a technical limitation. When we analyze a workflow - // to add emulation of workflow-common parameters, the API that does this tries to resolve all - // of the activities and types in that workflow. Workflows that already call workflows (B, C,...) - // rely on a temporary DLL that we don't have access to (and can't supply to the - // API that requires it). - - if (workflowInfo.WorkflowsCalled.Count > 0) - { - workflowInfo.NestedXamlDefinition = xaml; - return; - } - - // Read the XAML into an activity builder to determine what properties the - // actual workflow has. - ActivityBuilder activityBuilder = (ActivityBuilder)XamlServices.Load( - ActivityXamlServices.CreateBuilderReader( - new XamlXmlReader( - new StringReader(xaml)))); - - HashSet existingProperties = new HashSet(StringComparer.OrdinalIgnoreCase); - for (int index = 0; index < activityBuilder.Properties.Count; index++) - { - existingProperties.Add(activityBuilder.Properties[index].Name); - } - - // Extract what parameters they used - StaticBindingResult specifiedParameters = StaticParameterBinder.BindCommand(commandAst, false); - List parameterNames = specifiedParameters.BoundParameters.Keys.ToList(); - - // See what properties are on PSRemotingActivity - PropertyInfo[] psRemotingProperties = typeof(PSRemotingActivity).GetProperties(); - bool usesPsRemotingProperties = false; - - // See if they use any PSRemoting properties - foreach (PropertyInfo property in psRemotingProperties) - { - foreach (string parameterName in parameterNames) - { - if (property.Name.StartsWith(parameterName, StringComparison.OrdinalIgnoreCase)) - { - if (! existingProperties.Contains(parameterName)) - { - usesPsRemotingProperties = true; - break; - } - } - } - - if (usesPsRemotingProperties) { break; } - } - - PropertyInfo[] propertiesToUse; - if (! usesPsRemotingProperties) - { - if (String.IsNullOrEmpty(workflowInfo.NestedXamlDefinition)) - { - workflowInfo.NestedXamlDefinition = xaml; - - // Replace the name with _Nested - workflowInfo.NestedXamlDefinition = Regex.Replace( - workflowInfo.NestedXamlDefinition, - "(]*>)", - "$1$2_Nested$3", - RegexOptions.Singleline); - - // Replace any parameter defaults - while (Regex.IsMatch(workflowInfo.NestedXamlDefinition, - "]*>", RegexOptions.Singleline)) - { - workflowInfo.NestedXamlDefinition = Regex.Replace( - workflowInfo.NestedXamlDefinition, - "(]*>)", - "$1$2_Nested$3", - RegexOptions.Singleline); - } - } - - // For nested workflows we want to be able to flow preference variables. To do this we need - // to include the Verbose, Debug, ErrorAction, WarningAction, and InformationAction parameters in the - // nested Xaml definition. - Collection properties = new Collection(); - foreach (var property in psRemotingProperties) - { - if (property.Name.Equals("Verbose", StringComparison.OrdinalIgnoreCase) || - property.Name.Equals("Debug", StringComparison.OrdinalIgnoreCase) || - property.Name.Equals("ErrorAction", StringComparison.OrdinalIgnoreCase) || - property.Name.Equals("WarningAction", StringComparison.OrdinalIgnoreCase) || - property.Name.Equals("InformationAction", StringComparison.OrdinalIgnoreCase)) - { - properties.Add(property); - } - } - - if (properties.Count > 0) - { - propertiesToUse = new PropertyInfo[properties.Count]; - properties.CopyTo(propertiesToUse, 0); - } - else - { - // If they don't use any "workflow calling workflow" common parameters, - // Just use the existing XAML - return; - } - } - else - { - propertiesToUse = psRemotingProperties; - } - - // Otherwise, crack open the XAML and create a new workflow that has all of the common - // parameters they might use. - activityBuilder.Name = activityBuilder.Name + "_Nested"; - - // Sequence to set host defaults from the incoming parameters that were auto-generated - // for the function.. - Sequence configureSequence = new Sequence() - { - Activities = { } - }; - - // Activity that holds the existing logic of the current activity - Activity existingLogic = activityBuilder.Implementation; - - // Sequence to restore values back to the host defaults that were present when - // we entered the function. Needs to be duplicated at the end of the existing logic - // and also in the catch() clause. - Sequence undoSequence = new Sequence() { Activities = { } }; - Sequence undoSequenceInCatch = new Sequence() { Activities = { } }; - - Collection savedHostDefaults = new Collection(); - - // Go through all the properties in PSRemotingActivity they use, and add them as - // virtual properties to this activity - foreach (PropertyInfo field in propertiesToUse) - { - // Don't generate wrapper properties for ones that are already defined - if (existingProperties.Contains(field.Name)) { continue; } - - // See if it's an argument - if (typeof(Argument).IsAssignableFrom(field.PropertyType)) - { - // Create a new property - DynamicActivityProperty newProperty = new DynamicActivityProperty(); - newProperty.Name = field.Name; - newProperty.Type = field.PropertyType; - activityBuilder.Properties.Add(newProperty); - - // Create a variable to hold the saved host default - savedHostDefaults.Add( - new Variable() { Name = "Saved_" + field.Name } - ); - - // Add the configuration of this property to the sequence that sets - // the host defaults. - IsArgumentSet isArgumentSetActivity = new IsArgumentSet(); - ActivityBuilder.SetPropertyReference(isArgumentSetActivity, new ActivityPropertyReference { SourceProperty = field.Name, TargetProperty = "Argument" }); - configureSequence.Activities.Add( - new If() - { - Condition = isArgumentSetActivity, - Then = new Sequence - { - Activities = { - new GetPSWorkflowData { - VariableToRetrieve = PSWorkflowRuntimeVariable.Other, - OtherVariableName = field.Name, - Result = new VisualBasic.Activities.VisualBasicReference("Saved_" + field.Name) - }, - new SetPSWorkflowData() - { - OtherVariableName = field.Name, - Value = new InArgument((System.Activities.Activity)new VisualBasic.Activities.VisualBasicValue(field.Name)) - } - } - } - } - ); - - // Add the restoration of this property to the undo sequence - isArgumentSetActivity = new IsArgumentSet(); - ActivityBuilder.SetPropertyReference(isArgumentSetActivity, new ActivityPropertyReference { SourceProperty = field.Name, TargetProperty = "Argument" }); - undoSequence.Activities.Add( - new If() - { - Condition = isArgumentSetActivity, - Then = new SetPSWorkflowData() - { - OtherVariableName = field.Name, - Value = new InArgument((System.Activities.Activity)new VisualBasic.Activities.VisualBasicValue("Saved_" + field.Name)) - } - } - ); - - // And the version in the catch block - isArgumentSetActivity = new IsArgumentSet(); - ActivityBuilder.SetPropertyReference(isArgumentSetActivity, new ActivityPropertyReference { SourceProperty = field.Name, TargetProperty = "Argument" }); - undoSequenceInCatch.Activities.Add( - new If() - { - Condition = isArgumentSetActivity, - Then = new SetPSWorkflowData() - { - OtherVariableName = field.Name, - Value = new InArgument((System.Activities.Activity)new VisualBasic.Activities.VisualBasicValue("Saved_" + field.Name)) - } - } - ); - } - } - - // Wrapper commands to save the current host settings, change them based on the parameters that have - // been passed in, and then restore them. - Sequence hostSettingWrapper = new Sequence() - { - Activities = { - new TryCatch() - { - Try = new Sequence() - { - Activities = { - configureSequence, - existingLogic, - undoSequence - } - }, - Catches = { - new Catch() - { - Action = new ActivityAction() - { - Handler = new Sequence() { - Activities = { - undoSequenceInCatch, - new Rethrow() - } - } - } - } - } - } - } - }; - - foreach (Variable savedHostDefault in savedHostDefaults) - { - hostSettingWrapper.Variables.Add(savedHostDefault); - } - - activityBuilder.Implementation = hostSettingWrapper; - - XmlWriterSettings settings = new XmlWriterSettings(); - settings.OmitXmlDeclaration = true; - settings.Indent = true; - - StringBuilder resultWriter = new StringBuilder(); - XmlWriter xmlWriter = XmlWriter.Create(resultWriter, settings); - XamlSchemaContext xamlSchemaContext = new XamlSchemaContext(); - XamlXmlWriter xamlXmlWriter = new XamlXmlWriter(xmlWriter, xamlSchemaContext); - XamlWriter xamlWriter = ActivityXamlServices.CreateBuilderWriter(xamlXmlWriter); - XamlServices.Save(xamlWriter, activityBuilder); - - workflowInfo.NestedXamlDefinition = resultWriter.ToString(); - } - - private Dictionary resolvedCommands = new Dictionary(StringComparer.OrdinalIgnoreCase); - - private CommandInfo ResolveCommand(string commandName) - { - CommandInfo resolvedCommand = null; - - if (resolvedCommands.TryGetValue(commandName, out resolvedCommand)) - { - return resolvedCommand; - } - - var scopeEntry = scope.LookupCommand(commandName); - if (scopeEntry != null) - { - resolvedCommands[commandName] = null; - return null; - } - - // Make sure we only call the cmdlet. - var getCommandCommand = new CmdletInfo("Get-Command", typeof(GetCommandCommand)); - do - { - // Look in the session for the command name. - invoker.Commands.Clear(); - invoker.AddCommand(getCommandCommand) - .AddParameter("Name", commandName) - .AddParameter("ErrorAction", ActionPreference.Ignore); - var result = invoker.Invoke(); - - // There was a result - this is a command from the session. - // Resolve an aliases, and figure out the actual command name. - if (result.Count > 0) - { - foreach (CommandInfo currentResult in result) - { - string compareName = currentResult.Name; - string moduleQualifiedName = currentResult.ModuleName + "\\" + currentResult.Name; - - if (currentResult.CommandType == CommandTypes.Application) - { - compareName = System.IO.Path.GetFileNameWithoutExtension(currentResult.Name); - } - - if (String.Equals(commandName, currentResult.Name, StringComparison.OrdinalIgnoreCase) || - String.Equals(commandName, moduleQualifiedName, StringComparison.OrdinalIgnoreCase) || - String.Equals(commandName, compareName, StringComparison.OrdinalIgnoreCase)) - { - resolvedCommand = currentResult; - - if (resolvedCommand is AliasInfo) - { - commandName = resolvedCommand.Definition; - } - break; - } - } - } - - // Also chase down aliases - } while (resolvedCommand is AliasInfo); - - resolvedCommands[commandName] = resolvedCommand; - return resolvedCommand; - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Microsoft.Reliability", - "CA2001:AvoidCallingProblematicMethods", - MessageId = "System.Reflection.Assembly.LoadFrom")] - private static void PopulateActivityStaticMap() - { - string[] defaultAssemblies = { - "Microsoft.PowerShell.Activities", - "Microsoft.PowerShell.Core.Activities", - "Microsoft.PowerShell.Diagnostics.Activities", - "Microsoft.PowerShell.Management.Activities", - "Microsoft.PowerShell.Security.Activities", - "Microsoft.PowerShell.Utility.Activities", - "Microsoft.WSMan.Management.Activities", - "Microsoft.PowerShell.Workflow.ServiceCore" - }; - - // Add our default assemblies - foreach (string assembly in defaultAssemblies) - { - Assembly loadedAssembly = null; - - try - { - loadedAssembly = Assembly.Load(assembly); - } - catch (IOException) - { - string newAssembly = assembly + ", Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; - loadedAssembly = Assembly.Load(newAssembly); - } - - ProcessAssembly(loadedAssembly, staticProcessedActivityLibraries, staticActivityMap); - } - } - - [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] - internal static Dictionary GetActivityMap(IEnumerable assembliesToScan, out HashSet processedActivityLibraries) - { - // Guard access to private static variables. IEnumerable use is not thread safe. - Dictionary activityMap = null; - lock (staticProcessedActivityLibraries) - { - activityMap = new Dictionary(staticActivityMap, StringComparer.OrdinalIgnoreCase); - - if (assembliesToScan == null) - { - processedActivityLibraries = new HashSet(staticProcessedActivityLibraries, StringComparer.OrdinalIgnoreCase); - return activityMap; - } - - processedActivityLibraries = new HashSet(staticProcessedActivityLibraries, StringComparer.OrdinalIgnoreCase); - } - - foreach (string assembly in assembliesToScan) - { - Assembly loadedAssembly = null; - - try - { - // Try first from the GAC - loadedAssembly = Assembly.Load(assembly); - } - catch (IOException) - { - try - { - // And second by path - loadedAssembly = Assembly.LoadFrom(assembly); - } - catch (IOException) - { - // Finally, by relative path - if (CoreRunspaces.Runspace.DefaultRunspace != null) - { - using (System.Management.Automation.PowerShell nestedPs = - System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) - { - nestedPs.AddCommand("Get-Location"); - PathInfo result = nestedPs.Invoke()[0]; - - string currentLocation = result.ProviderPath; - - try - { - loadedAssembly = Assembly.LoadFrom(Path.Combine(currentLocation, assembly)); - } - catch (IOException) - { - } - } - } - } - } - - // Throw an error if we weren't able to load the assembly. - if (loadedAssembly == null) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.CouldNotLoadRequiredAssembly, assembly); - throw new IOException(error); - } - - ProcessAssembly(loadedAssembly, processedActivityLibraries, activityMap); - } - - return activityMap; - } - - private static void ProcessAssembly(Assembly activityAssembly, HashSet processedActivityLibraries, Dictionary activityMap) - { - // Remember that we've processed this assembly (and therefore - // the module). - string assemblyName = activityAssembly.ManifestModule.Name; - int activityIndex = assemblyName.IndexOf(".Activities.dll", StringComparison.OrdinalIgnoreCase); - if(activityIndex > 0) - { - string moduleName = assemblyName.Substring(0, activityIndex); - processedActivityLibraries.Add(moduleName); - } - - bool isRunningInConstrainedLanguage = false; - - // If we're in ConstrainedLanguage mode, block inline XAML as it can't be verified. - CoreRunspaces.Runspace currentRunspace = (CoreRunspaces.Runspace)CoreRunspaces.Runspace.DefaultRunspace; - if (currentRunspace != null) - { - if (currentRunspace.SessionStateProxy.LanguageMode == PSLanguageMode.ConstrainedLanguage) - { - isRunningInConstrainedLanguage = true; - } - } - - - foreach (Type type in activityAssembly.GetTypes()) - { - // Process activities exported by this assembly - if (!typeof(System.Activities.Activity).IsAssignableFrom(type)) - { - continue; - } - - // Ensure it derives from PSActivity if it's in constrained language, - // as arbitrary workflow activities (like VisualBasicValue) may be - // unsafe - if (isRunningInConstrainedLanguage && - (!typeof(Microsoft.PowerShell.Activities.PSActivity).IsAssignableFrom(type))) - { - continue; - } - - // If the activity map already contains an assembly with this name, - // then set the value to NULL. During processing, we will recognize - // this and throw an exception about this type name being ambiguous. - if (activityMap.ContainsKey(type.Name)) - { - activityMap[type.Name] = null; - } - else - { - activityMap[type.Name] = type; - } - - // Update the activity map with the full name to support users specifying - // an activity with full name. - // If the activity map already contains an assembly with this full name, - // set the value to NULL as is done for Name key. - if (activityMap.ContainsKey(type.FullName)) - { - activityMap[type.FullName] = null; - } - else - { - activityMap[type.FullName] = type; - } - - // Update the activity map with both the - // short name and full name. By convention, activities generated from commands - // get put in a namespace with ".Activities" after the module name. Remove the - // ".Activities" bit so that module-qualified command lookups work - i.e.: - // Microsoft.PowerShell.Utility\Write-Output - activityMap[type.Namespace.Replace(".Activities", "") + "\\" + type.Name] = type; - } - } - - private void GeneratePersist(CommandAst commandAst) - { - // Verify they've used the correct syntax - if (commandAst.CommandElements.Count > 1) - { - ReportError("CheckpointWorkflowSyntax", ActivityResources.CheckpointWorkflowSyntax, commandAst.Extent); - } - - GenerateActivityCall(null, null, typeof(Microsoft.PowerShell.Activities.PSPersist), null, null, null, null, commandAst.Extent); - } - - private void GenerateSuspend(CommandAst commandAst) - { - StaticBindingResult bindingResult = StaticParameterBinder.BindCommand(commandAst, false); - - // Verify they've used the correct syntax - if (bindingResult.BoundParameters.Count == 0) - { - GenerateActivityCall(null, null, typeof(Microsoft.PowerShell.Activities.Suspend), null, null, null, null, commandAst.Extent); - return; - } - - if (bindingResult.BoundParameters.Count == 1 && bindingResult.BoundParameters.ContainsKey("Label")) - { - Object label = bindingResult.BoundParameters["Label"].ConstantValue; - if (label == null) - { - ReportError("SuspendWorkflowSyntax", ActivityResources.SuspendWorkflowSyntax, commandAst.Extent); - return; - } - - CommandArgumentInfo argumentInfo = new CommandArgumentInfo(); - - argumentInfo.Value = label.ToString(); - argumentInfo.IsLiteral = true; - - // Prepare the parameter used - Dictionary finalParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); - finalParameters["Label"] = argumentInfo; - - GenerateActivityCall(null, null, typeof(Microsoft.PowerShell.Activities.Suspend), null, finalParameters, null, null, commandAst.Extent); - - return; - } - - ReportError("SuspendWorkflowSyntax", ActivityResources.SuspendWorkflowSyntax, commandAst.Extent); - } - - private void GenerateInvokeExpression(CommandAst commandAst) - { - StaticBindingResult bindingResult = StaticParameterBinder.BindCommand(commandAst, false); - - // Verify they've used the correct syntax - if (bindingResult.BoundParameters.Count != 2) - { - ReportError("InvokeExpressionSyntaxTooManyParameters", ActivityResources.InvokeExpressionSyntax, commandAst.Extent); - return; - } - - // Validate the 'Language' parameter - if (bindingResult.BoundParameters.ContainsKey("Language")) - { - ParameterBindingResult language = bindingResult.BoundParameters["Language"]; - Object constantLanguage = language.ConstantValue; - if (constantLanguage == null) - { - ReportError("InvokeExpressionSyntaxLanguageNotConstant", ActivityResources.InvokeExpressionSyntax, commandAst.Extent); - return; - } - - if (!String.Equals("XAML", constantLanguage.ToString(), StringComparison.OrdinalIgnoreCase)) - { - ReportError("MustUseXamlLanguage", ActivityResources.MustUseXamlLanguage, language.Value.Extent); - } - } - else - { - ReportError("InvokeExpressionSyntaxLanguageMissing", ActivityResources.InvokeExpressionSyntax, commandAst.Extent); - return; - } - - // Validate the 'Command' parameter - if (bindingResult.BoundParameters.ContainsKey("Command")) - { - ParameterBindingResult command = bindingResult.BoundParameters["Command"]; - Object constantCommand = command.ConstantValue; - if (constantCommand == null) - { - ReportError("InvokeExpressionSyntaxCommandNotConstant", ActivityResources.InvokeExpressionSyntax, commandAst.Extent); - return; - } - - // If we're in ConstrainedLanguage mode, block inline XAML as it can't be verified. - CoreRunspaces.Runspace currentRunspace = (CoreRunspaces.Runspace) CoreRunspaces.Runspace.DefaultRunspace; - if (currentRunspace != null) - { - if (currentRunspace.SessionStateProxy.LanguageMode == PSLanguageMode.ConstrainedLanguage) - { - ReportError("InlineXamlNotSupported", ActivityResources.InlineXamlNotSupported, commandAst.Extent); - return; - } - } - - string inlineXaml = constantCommand.ToString(); - bodyElements.Add(inlineXaml); - } - else - { - ReportError("InvokeExpressionSyntaxCommandMissing", ActivityResources.InvokeExpressionSyntax, commandAst.Extent); - return; - } - } - - private void GenerateStartSleep(CommandAst commandAst) - { - CommandInfo command = ResolveCommand("Start-Sleep"); - Dictionary parameters = GetAndResolveParameters(commandAst, true); - - // Verify they've used the correct syntax - if (parameters.Count != 1) - { - ReportError("StartSleepSyntaxTooManyParameters", ActivityResources.StartSleepSyntax, commandAst.Extent); - return; - } - - CommandArgumentInfo argumentInfo = new CommandArgumentInfo(); - - if (parameters.ContainsKey("Seconds")) - { - // 'Seconds' parameter - string paramValue = parameters["Seconds"].Value.ToString(); - if (paramValue == null) - { - ReportError("StartSleepSyntaxCouldNotParseSeconds", ActivityResources.StartSleepSyntax, commandAst.Extent); - return; - } - - int secondsInt = -1; - if (Int32.TryParse(paramValue, out secondsInt)) - { - // Create literal TimeSpan argument type from seconds. - argumentInfo.Value = new System.TimeSpan(0, 0, secondsInt).ToString(); - argumentInfo.IsLiteral = true; - } - else - { - // Create expression argument with conversion from seconds to ticks for TimeSpan type. - argumentInfo.Value = String.Format(CultureInfo.InvariantCulture, - @"([Int64]({0}) * 10000000)", paramValue); - - argumentInfo.IsLiteral = false; - } - } - else if (parameters.ContainsKey("Milliseconds")) - { - // 'Milliseconds' parameter - string paramValue = parameters["Milliseconds"].Value.ToString(); - if (paramValue == null) - { - ReportError("StartSleepSyntaxCouldNotParseSeconds", ActivityResources.StartSleepSyntax, commandAst.Extent); - return; - } - - int mSecondsInt = -1; - if (Int32.TryParse(paramValue, out mSecondsInt)) - { - // Create literal TimeSpan argument type from milliseconds. - argumentInfo.Value = new System.TimeSpan(0, 0, 0, 0, mSecondsInt).ToString(); - argumentInfo.IsLiteral = true; - } - else - { - // Create expression argument with conversion from milliseconds to ticks for TimeSpan type. - argumentInfo.Value = String.Format(CultureInfo.InvariantCulture, - @"([Int64]({0}) * 10000)", paramValue); - - argumentInfo.IsLiteral = false; - } - } - else - { - ReportError("StartSleepSyntaxSecondsMissing", ActivityResources.StartSleepSyntax, commandAst.Extent); - return; - } - - // Prepare the parameter used - Dictionary finalParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); - finalParameters["Duration"] = argumentInfo; - - GenerateActivityCall(null, null, typeof(System.Activities.Statements.Delay), null, finalParameters, null, null, commandAst.Extent); - } - - private void GenerateNewObject(CommandAst commandAst) - { - CommandInfo command = ResolveCommand("New-Object"); - Dictionary parameters = GetAndResolveParameters(commandAst, true); - - // Verify they've used the correct syntax - if ((parameters.Count == 0) || (parameters.Count > 2)) - { - ReportError("NewObjectSyntaxTooManyParameters", ActivityResources.NewObjectSyntax, commandAst.Extent); - return; - } - - if (parameters.ContainsKey("TypeName")) - { - // 'TypeName' parameter - string paramValue = parameters["TypeName"].OriginalValue.ToString(); - Type outputType = ResolveTypeFromParameterValue(commandAst, paramValue); - if (outputType == null) - { - // Error reporting already handled by ResolveTypeFromParameterValue - return; - } - - string expression = GetPowerShellValueExpression(commandAst); - GeneratePowerShellValue(outputType, expression, false, true); - } - else - { - ReportError("NewObjectSyntaxMissingTypeName", ActivityResources.NewObjectSyntax, commandAst.Extent); - } - } - - private Type ResolveTypeFromParameterValue(CommandAst commandAst, string paramValue) - { - Type outputType = null; - - try - { - outputType = LanguagePrimitives.ConvertTo(paramValue); - } - catch (PSInvalidCastException) - { - // Find the actual parameter AST - Ast resultAst = commandAst; - foreach (CommandElementAst commandElement in commandAst.CommandElements) - { - var parameter = commandElement as CommandParameterAst; - if (parameter != null) - { - if (String.Equals("TypeName", parameter.ParameterName, StringComparison.OrdinalIgnoreCase)) - { - resultAst = parameter; - break; - } - } - } - - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.NewObjectCouldNotFindType, paramValue); - ReportError("NewObjectCouldNotFindType", error, resultAst.Extent); - } - - return outputType; - } - - // We need to convert command arguments to their script representation - // for the following scenario: - // Get-Process -Name Foo,Bar - // When we call the Get-Process activity, we need to coerce its arguments - // to the type expected by the parameter - for example, string[]. - // To do this, we generate basically the following XAML: - // - // - // - // - // - // Get-Process -Name $arguments - // But since the arguments were retrieved in parameter parsing mode, we need - // to quote strings (since they may be considered commands) before supplying - // them as a PowerShell expression. - private string ProcessCommandArgument(CommandElementAst argument) - { - if (argument == null) - return null; - string argumentValue = ""; - - ArrayLiteralAst arrayLiteralArgument = argument as ArrayLiteralAst; - - if (arrayLiteralArgument != null) - { - foreach (ExpressionAst expression in arrayLiteralArgument.Elements) - { - if (!String.IsNullOrEmpty(argumentValue)) - argumentValue += ","; - - argumentValue += ProcessCommandArgumentElement(expression); - } - } - else - { - argumentValue = ProcessCommandArgumentElement(argument); - } - - return argumentValue; - } - - private string ProcessCommandArgumentElement(CommandElementAst argument) - { - // If it's a string constant, single quote it. - StringConstantExpressionAst stringAst = argument as StringConstantExpressionAst; - if (stringAst != null && stringAst.StringConstantType == StringConstantType.BareWord) - { - string bareContent = argument.Extent.Text.Replace("'", "''"); - bareContent = "'" + bareContent + "'"; - - return bareContent; - } - - // If it's an expandable string, double quote it - ExpandableStringExpressionAst expandableString = argument as ExpandableStringExpressionAst; - if (expandableString != null && expandableString.StringConstantType == StringConstantType.BareWord) - { - string bareContent = argument.Extent.Text.Replace("\"", "`\""); - bareContent = "\"" + bareContent + "\""; - - return bareContent; - } - - return argument.Extent.Text; - } - - private void ProcessInlineScriptAst(CommandAst commandAst) - { - if (commandAst.CommandElements.Count == 1) - { - ReportError("InlineScriptSyntaxTooFewParameters", ActivityResources.InlineScriptSyntax, commandAst.Extent); - return; - } - - var scriptBlock = commandAst.CommandElements[1] as ScriptBlockExpressionAst; - bool scriptBlockExists = scriptBlock != null; - if (!scriptBlockExists) - { - ReportError("InlineScriptMissingStatementBlock", ActivityResources.InlineScriptSyntax, commandAst.Extent); - return; - } - - List variableExprsWithWorkflowPrefix = ContainVariablesWithWorkflowPrefix(scriptBlock); - if (variableExprsWithWorkflowPrefix.Any()) - { - ReportError("CannotUseWorkflowPrefixInInlineScript", ActivityResources.CannotUseWorkflowPrefixInInlineScript, variableExprsWithWorkflowPrefix[0].Extent); - } - - StaticBindingResult bindingResult = StaticParameterBinder.BindCommand(commandAst, false); - - // Remove the first positional element (the script) if there is one - if (bindingResult.BoundParameters.ContainsKey("0")) - { - bindingResult.BoundParameters.Remove("0"); - } - - bool isCommandSpecified = bindingResult.BoundParameters.ContainsKey("Command"); - bool isCommandNameSpecified = bindingResult.BoundParameters.ContainsKey("CommandName"); - bool isParametersSpecified = bindingResult.BoundParameters.ContainsKey("Parameters"); - var sbExpr = commandAst.CommandElements[1] as ScriptBlockExpressionAst; - - IScriptExtent errorParameterExtent = null; - if (isCommandSpecified) - { - errorParameterExtent = bindingResult.BoundParameters["Command"].Value.Extent; - } - else if (isCommandNameSpecified) - { - errorParameterExtent = bindingResult.BoundParameters["CommandName"].Value.Extent; - } - else if (isParametersSpecified) - { - errorParameterExtent = bindingResult.BoundParameters["Parameters"].Value.Extent; - } - - if (errorParameterExtent != null) - { - ReportError("InlineScriptParameterNotSupported", ActivityResources.InlineScriptSyntax, errorParameterExtent); - } - - Dictionary convertedParameters = ResolveParameters(bindingResult); - String scriptToProcess = sbExpr.ScriptBlock.EndBlock.Extent.Text; - GenerateInlineScript(scriptToProcess, null, convertedParameters, commandAst.Extent, true); - } - - private List ContainVariablesWithWorkflowPrefix(Ast scriptBlock) - { - Func variableExprWithWorkflowPrefixSearcher = - (ast) => - { - var variableExpr = ast as VariableExpressionAst; - if (variableExpr == null) { return false; } - - return variableExpr.VariablePath.UserPath.StartsWith("WORKFLOW:", StringComparison.OrdinalIgnoreCase); - }; - - return scriptBlock.FindAll(variableExprWithWorkflowPrefixSearcher, searchNestedScriptBlocks: true).ToList(); - } - - private const string PSRequiredModules = "PSRequiredModules"; - private void GenerateInlineScript(string inlineScript, string displayName, Dictionary parameters, IScriptExtent extent, bool isExplicit) - { - Dictionary arguments = new Dictionary(StringComparer.OrdinalIgnoreCase); - var argumentInfo = new CommandArgumentInfo { Value = inlineScript, IsLiteral = true }; - arguments.Add("Command", argumentInfo); - - if (displayName != null) - { - var displayNameArgInfo = new CommandArgumentInfo { Value = displayName, IsLiteral = true }; - arguments.Add("DisplayName", displayNameArgInfo); - } - - if (!isExplicit) - { - string moduleQualifier = GetModuleQualifier(inlineScript); - if (moduleQualifier != null) - { - CommandArgumentInfo requiredModuleArgument = new CommandArgumentInfo(); - requiredModuleArgument.Value = string.Format(CultureInfo.InvariantCulture, - "'{0}'", moduleQualifier); - requiredModuleArgument.IsLiteral = false; - arguments[PSRequiredModules] = requiredModuleArgument; - } - } - - // Copy the parameters - if (parameters != null) - { - foreach (string key in parameters.Keys) - { - arguments.Add(key, parameters[key]); - } - } - - GenerateActivityCall(null, "InlineScript", typeof(InlineScript), null, arguments, null, null, extent); - } - - private string GetModuleQualifier(string command) - { - if (string.IsNullOrEmpty(command)) - return null; - - int pos = command.IndexOf('\\'); - - if (pos < 0) - return null; - - System.Management.Automation.Language.Token[] tokens = null; - System.Management.Automation.Language.ParseError[] errors = null; - - System.Management.Automation.Language.Parser.ParseInput(command, out tokens, out errors); - if (tokens == null || errors == null || tokens.Length == 0 || errors.Length > 0) - return null; - - command = tokens[0].Text; - pos = command.IndexOf('\\'); - - if (pos > 0 && pos < command.Length - 1) - return command.Substring(0, pos); - - return null; - } - - private void SetVariableArgumentValue(CommandArgumentInfo argument, ref object argumentValue) - { - System.Diagnostics.Debug.Assert(argumentValue != null, "argumentValue should not be null"); - - if (argument != null) - { - argumentValue = "$" + argument.OriginalValue; - } - else if (argumentValue is CommandElementAst) - { - var cmdElement = argumentValue as CommandElementAst; - argumentValue = "$" + cmdElement; - } - } - - private void GenerateActivityCall( - CommandInfo commandInfo, - string invocationName, - Type activityType, - DynamicActivity activityInstance, - Dictionary arguments, - StaticBindingResult argumentResolution, - StaticBindingResult syntacticResolution, - IScriptExtent extent - ) - { - bool isNestedWorkflow = activityInstance != null; - - // If we're in a pipeline, ensure that this activity derives from PipelineEnabledActivity - if (this.isVisitingPipeline) - { - if ((activityType != null) && (! typeof(PipelineEnabledActivity).IsAssignableFrom(activityType))) - { - string activityName = extent.ToString(); - if (commandInfo != null) - { - activityName = commandInfo.Name; - } - - ReportError("ActivityNotSupportedInPipeline", - String.Format(CultureInfo.InvariantCulture, ActivityResources.ActivityNotSupportedInPipeline, activityName), extent); - } - } - - // Generate symbolic information for this activity call. - if (!String.Equals("SetPSWorkflowData", invocationName, StringComparison.OrdinalIgnoreCase)) - { - GenerateSymbolicInformation(extent); - } - - if ((arguments != null) && arguments.ContainsKey("Result")) - { - if(! this.ValidateOnly) - { - ReportError("CannotSpecifyResultArgument", ActivityResources.CannotSpecifyResultArgument, extent); - } - } - - string attributes = string.Empty; - if (string.Equals("InlineScript", invocationName)) - { - if (arguments.ContainsKey("Command")) - { - CommandArgumentInfo argument = arguments["Command"]; - arguments.Remove("Command"); - string argumentValue = argument.Value.ToString(); - if (argumentValue == null) - { - argumentValue = string.Empty; - } - attributes = " Command=\"" + - EncodeStringArgument(argumentValue, false) + - "\""; - } - } - - Dictionary propertiesToEmulate = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // Check if the error stream is redirected for the current command - if (this.mergeErrorToOutput) - { - // Check if the dynamic activity instance contains the properties we need - Type parameterType = null; - if (!IsParameterAvailable(activityType, activityInstance, "MergeErrorToOutput", false, extent, out parameterType)) - { - // If this is a workflow calling workflow, we'll emulate the property - if (isNestedWorkflow) - { - propertiesToEmulate.Add("MergeErrorToOutput", "[True]"); - } - else - { - // Don't report on parameter errors from nested workflows, as they require compilation - if (!this.ValidateOnly) - { - ReportError("CannotMergeErrorToOutput", - String.Format(CultureInfo.InvariantCulture, ActivityResources.CannotMergeErrorToOutput, invocationName), extent); - } - } - } - else - { - attributes += @" MergeErrorToOutput = ""True"""; - } - } - - // Process the generic activity type - Dictionary genericTypeMap = null; - if (activityType.IsGenericType && arguments.ContainsKey(GenericTypesKey)) - { - var genericTypes = (Type[]) arguments[GenericTypesKey].Value; - var concatenatedTypeNames = new StringBuilder(); - foreach (Type type in genericTypes) - { - string convertedTypeName = GetConvertedTypeName(type); - concatenatedTypeNames.Append(convertedTypeName); - concatenatedTypeNames.Append(", "); - } - concatenatedTypeNames.Remove(concatenatedTypeNames.Length - 2, 2); - attributes += String.Format(CultureInfo.InvariantCulture, @" x:TypeArguments=""{0}""", concatenatedTypeNames); - arguments.Remove(GenericTypesKey); - - genericTypeMap = GetGenericTypeMap(activityType, genericTypes); - } - - // Create an assembly name reference - string friendlyName = GetFriendlyName(invocationName, activityType); - - if ((commandInfo != null) && (invocationName == null)) - { - invocationName = commandInfo.Name; - } - - if (String.IsNullOrEmpty(invocationName)) - { - invocationName = extent.Text; - } - - // Generate the call to the activity - - // Capture the result if there is any output variable defined - var currentVariable = GetVariableToUse(); - string variableToUse = null; - bool isAggregatingVariable = false; - if (currentVariable != null) - { - variableToUse = currentVariable.VariableName; - isAggregatingVariable = currentVariable.IsAggregatingVariable; - } - VariableDefinition variableToUseDefinition = null; - - if (!String.IsNullOrEmpty(variableToUse)) - { - variableToUseDefinition = GetVariableDefinition(variableToUse); - variableToUseDefinition = variableToUseDefinition ?? members[variableToUse]; - } - - string appendOutput = String.Empty; - string actualAggregatingVariable = String.Empty; - bool needAggregationVariable = false; - bool needConversionVariable = false; - - if (!String.IsNullOrEmpty(variableToUse)) - { - if (activityType.Equals(typeof(System.Activities.Statements.Delay))) - { - if (!isAggregatingVariable) - { - ReportError("CannotAssignStartSleepToVariable", ActivityResources.CannotAssignStartSleepToVariable, extent); - } - else - { - // ignore the storagescope variable for start-sleep - attributes = null; - } - } - else - { - // Activities derived from Activity have two Result properties, one from the Activity - // the other from ActivityWithResult. So we cannot handle the property Result as a regular property. - Type[] genericArgumentTypes = null; - bool isActivityWithResult = activityType.IsGenericType && IsAssignableFromGenericType(typeof(Activity<>), activityType, out genericArgumentTypes); - Type activityOutputType = null; - - // Check if the dynamic activity instance contains the properties we need - Type parameterType = null; - if (!IsParameterAvailable(activityType, activityInstance, "Result", isActivityWithResult, extent, out parameterType)) - { - // If this is a workflow calling workflow, we'll emulate the property - if (isNestedWorkflow) - { - propertiesToEmulate.Add("Result", "[" + variableToUse + "]"); - } - else - { - if (!this.ValidateOnly) - { - ReportError("ActivityDoesNotContainResultProperty", - String.Format(CultureInfo.InvariantCulture, ActivityResources.ActivityDoesNotContainResultProperty, variableToUse, invocationName), extent); - } - else - { - return; - } - } - } - - // Get the activity output type - if (parameterType != null) - { - activityOutputType = GetActualPropertyType( - isActivityWithResult ? genericArgumentTypes[genericArgumentTypes.Length - 1] : parameterType, - genericTypeMap, "Result", extent); - } - - Type unused = null; - - // Check if we need to make a temporary variable for either aggregation, or conversion. - if (isAggregatingVariable && - !IsParameterAvailable(activityType, activityInstance, "AppendOutput", false, extent, out unused)) - { - needAggregationVariable = true; - } - if ((variableToUseDefinition.Type != activityOutputType) && (! isNestedWorkflow)) - { - needConversionVariable = true; - } - - // If we need a temporary variable, create it. - if (needAggregationVariable || needConversionVariable) - { - Type tempVarType = isNestedWorkflow - ? typeof(PSDataCollection) - : activityOutputType; - - // Define the temporary variable - string tempVarName = GenerateUniqueVariableName("__AggregatingTempVar"); - DefineVariable(tempVarName, tempVarType, extent, null); - - if (isNestedWorkflow) - { - // We are calling a nested workflow. we need to create a new instance of the storage variable. - try - { - EnterStorage(tempVarName, false); - string constructor = "New-Object -Type System.Management.Automation.PSDataCollection[PSObject]"; - GeneratePowerShellValue(typeof(System.Management.Automation.PSDataCollection), constructor, false, true); - } - finally - { - LeaveStorage(); - } - } - - actualAggregatingVariable = variableToUse; - variableToUse = tempVarName; - } - - if (isActivityWithResult) - { - appendOutput = String.Format(CultureInfo.InvariantCulture, " Result=\"[{0}]\"", variableToUse); - } - else - { - if (!propertiesToEmulate.ContainsKey("Result")) - { - arguments["Result"] = new CommandArgumentInfo() { Value = "$" + variableToUse }; - } - - if (isAggregatingVariable && !needAggregationVariable) { appendOutput = AppendOutputTemplate; } - } - } - } - - string getPSWorkflowDataFriendlyName = GetFriendlyName("GetPSWorkflowData", typeof(GetPSWorkflowData)); - string setPSWorkflowDataFriendlyName = GetFriendlyName("SetPSWorkflowData", typeof(SetPSWorkflowData)); - - // If we have any properties to emulate, save the old values and set the new ones - if (propertiesToEmulate.Count > 0) - { - WriteLine(""); - IndentLevel(); - WriteLine(""); - IndentLevel(); - WriteLine(""); - - IndentLevel(); - - // Prepare the variables - foreach (string propertyToEmulate in propertiesToEmulate.Keys) - { - // Create variables for the saved values if we need them - string variableName = "PSSaved_" + propertyToEmulate; - if (!VariableDefinedInCurrentScope(variableName)) - { - DefineVariable(variableName, typeof(Object), extent, null); - } - - // Save the existing variable, and set the new one. - WriteLine(@"<" + getPSWorkflowDataFriendlyName + @" x:TypeArguments=""x:Object"" OtherVariableName=""" + - propertyToEmulate + @""" Result=""[" + variableName + @"]"" VariableToRetrieve=""Other"" />"); - WriteLine(@"<" + setPSWorkflowDataFriendlyName + @" PSRemotingBehavior=""{x:Null}"" OtherVariableName=""" + - propertyToEmulate + @""" Value=""" + propertiesToEmulate[propertyToEmulate] + @""" />"); - } - } - - WriteLine("<" + friendlyName + attributes + appendOutput + ">"); - IndentLevel(); - - // Now process the arguments - if (arguments != null) - { - // Attempt to add any parameter defaults if they have been supplied from preference variables - UpdateArgumentsFromPreferenceVariables(activityType, arguments, activityInstance); - var boundParameters = new HashSet(StringComparer.OrdinalIgnoreCase); - - List argumentsToProcess = arguments.Keys.ToList(); - if((argumentResolution != null) && (argumentResolution.BindingExceptions != null)) - { - argumentsToProcess.AddRange(argumentResolution.BindingExceptions.Keys); - } - - foreach (string parameterNameIterator in argumentsToProcess) - { - string parameterName = parameterNameIterator; - - CommandArgumentInfo argument = null; - object argumentValue = null; - bool isLiteral = false; - bool wasSpecifiedAsSwitch = false; - - if (arguments.ContainsKey(parameterName)) - { - argument = arguments[parameterName]; - argumentValue = argument.Value; - isLiteral = argument.IsLiteral; - - // Detect a boolean that was used like a switch - if ((syntacticResolution != null) && - ((string)argumentValue == "-" + parameterName) && - (syntacticResolution.BoundParameters[parameterName].ConstantValue is bool)) - { - argument = null; - argumentValue = syntacticResolution.BoundParameters[parameterName].ConstantValue; - isLiteral = true; - wasSpecifiedAsSwitch = true; - } - } - else if (syntacticResolution != null) - { - // This was not bound successfully, look in the syntactic - // version of the bound parameters. - argumentValue = syntacticResolution.BoundParameters[parameterName].Value; - isLiteral = (syntacticResolution.BoundParameters[parameterName].ConstantValue != null); - } - - // If this is 'PipelineVariable', ignore this argument as it is implemented - // by workflow compilation itself. - if (String.Equals("PipelineVariable", parameterName, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - // If the argument value is NULL, then this was a switch parameter and - // we change it to $true - if (argumentValue == null) - { - argumentValue = "$true"; - } - - // If this is ErrorVariable or WarningVariable, switch it to PSError and PSWarning, - // respectively - if (String.Equals("ErrorVariable", parameterName, StringComparison.OrdinalIgnoreCase) || - String.Equals("EV", parameterName, StringComparison.OrdinalIgnoreCase)) - - { - parameterName = "PSError"; - SetVariableArgumentValue(argument, ref argumentValue); - } - - if (String.Equals("WarningVariable", parameterName, StringComparison.OrdinalIgnoreCase) || - String.Equals("WV", parameterName, StringComparison.OrdinalIgnoreCase)) - { - parameterName = "PSWarning"; - SetVariableArgumentValue(argument, ref argumentValue); - isLiteral = true; - } - - if (String.Equals("InformationVariable", parameterName, StringComparison.OrdinalIgnoreCase) || - String.Equals("IV", parameterName, StringComparison.OrdinalIgnoreCase)) - { - parameterName = "PSInformation"; - SetVariableArgumentValue(argument, ref argumentValue); - isLiteral = true; - } - - Type propertyType = GetPropertyType(activityType, activityInstance, ref parameterName, extent); - - // Flatten Nullable arguments down to their base - if ((propertyType != null) && propertyType.IsGenericType && (propertyType.GetGenericTypeDefinition().IsAssignableFrom(typeof(Nullable<>)))) - { - propertyType = propertyType.GetGenericArguments()[0]; - } - - // If we can't find the property name, we may have done parameter avoidance and renamed the - // activity's parameter so that it didn't conflict with something in workflow. Try to - // undo it and try again. - if ((invocationName != null) && (propertyType == null)) - { - // If it has a noun, trim it to include only the noun - string avoidanceQualifier = invocationName; - int nounIndex = avoidanceQualifier.IndexOf('-'); - if (nounIndex >= 0) - { - avoidanceQualifier = avoidanceQualifier.Substring(nounIndex + 1); - } - - // Try again - parameterName = avoidanceQualifier + parameterNameIterator; - propertyType = GetPropertyType(activityType, activityInstance, ref parameterName, extent); - } - - // If we couldn't find the parameter, generate an error. - if (propertyType == null) - { - if ((invocationName != null) && (argumentResolution != null) && argumentResolution.BoundParameters.ContainsKey(parameterName)) - { - StaticBindingError bindingException = argumentResolution.BindingExceptions[parameterName]; - ReportError("CannotFindParameter", bindingException.BindingException.Message, extent); - return; - } - else - { - Dictionary availableProperties = GetAvailableProperties(activityType, activityInstance); - - // Check if this is "Workflows calling workflows" - if ((activityInstance != null) && (parameterNameIterator.StartsWith("PS", StringComparison.OrdinalIgnoreCase))) - { - // Don't report on parameter errors from nested workflows, as they require compilation - if (!this.ValidateOnly) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.CouldNotFindParameterNameNested, parameterNameIterator); - ReportError("CouldNotFindWorkflowCommonParameterNameNested", error, extent); - } - - return; - } - else if (IsNotSupportedCommonParameter(parameterNameIterator)) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.CommonParameterNotSupported, parameterNameIterator, invocationName); - ReportError("CommonParameterNotSupported", error, extent); - return; - } - else if (String.Equals("ComputerName", parameterNameIterator, StringComparison.OrdinalIgnoreCase) && - availableProperties.ContainsKey("PSComputerName")) - { - ReportError("RemotingHandledByPSComputerName", ActivityResources.RemotingHandledByPSComputerName, extent); - return; - } - else - { - // They've typed a parameter that we can't map. Since there is no "Get-Command -Syntax" option, dump out all - // of the properties for them. - List completeProperties = new List(availableProperties.Keys); - - // If this is a workflow calling workflow, add in the virtual properties that don't exist, since they may not - // have been actually compiled (if they were not used). - foreach(PropertyInfo psRemotingProperty in typeof(PSRemotingActivity).GetProperties()) - { - if (typeof(Argument).IsAssignableFrom(psRemotingProperty.PropertyType)) - { - if (!completeProperties.Contains(psRemotingProperty.Name, StringComparer.OrdinalIgnoreCase)) - { - completeProperties.Add(psRemotingProperty.Name); - } - } - } - - string[] sortedParameters = completeProperties.ToArray(); - Array.Sort(sortedParameters); - - string supportedParametersString = String.Join(", ", sortedParameters); - - // Check if this is one of our activities that shadow a cmdlet name - List shadowedCmdlets = new List() { "Invoke-Command" }; - if (shadowedCmdlets.Contains(invocationName, StringComparer.OrdinalIgnoreCase)) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.CouldNotFindParameterNameShadowedActivity, parameterNameIterator, supportedParametersString); - ReportError("CouldNotFindParameterNameShadowedActivity", error, extent); - return; - } - else - { - // Don't report on parameter errors from nested workflows, as they require compilation - if (!this.ValidateOnly) - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.CouldNotFindParameterName, parameterNameIterator, supportedParametersString); - ReportError("CouldNotFindParameterName", error, extent); - } - - return; - } - } - } - } - - // Check if multiple specified parameters can be resolved to the same actual parameter - if (!boundParameters.Contains(parameterName)) - { - boundParameters.Add(parameterName); - } - else - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.DuplicateParametersNotAllowed, parameterName); - ReportError("DuplicateParametersNotAllowed", error, argument.ArgumentAst != null ? argument.ArgumentAst.Extent : extent); - } - - Type actualPropertyType = GetActualPropertyType(propertyType, genericTypeMap, parameterName, extent); - - // Detect calls to non-boolean properties that were specified like a switch - if ((actualPropertyType != typeof(bool)) && (actualPropertyType != typeof(SwitchParameter))) - { - if (wasSpecifiedAsSwitch) - { - string errorMsg = String.Format(CultureInfo.InvariantCulture, ActivityResources.MissingValueForParameter, parameterName); - ReportError("MissingValueForParameter", errorMsg, extent); - } - } - - ScriptBlockExpressionAst argumentValueAsScriptBlock = null; - if (argument != null) - { - argumentValueAsScriptBlock = argument.ArgumentAst as ScriptBlockExpressionAst; - } - - // See if this is a script block being bound to an activity - this is a container activity. - if( - argumentValueAsScriptBlock != null && - ( - typeof(Activity).IsAssignableFrom(propertyType) || - typeof(Activity[]).IsAssignableFrom(propertyType) || - (propertyType.IsGenericType && (typeof(Activity).IsAssignableFrom(propertyType.GetGenericArguments()[0]))) - )) - { - bool previousDisableSymbolGeneration = this.disableSymbolGeneration; - - WriteLine("<" + friendlyName + "." + parameterName + ">"); - IndentLevel(); - - WriteLine(""); - IndentLevel(); - - try - { - this.disableSymbolGeneration = true; - - foreach (StatementAst statement in argumentValueAsScriptBlock.ScriptBlock.EndBlock.Statements) - { - statement.Visit(this); - } - } - finally - { - this.disableSymbolGeneration = previousDisableSymbolGeneration; - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - } - } - // If the property type is not generic, then we have to in-line the string representation - // Or, if the incoming argument is of the exact type, and it's not a string (because then it could contain variable - // references etc), then use its string representation - else if ((!propertyType.IsGenericType) || - ((!ContainsLanguageElements(argumentValue.ToString())) && - (actualPropertyType == argumentValue.GetType()))) - { - string valueToUse = argumentValue.ToString(); - if ((argument != null) && (argument.OriginalValue != null)) - { - valueToUse = argument.OriginalValue.ToString(); - } - - if (ContainsLanguageElements(argumentValue.ToString())) - { - string convertedText = GetEquivalentVBTextForLiteralValue(actualPropertyType, valueToUse); - if (String.IsNullOrEmpty(convertedText)) - { - // If this is a direct property assignment, ensure it doesn't contain any PowerShell language - // elements. - - IScriptExtent errorExtent = null; - Ast argumentAst = argumentValue as Ast; - if (argumentAst != null) - { - errorExtent = argumentAst.Extent; - } - else - { - errorExtent = argument.ArgumentAst.Extent; - } - - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.PropertyDoesNotSupportPowerShellLanguage, parameterNameIterator); - ReportError("PropertyDoesNotSupportPowerShellLanguage", error, errorExtent); - } - - valueToUse = convertedText; - } - else - { - // This will be injected directly into the XAML - escape it - if (propertyType.IsGenericType) - { - valueToUse = EncodeStringArgument(valueToUse, false); - } - else - { - valueToUse = EncodeStringArgumentLiteral(valueToUse, false); - } - } - - // In the XAML processing sequence, provided values from a markup extension do not invoke additional value conversion. (http://msdn.microsoft.com/en-us/library/ms742135.aspx) - // So we use the markup extension to represent an empty string only if the property type is exactly System.String - if (String.Empty.Equals(valueToUse)) - { - if (propertyType.Equals(typeof(string))) - { - valueToUse = ""; - } - else - { - valueToUse = "[\"\"]"; - } - } - - WriteLine("<" + friendlyName + "." + parameterName + ">" + valueToUse + ""); - } - else - { - string argumentValueString = argumentValue.ToString(); - string variableName = argumentValueString.Substring(1); - - // If this is is an OutArgument or InOutArgument, they must supply a variable name. - // If they've supplied a variable name and the type matches, use that directly. - if ((propertyType.BaseType != null) && - (typeof(InOutArgument).IsAssignableFrom(propertyType.BaseType) || - typeof(OutArgument).IsAssignableFrom(propertyType.BaseType))) - { - if (argumentValueString.StartsWith("$", StringComparison.OrdinalIgnoreCase)) - { - if(! (VariableDefined(variableName) || members.ContainsKey(variableName))) - { - DefineVariable(variableName, actualPropertyType, extent, null); - } - - WriteLine("<" + friendlyName + "." + parameterName + ">[" + variableName + "]"); - } - else - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.MustSupplyVariableReferenceForInOutArgument, parameterName); - ReportError("MustSupplyVariableReferenceForInOutArgument", error, extent); - } - } - else - { - // Otherwise, go through PowerShellValue to resolve variables, expressions, etc. - string propertyFriendlyName = GetConvertedTypeName(actualPropertyType); - - WriteLine("<" + friendlyName + "." + parameterName + ">"); - IndentLevel(); - - String argumentLine = String.Format(CultureInfo.InvariantCulture, - @"<{0} x:TypeArguments=""{1}"">", - GetFriendlyName(null, propertyType), - propertyFriendlyName); - WriteLine(argumentLine); - IndentLevel(); - - // Now make the call - string expression = argumentValue.ToString(); - if (argumentValue is Ast) - { - expression = GetPowerShellValueExpression((Ast)argumentValue); - } - GeneratePowerShellValue(actualPropertyType, expression, isLiteral, false); - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - } - } - } - } - - // Close the activity call - UnindentLevel(); - WriteLine(""); - - // If we had any properties to emulate, restore the old values - if (propertiesToEmulate.Count > 0) - { - string exceptionFriendlyName = GetFriendlyName(null, typeof(Exception)); - // Restore the variables if there was no error - foreach(string propertyToEmulate in propertiesToEmulate.Keys) - { - // Variable names from the saved values if we need them - string variableName = "PSSaved_" + propertyToEmulate; - - WriteLine(@"<" + setPSWorkflowDataFriendlyName + @" PSRemotingBehavior=""{x:Null}"" OtherVariableName=""" + - propertyToEmulate + @""" Value=""[" + variableName + @"]"" />"); - } - - // Close the sequence and Try / Catch - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(""); - WriteLine(""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(""); - IndentLevel(); - - // Restore the variables in the Catch - foreach(string propertyToEmulate in propertiesToEmulate.Keys) - { - // Variable names from the saved values if we need them - string variableName = "PSSaved_" + propertyToEmulate; - - WriteLine(@"<" + setPSWorkflowDataFriendlyName + @" PSRemotingBehavior=""{x:Null}"" OtherVariableName=""" + - propertyToEmulate + @""" Value=""[" + variableName + @"]"" />"); - } - - WriteLine(""); - UnindentLevel(); - - WriteLine(""); - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(""); - - } - - // If we created an aggregation variable, emit its results into the actual storage variable - if (needAggregationVariable) - { - string writeOutputFriendlyName = GetFriendlyName(null, typeof(Microsoft.PowerShell.Utility.Activities.WriteOutput)); - GenerateWriteOutputStart(writeOutputFriendlyName, actualAggregatingVariable, extent); - - GeneratePowerShellValue(typeof(PSObject[]), "$" + variableToUse, false, false); - GenerateWriteOutputEnd(writeOutputFriendlyName); - } - - // If we needed a conversion variable, use PowerShellValue to do the conversion - if (needConversionVariable && (! needAggregationVariable)) - { - // We rewrite an assignment such as: "$x += 1" to "$x = $x + 1; $x" so that - // PowerShell returns the new value after assignment. - string assignmentExpression = null; - - if (isAggregatingVariable) - { - assignmentExpression = "$" + actualAggregatingVariable + " + $" + variableToUse; - } - else - { - assignmentExpression = "$" + variableToUse; - } - - GeneratePowerShellValue(variableToUseDefinition.Type, assignmentExpression, false, actualAggregatingVariable); - } - } - - // Map the generic argument type names to their actual types - // For example: - // input: List --- output: {T, string} - private static Dictionary GetGenericTypeMap(Type activityType, Type[] genericTypeArray) - { - if (!activityType.IsGenericType) - { - return null; - } - - var genericTypeMap = new Dictionary(StringComparer.OrdinalIgnoreCase); - var activityTypeDefinition = activityType.GetGenericTypeDefinition(); - var genericParamTypes = activityTypeDefinition.GetGenericArguments(); - - for (int i = 0; i < genericParamTypes.Length; i++) - { - genericTypeMap.Add(genericParamTypes[i].Name, genericTypeArray[i]); - } - - return genericTypeMap; - } - - private Type GetActualPropertyType(Type rawPropertyType, Dictionary genericTypeMap, string parameterName, IScriptExtent extent) - { - Type[] unusedGenericArgumentTypes; - bool isActivityArgument = - IsAssignableFromGenericType(typeof (InArgument<>), rawPropertyType, out unusedGenericArgumentTypes) || - IsAssignableFromGenericType(typeof (InOutArgument<>), rawPropertyType, out unusedGenericArgumentTypes) || - IsAssignableFromGenericType(typeof (OutArgument<>), rawPropertyType, out unusedGenericArgumentTypes); - - Type actualPropertyType = isActivityArgument ? rawPropertyType.GetGenericArguments()[0] : rawPropertyType; - - if (actualPropertyType.IsGenericParameter) - { - // If it's in process of the compilation - // --- 'ResolveGenericParameterType' throws if we cannot find the actual type of the specified generic parameter - // If it's only for validation - // --- 'ResolveGenericParameterType' returns null if we cannot find the actual type of the specified generic parameter, - // in that case, we use the "actualPropertyType" directly - Type parameterType = ResolveGenericParameterType(actualPropertyType, genericTypeMap, parameterName, extent); - actualPropertyType = !this.validateOnly - ? parameterType - : (parameterType ?? actualPropertyType); - } - else if (actualPropertyType.IsGenericType && actualPropertyType.ContainsGenericParameters) - { - // If it's in process of the compilation - // --- 'ResolveTypeWithGenericParameters' throws if we cannot resolve the specified generic type - // If it's only for validation - // --- 'ResolveTypeWithGenericParameters' returns null if we cannot resolve the specified generic type, - // in that case, we use the "actualPropertyType" directly - Type resolvedPropertyType = ResolveTypeWithGenericParameters(actualPropertyType, genericTypeMap, parameterName, extent); - actualPropertyType = !this.validateOnly - ? resolvedPropertyType - : (resolvedPropertyType ?? actualPropertyType); - } - - return actualPropertyType; - } - - // Return the actual type for the generic parameter. - // If "this.ValidateOnly == true", this method returns null when no actual type can be found for the generic parameter - // If "this.ValidateOnly == false", this method throws when no actual type can be found for the generic parameter - private Type ResolveGenericParameterType(Type genericParameter, Dictionary genericTypeMap, string parameterName, IScriptExtent extent) - { - Type actualParameterType = null; - if (genericTypeMap == null || !genericTypeMap.TryGetValue(genericParameter.Name, out actualParameterType)) - { - string errorMsg = String.Format(CultureInfo.InvariantCulture, ActivityResources.GenericParameterTypeNotFound, genericParameter.Name, parameterName); - ReportError("GenericParameterTypeNotFound", errorMsg, extent); - } - - return actualParameterType; - } - - // Resolve the specified generic type that contains generic parameters - // If "this.ValidateOnly == true", this method returns null when the resolution fails - // If "this.ValidateOnly == false", this method throws when the resolution fails - private Type ResolveTypeWithGenericParameters(Type genericPropertyType, Dictionary genericTypeMap, string parameterName, IScriptExtent extent) - { - Type[] genericArguments = genericPropertyType.GetGenericArguments(); - var resolvedArgumentTypes = new List(genericArguments.Length); - - foreach (Type argument in genericArguments) - { - if (argument.IsGenericParameter) - { - Type resolvedParameterType = ResolveGenericParameterType(argument, genericTypeMap, parameterName, extent); - // It's for validation only, and we cannot find the actual type of the specified generic parameter - if (resolvedParameterType == null) { return null; } - resolvedArgumentTypes.Add(resolvedParameterType); - } - else if (argument.IsGenericType && argument.ContainsGenericParameters) - { - Type resolvedGenericArgument = ResolveTypeWithGenericParameters(argument, genericTypeMap, parameterName, extent); - if (resolvedGenericArgument == null) { return null; } - resolvedArgumentTypes.Add(resolvedGenericArgument); - } - else - { - resolvedArgumentTypes.Add(argument); - } - } - - Type resolvedGenericPropertyType = null; - try - { - Type genericTypeDefinition = genericPropertyType.GetGenericTypeDefinition(); - resolvedGenericPropertyType = genericTypeDefinition.MakeGenericType(resolvedArgumentTypes.ToArray()); - } - catch - { - string errorMsg = String.Format(CultureInfo.InvariantCulture, ActivityResources.GenericPropertyTypeNotResolved, genericPropertyType.Name, parameterName); - ReportError("GenericParameterTypeNotFound", errorMsg, extent); - } - - return resolvedGenericPropertyType; - } - - private bool IsParameterAvailable(Type activityType, DynamicActivity activityInstance, string parameterName, bool ignoreAmbiguousMatch, IScriptExtent extent, out Type propertyType) - { - propertyType = null; - try - { - propertyType = GetPropertyType(activityType, activityInstance, ref parameterName, extent); - return propertyType != null; - } - catch (AmbiguousMatchException) - { - // ignore the AmbiguousMatchException. The Activity has two Result parameters. - if (ignoreAmbiguousMatch) - { - return true; - } - - string errorMsg = String.Format(CultureInfo.InvariantCulture, ActivityResources.AmbiguousPropertiesFound, parameterName); - ReportError("AmbiguousParametersFound", errorMsg, extent); - return false; - } - } - - private static bool IsAssignableFromGenericType(Type parentType, Type fromType, out Type[] genericArgumentTypes) - { - genericArgumentTypes = null; - if (!fromType.IsGenericType) { return false; } - - // Check the interfaces - if (CheckInterface(parentType, fromType, out genericArgumentTypes)) { return true; } - - // No match in interfaces, then we check the class hierarchy - return CheckClass(parentType, fromType, out genericArgumentTypes); - } - - private static bool CheckInterface(Type parentType, Type fromType, out Type[] genericArgumentTypes) - { - genericArgumentTypes = null; - var interfaceTypes = fromType.GetInterfaces(); - foreach (var it in interfaceTypes) - { - if (it.IsGenericType) - { - if (it.GetGenericTypeDefinition() == parentType) - { - genericArgumentTypes = it.GetGenericArguments(); - return true; - } - } - } - - return false; - } - - private static bool CheckClass(Type parentType, Type fromType, out Type[] genericArgumentTypes) - { - genericArgumentTypes = null; - if (fromType.GetGenericTypeDefinition() == parentType) - { - genericArgumentTypes = fromType.GetGenericArguments(); - return true; - } - - Type baseType = fromType.BaseType; - if (baseType == null || !baseType.IsGenericType) return false; - - return CheckClass(parentType, baseType, out genericArgumentTypes); - } - - private bool ContainsLanguageElements(string input) - { - if (String.IsNullOrEmpty(input)) - { - return false; - } - - if (input[0] == '\'' && input[input.Length - 1] == '\'') - { - return false; - } - - char[] languageElementIdentifiers = { '`', '$', '(', '@' }; - if (input.IndexOfAny(languageElementIdentifiers) >= 0) - { - return true; - } - - return false; - } - - private void GenerateSymbolicInformation(IScriptExtent extent) - { - // Generate the symbolic metadata for this command call - if (! this.disableSymbolGeneration) - { - string position = String.Format(CultureInfo.InvariantCulture, "{0}:{1}:{2}", extent.StartLineNumber, extent.StartColumnNumber, this.name); - - // Stop storing results into the storage variable - Stack savedStorage = this.resultVariables; - bool savedMergeErrorToOutput = this.mergeErrorToOutput; - - try - { - this.resultVariables = null; - this.mergeErrorToOutput = false; - - Dictionary setHostValueArguments = new Dictionary(StringComparer.OrdinalIgnoreCase); - setHostValueArguments["OtherVariableName"] = new CommandArgumentInfo { Value = "Position", IsLiteral = true }; - setHostValueArguments["Value"] = new CommandArgumentInfo { Value = position, IsLiteral = true }; - - GenerateActivityCall(null, "SetPSWorkflowData", typeof(SetPSWorkflowData), null, setHostValueArguments, null, null, extent); - } - finally - { - this.resultVariables = savedStorage; - this.mergeErrorToOutput = savedMergeErrorToOutput; - } - } - } - - // The list of parameters that need to be ignored. - static List ignoredParameters; - - private static bool IsNotSupportedCommonParameter(string parameterName) - { - return ignoredParameters.Contains(parameterName, StringComparer.OrdinalIgnoreCase); - } - - private void UpdateArgumentsFromPreferenceVariables(Type activityType, Dictionary arguments, DynamicActivity activityInstance) - { - string preferenceVariable = null; - - if (typeof(PSActivity).IsAssignableFrom(activityType)) - { - // Process any of the ubiquitous parameters for PSActivity - - preferenceVariable = "DebugPreference"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable))) - { - SetDebugPreferenceArg(arguments, preferenceVariable); - } - - preferenceVariable = "ErrorActionPreference"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable))) - { - SetErrorActionPreferenceArg(arguments, preferenceVariable); - } - - preferenceVariable = "VerbosePreference"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable))) - { - SetVerbosePreferenceArg(arguments, preferenceVariable); - } - - preferenceVariable = "WarningPreference"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable))) - { - SetWarningActionPreferenceArg(arguments, preferenceVariable); - } - - preferenceVariable = "InformationPreference"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable))) - { - SetInformationActionPreferenceArg(arguments, preferenceVariable); - } - - preferenceVariable = "WhatIfPreference"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable))) - { - CommandArgumentInfo preferenceVariableInfo = new CommandArgumentInfo(); - preferenceVariableInfo.Value = "$" + preferenceVariable; - arguments["WhatIf"] = preferenceVariableInfo; - } - } - else if (typeof(DynamicActivity).IsAssignableFrom(activityType) && (activityInstance != null)) - { - // Process ubiquitous parameters for DynamicActivity if supported. - - preferenceVariable = "VerbosePreference"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable)) && activityInstance.Properties.Contains("Verbose")) - { - SetVerbosePreferenceArg(arguments, preferenceVariable); - } - - preferenceVariable = "DebugPreference"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable)) && activityInstance.Properties.Contains("Debug")) - { - SetDebugPreferenceArg(arguments, preferenceVariable); - } - - preferenceVariable = "ErrorActionPreference"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable)) && activityInstance.Properties.Contains("ErrorAction")) - { - SetErrorActionPreferenceArg(arguments, preferenceVariable); - } - - preferenceVariable = "WarningPreference"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable)) && activityInstance.Properties.Contains("WarningAction")) - { - SetWarningActionPreferenceArg(arguments, preferenceVariable); - } - - preferenceVariable = "InformationPreference"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable)) && activityInstance.Properties.Contains("InformationAction")) - { - SetInformationActionPreferenceArg(arguments, preferenceVariable); - } - } - - // Process any of the ubiquitous parameters for PSRemotingActivity - if (typeof(PSRemotingActivity).IsAssignableFrom(activityType)) - { - // PSSessionApplicationName - preferenceVariable = "PSSessionApplicationName"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable))) - { - CommandArgumentInfo preferenceVariableInfo = new CommandArgumentInfo(); - preferenceVariableInfo.Value = "$" + preferenceVariable; - arguments["PSApplicationName"] = preferenceVariableInfo; - } - - // PSSessionConfigurationName - preferenceVariable = "PSSessionConfigurationName"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable))) - { - CommandArgumentInfo preferenceVariableInfo = new CommandArgumentInfo(); - preferenceVariableInfo.Value = "$" + preferenceVariable; - arguments["PSConfigurationName"] = preferenceVariableInfo; - } - - // PSSessionOption - preferenceVariable = "PSSessionOption"; - if (VariableDefined(preferenceVariable) && (!arguments.ContainsKey(preferenceVariable))) - { - CommandArgumentInfo preferenceVariableInfo = new CommandArgumentInfo(); - preferenceVariableInfo.Value = "$" + preferenceVariable; - arguments["PSSessionOption"] = preferenceVariableInfo; - } - } - } - - private static void SetVerbosePreferenceArg(Dictionary arguments, string preferenceVariable) - { - CommandArgumentInfo preferenceVariableInfo = new CommandArgumentInfo(); - preferenceVariableInfo.Value = "if($" + preferenceVariable + " -eq 'Continue') { $true } else { $false }"; - arguments["Verbose"] = preferenceVariableInfo; - } - - private static void SetDebugPreferenceArg(Dictionary arguments, string preferenceVariable) - { - CommandArgumentInfo preferenceVariableInfo = new CommandArgumentInfo(); - preferenceVariableInfo.Value = "if($" + preferenceVariable + " -eq 'Continue') { $true } else { $false }"; - arguments["Debug"] = preferenceVariableInfo; - } - - private static void SetErrorActionPreferenceArg(Dictionary arguments, string preferenceVariable) - { - SetSimpleActionPreferenceArg("ErrorAction", arguments, preferenceVariable); - } - - private static void SetWarningActionPreferenceArg(Dictionary arguments, string preferenceVariable) - { - SetSimpleActionPreferenceArg("WarningAction", arguments, preferenceVariable); - } - - private static void SetInformationActionPreferenceArg(Dictionary arguments, string preferenceVariable) - { - SetSimpleActionPreferenceArg("InformationAction", arguments, preferenceVariable); - } - - private static void SetSimpleActionPreferenceArg(string preference, Dictionary arguments, string preferenceVariable) - { - CommandArgumentInfo preferenceVariableInfo = new CommandArgumentInfo(); - preferenceVariableInfo.Value = "$" + preferenceVariable; - arguments[preference] = preferenceVariableInfo; - } - - - private Type GetPropertyType(Type activityType, DynamicActivity activityInstance, ref string parameterName, IScriptExtent extent) - { - Dictionary mergedProperties = GetAvailableProperties(activityType, activityInstance); - Type resultType = null; - - // Map their case-insensitive property lookup to the actual property - // name, and do minimal substring matching. - List parameterMatches = new List(); - foreach(string propertyName in mergedProperties.Keys) - { - // Check if it is an exact match. If so, return immediately. - if(String.Equals(propertyName, parameterName, StringComparison.OrdinalIgnoreCase)) - { - resultType = mergedProperties[propertyName]; - parameterName = propertyName; - return resultType; - } - - // Check if it is a substring match. If so, remember which parameters have - // matched and store the result type of the last one. - if(propertyName.StartsWith(parameterName, StringComparison.OrdinalIgnoreCase)) - { - resultType = mergedProperties[propertyName]; - parameterMatches.Add("-" + propertyName); - } - } - - // Verify there were not multiple substring matches - if (parameterMatches.Count > 1) - { - string combinedMatches = String.Join(" ", parameterMatches); - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.AmbiguousParameter, parameterName, combinedMatches); - ReportError("AmbiguousParameter", error, extent); - } - else if(parameterMatches.Count == 1) - { - parameterName = parameterMatches[0].Substring(1); - } - - return resultType; - } - - internal static Dictionary GetAvailableProperties(Type activityType, DynamicActivity activityInstance) - { - Dictionary mergedProperties = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // If this is a dynamic activity, pull the info from activityInstance - if (activityInstance != null) - { - for (int index = 0; index < activityInstance.Properties.Count; index++) - { - string propertyName = activityInstance.Properties[index].Name; - Type propertyType = activityInstance.Properties[index].Type; - mergedProperties[propertyName] = propertyType; - } - } - else - { - // This is a concrete activity - foreach (PropertyInfo property in activityType.GetProperties(BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance)) - { - mergedProperties[property.Name] = property.PropertyType; - } - } - - // Remove the "ID" property, as it is in the Activity base class and will be avoided - // by cmdlets - if (mergedProperties.ContainsKey("Id")) - { - mergedProperties.Remove("Id"); - } - - return mergedProperties; - } - - // Gets properties understood / handled by workflow compilation but not by the activity itself - internal static Dictionary GetVirtualProperties(Type activityType, DynamicActivity activityInstance) - { - if (String.Equals(activityType.FullName, "Microsoft.PowerShell.Core.Activities.ForEachObject", StringComparison.OrdinalIgnoreCase)) - { - return new Dictionary { { "PipelineVariable", typeof(string) } }; - } - else - { - return null; - } - } - - /// - /// Utility to encode an expression so it can be inserted in the generated XAML. - /// - /// The expression to encode - /// - /// If true, the string should be wrapped in quotes to it works - /// like a literal expression. - /// - /// - private string EncodeStringArgument(string bareContent, bool isLiteral) - { - // First, do XAML encoding - bareContent = EncodeStringNonArgument(bareContent, isLiteral); - - // Since this is an argument, escape content (i.e.: '[') to prevent - // the expression from looking like a VB value - var literal = new System.Activities.Expressions.Literal(bareContent); - string literalEncoded = literal.ConvertToString(null); - bareContent = literalEncoded; - - return bareContent; - } - - // Encodes a string so that it can be used directly in XAML. - private string EncodeStringNonArgument(string bareContent, bool isLiteral) - { - bareContent = EncodeStringArgumentLiteral(bareContent, isLiteral); - - // Escape markup extensions - if (bareContent.StartsWith("{", StringComparison.OrdinalIgnoreCase)) - { - bareContent = "{}" + bareContent; - } - - return bareContent; - } - - // Encodes a string so that it can be used directly in XAML. - private string EncodeStringArgumentLiteral(string bareContent, bool isLiteral) - { - StringBuilder encodedString = new StringBuilder(bareContent.Length); - System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings(); - settings.ConformanceLevel = System.Xml.ConformanceLevel.Fragment; - using (var writer = System.Xml.XmlWriter.Create(encodedString, settings)) - { - writer.WriteString(bareContent); - } - - bareContent = encodedString.ToString(); - - bareContent = bareContent.Replace("\r", " "); - bareContent = bareContent.Replace("\n", " "); - bareContent = bareContent.Replace(@"""", """); - - // If the target to assign to is an argument, it expects - // an expression so re-write - // hello - // as - // 'hello' - if (isLiteral) - { - bareContent = bareContent.Replace("'", "''"); - bareContent = "'" + bareContent + "'"; - } - - return bareContent; - } - - private Dictionary GetAndResolveParameters(CommandAst commandAst, bool searchSessionState) - { - StaticBindingResult bindingResult = StaticParameterBinder.BindCommand(commandAst, searchSessionState); - Dictionary convertedParameters = ResolveParameters(bindingResult); - - return convertedParameters; - } - - private Dictionary ResolveParameters(StaticBindingResult parameters) - { - Dictionary convertedParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (string parameterName in parameters.BoundParameters.Keys) - { - CommandArgumentInfo argumentInfo = new CommandArgumentInfo(); - argumentInfo.Value = ProcessCommandArgument(parameters.BoundParameters[parameterName].Value); - argumentInfo.ArgumentAst = parameters.BoundParameters[parameterName].Value; - - if(argumentInfo.Value != null) - { - StringConstantExpressionAst stringAst = parameters.BoundParameters[parameterName].Value as StringConstantExpressionAst; - if (stringAst != null) - { - argumentInfo.OriginalValue = stringAst.Value; - } - else - { - argumentInfo.OriginalValue = parameters.BoundParameters[parameterName].Value.Extent.Text; - } - } - convertedParameters.Add(parameterName, argumentInfo); - } - - return convertedParameters; - } - - object ICustomAstVisitor.VisitCommandExpression(CommandExpressionAst commandExpressionAst) - { - // If this is a unary expression, it will update the variable in-place. Generate an assignment - // statement. - if (commandExpressionAst.Expression is UnaryExpressionAst) - { - return commandExpressionAst.Expression.Visit(this); - } - - // If it is a parenthesized expression, visit what's in the parens - ParenExpressionAst parenExpression = commandExpressionAst.Expression as ParenExpressionAst; - if (parenExpression != null) - { - return parenExpression.Pipeline.Visit(this); - } - - // If this is a method call, throw an error that they should use Inline Script instead. - if (commandExpressionAst.Expression is InvokeMemberExpressionAst) - { - ReportError("MethodInvocationNotSupported", ActivityResources.MethodInvocationNotSupported, commandExpressionAst.Expression.Extent); - } - - // If this is a sub expression, throw an error that they should use Inline Script instead. - if (commandExpressionAst.Expression is SubExpressionAst) - { - ReportError("SubExpressionNotSupported", ActivityResources.SubExpressionNotSupported, commandExpressionAst.Expression.Extent); - } - - // If this is an attributed expression, throw an error that they should use attribute expression only when declaring parameters for the script workflow - if ((commandExpressionAst.Expression is AttributedExpressionAst) && - (! (commandExpressionAst.Expression is ConvertExpressionAst))) - { - ReportError("AttributedExpressionNotSupported", ActivityResources.AttributedExpressionNotSupported, commandExpressionAst.Expression.Extent); - } - - if (commandExpressionAst.Redirections.Count > 0) - { - foreach (RedirectionAst redirection in commandExpressionAst.Redirections) - { - redirection.Visit(this); - } - - this.mergeErrorToOutput = true; - } - - // They've done a simple variable reference. - - // Ensure it has no side-effects, as we aren't capturing them. - string nameOfUnSupportedVariableFound = null; - if (CheckIfExpressionHasUnsupportedVariableOrHasSideEffects(null, commandExpressionAst, out nameOfUnSupportedVariableFound)) - { - if (string.IsNullOrEmpty(nameOfUnSupportedVariableFound)) - { - string errorTemplate = ActivityResources.CannotStoreResultsInUnsupportedElement; - ReportError("CannotStoreResultsInUnsupportedElement", - ActivityResources.CannotStoreResultsInUnsupportedElement, - commandExpressionAst.Expression.Extent); - } - else - { - string error = String.Format(CultureInfo.InvariantCulture, - ActivityResources.VariableNotSupportedInWorkflow, - nameOfUnSupportedVariableFound); - ReportError("VariableNotSupportedInWorkflow", error, commandExpressionAst.Expression.Extent); - } - } - - try - { - var currentVariable = GetVariableToUse(); - string variableToUse = null; - bool isAggregatingVariable = false; - if (currentVariable != null) - { - variableToUse = currentVariable.VariableName; - isAggregatingVariable = currentVariable.IsAggregatingVariable; - } - - // They're accessing the $input variable. Convert it to a simple call to Write-Output, which will - // pick up the input stream from the parameter defaults. - // We ignore error redirection in this case - VariableExpressionAst inputVariable = commandExpressionAst.Expression as VariableExpressionAst; - string writeOutputFriendlyName = GetFriendlyName(null, typeof(Microsoft.PowerShell.Utility.Activities.WriteOutput)); - if (inputVariable != null) - { - if (String.Equals("input", inputVariable.VariablePath.UserPath, StringComparison.OrdinalIgnoreCase)) - { - if (String.IsNullOrEmpty(variableToUse)) - { - WriteLine(String.Format(CultureInfo.InvariantCulture, "<{0} UseDefaultInput=\"true\" />", writeOutputFriendlyName)); - } - else - { - string appendOutput = isAggregatingVariable ? AppendOutputTemplate : string.Empty; - WriteLine(String.Format(CultureInfo.InvariantCulture, "<{0} UseDefaultInput=\"true\" Result = \"[{1}]\"{2} />", writeOutputFriendlyName, variableToUse, appendOutput)); - } - return null; - } - } - - // If we aren't storing an assignment, then add a call to Write-Output so that - // it can be sent to the output stream. - bool isOutputExpression = isAggregatingVariable || String.IsNullOrEmpty(variableToUse); - - // Generate the outer call to WriteOutput - Type variableType = null; - if (isOutputExpression) - { - // Otherwise, make the following value the input object of the - // Write-Output activity. - variableType = typeof(PSObject[]); - GenerateWriteOutputStart(writeOutputFriendlyName, variableToUse, commandExpressionAst.Extent); - } - else - { - variableType = GetVariableDefinition(variableToUse).Type; - } - - // Evaluate the expression - string expression = GetPowerShellValueExpression(commandExpressionAst.Expression); - GeneratePowerShellValue(variableType, expression, false, true); - - // Complete the outer call to WriteObject - if (isOutputExpression) - { - GenerateWriteOutputEnd(writeOutputFriendlyName); - } - } - finally - { - this.mergeErrorToOutput = false; - } - - return null; - } - - private void GenerateWriteOutputStart(string writeOutputFriendlyName, string storageScopeVariable, IScriptExtent extent) - { - GenerateSymbolicInformation(extent); - - Type inputType = typeof(PSObject[]); - string convertedTypeName = GetConvertedTypeName(inputType); - - if (String.IsNullOrEmpty(storageScopeVariable)) - { - WriteLine("<" + writeOutputFriendlyName + ">"); - } - else - { - WriteLine(String.Format(CultureInfo.InvariantCulture, "<{0} Result = \"[{1}]\"{2} >", writeOutputFriendlyName, storageScopeVariable, AppendOutputTemplate)); - } - IndentLevel(); - - WriteLine("<" + writeOutputFriendlyName + ".NoEnumerate>[System.Management.Automation.SwitchParameter.Present]"); - WriteLine("<" + writeOutputFriendlyName + ".InputObject>"); - IndentLevel(); - - // Assign the value to the InputObject argument - string template = @""; - WriteLine(String.Format(CultureInfo.InvariantCulture, template, convertedTypeName)); - IndentLevel(); - } - - private void GenerateWriteOutputEnd(string writeOutputFriendlyName) - { - UnindentLevel(); - - WriteLine(""); - UnindentLevel(); - - WriteLine(""); - UnindentLevel(); - - WriteLine(""); - } - - object ICustomAstVisitor.VisitCommandParameter(CommandParameterAst commandParameterAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitSwitchStatement(SwitchStatementAst switchStatementAst) - { - Type switchType = typeof(object); - - // Figure out the type of the switch statement by examining its expression (first), or - // its clauses (second). We need a strongly-typed switch statement, as workflow uses - // a hashtable lookup to determine condition hits. - ExpressionAst conditionExpressionAst = switchStatementAst.Condition.GetPureExpression(); - if (conditionExpressionAst != null) - { - switchType = conditionExpressionAst.StaticType; - } - - // Elevate ints to doubles so that the WF switch can handle the conversion - if ((switchType == typeof(int)) || - (switchType == typeof(Int32)) || - (switchType == typeof(UInt32))) - { - switchType = typeof(double); - } - - if ((switchType == typeof(object)) || - (!IsSupportedSwitchExpressionType(switchType)) - ) - { - switchType = null; - - foreach (Tuple switchClause in switchStatementAst.Clauses) - { - // Verify they've typed a literal (i.e.: integer, string) - ConstantExpressionAst key = switchClause.Item1 as ConstantExpressionAst; - if (key == null) - { - ReportError("SwitchOnlySupportsConstantExpression", ActivityResources.SwitchOnlySupportsConstantExpression, switchClause.Item1.Extent); - return null; - } - - if (switchType == null) - { - switchType = key.StaticType; - } - else - { - // Validate they only have one key type - if (key.StaticType != switchType) - { - ReportError("SwitchClauseMustBeOfSameType", ActivityResources.SwitchClauseMustBeOfSameType, key.Extent); - } - } - } - } - - if (switchType == null) - { - switchType = typeof(object); - } - - // Generic flag errors - if ( - ((switchStatementAst.Flags & SwitchFlags.Regex) == SwitchFlags.Regex) || - ((switchStatementAst.Flags & SwitchFlags.Wildcard) == SwitchFlags.Wildcard) || - ((switchStatementAst.Flags & SwitchFlags.File) == SwitchFlags.File) || - ((switchStatementAst.Flags & SwitchFlags.Parallel) == SwitchFlags.Parallel)) - { - ReportError("SwitchFlagNotSupported", ActivityResources.SwitchFlagNotSupported, switchStatementAst.Extent); - } - - // Validate any parameters to the switch statement - if ((switchType == typeof(string)) || (switchType == typeof(char))) - { - if ((switchStatementAst.Flags & SwitchFlags.CaseSensitive) != SwitchFlags.CaseSensitive) - { - ReportError("SwitchCaseSensitive", ActivityResources.SwitchCaseSensitive, switchStatementAst.Extent); - } - } - - GenerateSymbolicInformation(switchStatementAst.Extent); - - // Generate a temporary variable for the switch condition - string tempVarName = GenerateUniqueVariableName("SwitchCondition"); - Type conditionType = DetectType(tempVarName, false, switchStatementAst.Condition); - DefineVariable(tempVarName, conditionType, switchStatementAst.Condition.Extent, null); - - // Generate the assignment of the the clause to the temporary variable - string conditionExpression = GetPowerShellValueExpression(switchStatementAst.Condition); - GenerateAssignment(tempVarName, switchStatementAst.Condition.Extent, TokenKind.Equals, switchStatementAst.Condition, conditionExpression); - - - string switchTypeFriendlyName = GetConvertedTypeName(switchType); - WriteLine(@""); - IndentLevel(); - - WriteLine(""); - IndentLevel(); - - WriteLine(@""); - IndentLevel(); - - // Evaluate the switch expression - string errorMessage = ActivityResources.SwitchEnumerationNotSupported; - string switchExpressionTemplate = "$switchCondition = ${0}; if(@($switchCondition).Count -gt 1) {{ throw '{1}' }}; $switchCondition"; - string switchConditionExpression = String.Format(CultureInfo.InvariantCulture, switchExpressionTemplate, tempVarName, errorMessage); - GeneratePowerShellValue(switchType, switchConditionExpression, false, false); - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - - if (switchStatementAst.Default != null) - { - WriteLine(""); - IndentLevel(); - - WriteLine(""); - IndentLevel(); - - switchStatementAst.Default.Visit(this); - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - } - - foreach (Tuple switchClause in switchStatementAst.Clauses) - { - // Verify they've typed a literal (i.e.: integer, string) - ConstantExpressionAst key = switchClause.Item1 as ConstantExpressionAst; - if (key == null) - { - ReportError("SwitchOnlySupportsConstantExpression", ActivityResources.SwitchOnlySupportsConstantExpression, switchClause.Item1.Extent); - return null; - } - string switchKey = key.Value.ToString(); - - WriteLine(@""); - IndentLevel(); - - switchClause.Item2.Visit(this); - - UnindentLevel(); - WriteLine(""); - } - - UnindentLevel(); - WriteLine(""); - - return null; - } - - private bool IsSupportedSwitchExpressionType(Type switchType) - { - bool isSupportedSwitchExpressionType = - switchType.IsPrimitive || - (switchType == typeof(string)); - - return isSupportedSwitchExpressionType; - } - - object ICustomAstVisitor.VisitDataStatement(DataStatementAst dataStatementAst) - { - ReportError("DataSectionNotSupported", ActivityResources.DataSectionNotSupported, dataStatementAst.Extent); - return null; - } - - object ICustomAstVisitor.VisitForEachStatement(ForEachStatementAst forEachStatementAst) - { - if (!String.IsNullOrEmpty(forEachStatementAst.Label)) - { - ReportError("LoopLabelNotSupported", ActivityResources.LoopLabelNotSupported, forEachStatementAst.Extent); - } - - GenerateSymbolicInformation(forEachStatementAst.Extent); - - // Generate a temporary variable for the foreach condition - string tempVarName = GenerateUniqueVariableName("ForeachCondition"); - Type conditionType = DetectType(tempVarName, false, forEachStatementAst.Condition); - DefineVariable(tempVarName, conditionType, forEachStatementAst.Condition.Extent, null); - - // Generate the assignment of the the clause to the temporary variable - string conditionExpression = GetPowerShellValueExpression(forEachStatementAst.Condition); - GenerateAssignment(tempVarName, forEachStatementAst.Condition.Extent, TokenKind.Equals, forEachStatementAst.Condition, conditionExpression); - - // Clear the storage variable if there is one - if(forEachStatementAst.Parent is AssignmentStatementAst) - { - StorageVariable currentVariable = GetVariableToUse(); - GeneratePowerShellValue(conditionType, "$null", false, currentVariable.VariableName); - } - - // Check if this is a parallel foreach. If so, update the activity name. - string activityName = "ForEach"; - bool isParallel = false; - - if ((forEachStatementAst.Flags & ForEachFlags.Parallel) == ForEachFlags.Parallel) - { - activityName = GetFriendlyName(null, typeof(ThrottledParallelForEach)); - isParallel = true; - } - - WriteLine("<" + activityName + @" x:TypeArguments=""x:Object"">"); - IndentLevel(); - - WriteLine("<" + activityName + ".Values>"); - IndentLevel(); - - Type resultType = typeof(IEnumerable); - string convertedTypeName = GetConvertedTypeName(resultType); - - WriteLine(@""); - IndentLevel(); - - // Evaluate the expression for the ForEach values. We force this to evaluate as a list so that we - // can be guaranteed to iterate over it. - string expression = "$foreachIterator = $" + tempVarName + "; if($null -eq $foreachIterator) { ,@() } else { ,@($foreachIterator) }"; - GeneratePowerShellValue(resultType, expression, false, false); - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - - // Generate throttle limit if specified - if (isParallel && (forEachStatementAst.ThrottleLimit != null)) - { - WriteLine("<" + activityName + ".ThrottleLimit>"); - IndentLevel(); - - resultType = typeof(int); - convertedTypeName = GetConvertedTypeName(resultType); - - WriteLine(@""); - IndentLevel(); - - GeneratePowerShellValue(typeof(int), forEachStatementAst.ThrottleLimit.Extent.Text, false, false); - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - } - - // Generate the index variable - WriteLine(@""); - IndentLevel(); - - WriteLine(""); - IndentLevel(); - - // Convert their variable name to the original case if needed - string variableName = forEachStatementAst.Variable.VariablePath.ToString(); - VariableDefinition existingVariableDefinition = GetVariableDefinition(variableName); - if (existingVariableDefinition != null) - { - variableName = existingVariableDefinition.Name; - } - - WriteLine(@""); - - UnindentLevel(); - WriteLine(""); - - // Generate the Try/Catch block to capture all terminating errors - // so that that terminating error won't terminate other branches - if (isParallel) - { - AddTryCatchForParallelStart(); - } - - // And the statement body - WriteLine(""); - IndentLevel(); - - if (isParallel) - { - EnterScope(); - } - - try - { - forEachStatementAst.Body.Visit(this); - } - finally - { - if (isParallel) - { - DumpVariables("Sequence"); - LeaveScope(); - } - - UnindentLevel(); - WriteLine(""); - - // Finish the Try/Catch block that is to capture all terminating errors - if (isParallel) - { - AddTryCatchForParallelEnd(); - } - - UnindentLevel(); - WriteLine(@""); - - UnindentLevel(); - WriteLine(""); - } - - return null; - } - - object ICustomAstVisitor.VisitDoWhileStatement(DoWhileStatementAst doWhileStatementAst) - { - if (!String.IsNullOrEmpty(doWhileStatementAst.Label)) - { - ReportError("LoopLabelNotSupported", ActivityResources.LoopLabelNotSupported, doWhileStatementAst.Extent); - } - - GenerateSymbolicInformation(doWhileStatementAst.Extent); - GenerateLoop(doWhileStatementAst.Condition, doWhileStatementAst.Body, null, "DoWhile", false); - return null; - } - - object ICustomAstVisitor.VisitForStatement(ForStatementAst forStatementAst) - { - if (!String.IsNullOrEmpty(forStatementAst.Label)) - { - ReportError("LoopLabelNotSupported", ActivityResources.LoopLabelNotSupported, forStatementAst.Extent); - } - - // We compile a 'for' statement into a while statement. - // for(; ; ) { } - // Converts to: - // - // while() { ; } - - GenerateSymbolicInformation(forStatementAst.Extent); - - // Visit the initializer - if (forStatementAst.Initializer != null) - { - forStatementAst.Initializer.Visit(this); - } - - // Generate the loop - GenerateLoop(forStatementAst.Condition, forStatementAst.Body, forStatementAst.Iterator, "While", false); - - return null; - } - - object ICustomAstVisitor.VisitWhileStatement(WhileStatementAst whileStatementAst) - { - if (!String.IsNullOrEmpty(whileStatementAst.Label)) - { - ReportError("LoopLabelNotSupported", ActivityResources.LoopLabelNotSupported, whileStatementAst.Extent); - } - - GenerateSymbolicInformation(whileStatementAst.Extent); - GenerateLoop(whileStatementAst.Condition, whileStatementAst.Body, null, "While", false); - return null; - } - - private void GenerateLoop(PipelineBaseAst condition, StatementBlockAst body, PipelineBaseAst iterator, string whileType, bool isUntil) - { - // Check if the condition itself contains side-effects. If so, generate an error. - string nameOfUnSupportedVariableFound; - if ((condition != null) && CheckIfExpressionHasUnsupportedVariableOrHasSideEffects(null, condition, out nameOfUnSupportedVariableFound)) - { - if (string.IsNullOrEmpty(nameOfUnSupportedVariableFound)) - { - string errorTemplate = ActivityResources.ConditionsCannotHaveSideEffects; - ReportError("ConditionsCannotHaveSideEffects", errorTemplate, condition.Extent); - } - else - { - string error = String.Format(CultureInfo.InvariantCulture, - ActivityResources.VariableNotSupportedInWorkflow, - nameOfUnSupportedVariableFound); - ReportError("VariableNotSupportedInWorkflow", error, condition.Extent); - } - } - - // Check if the condition contains a command call. If so, generate an error - if ((condition != null) && AstContainsCommandCall(condition, null)) - { - string errorTemplate = ActivityResources.ConditionsCannotInvokeActivities; - ReportError("ConditionsCannotInvokeActivities", errorTemplate, condition.Extent); - } - - WriteLine(String.Format(CultureInfo.InvariantCulture, "<{0}>", whileType)); - IndentLevel(); - - WriteLine(String.Format(CultureInfo.InvariantCulture, "<{0}.Condition>", whileType)); - IndentLevel(); - - // Get a default condition if needed - string conditionExpression = null; - if (condition == null) - { - conditionExpression = "$true"; - } - else - { - conditionExpression = GetPowerShellValueExpression(condition); - } - - // If this is an 'until' loop, negate the condition - if (isUntil) - { - conditionExpression = "-not (" + conditionExpression + ")"; - } - - // Evaluate the PowerShell value of the condition - GeneratePowerShellValue(typeof(bool), conditionExpression, false, false); - - UnindentLevel(); - WriteLine(String.Format(CultureInfo.InvariantCulture, "", whileType)); - - WriteLine(""); - IndentLevel(); - - body.Visit(this); - if (iterator != null) - { - iterator.Visit(this); - } - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(String.Format(CultureInfo.InvariantCulture, "", whileType)); - } - - private static bool AstContainsCommandCall(PipelineBaseAst condition, HashSet allowedCommands) - { - Func searcher = (ast) => - { - CommandAst command = ast as CommandAst; - if (command == null) - { - return false; - } - - if (allowedCommands == null) - return true; - - var commandName = command.GetCommandName(); - return commandName == null || !allowedCommands.Contains(commandName); - }; - - Ast result = condition.Find(searcher, searchNestedScriptBlocks: true); - return result != null; - } - - object ICustomAstVisitor.VisitCatchClause(CatchClauseAst catchClauseAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitTryStatement(TryStatementAst tryStatementAst) - { - GenerateSymbolicInformation(tryStatementAst.Extent); - - string exceptionToRethrowName = GenerateUniqueVariableName("__ExceptionToRethrow"); - string exceptionTypeFriendlyName = GetConvertedTypeName(typeof(Exception)); - - // In Workflow, try / catch / finally acts unlike any other common language. - // The Finally block is only processed if the try block is fully processed, - // or one of its catch clauses are fully processed. - // To work around this, we need to wrap the try / catch in another try / catch - // and rethrow it in the finally clause if one is there. - if (tryStatementAst.Finally != null) - { - // Create the exceptionToRethrow variable - if (!VariableDefinedInCurrentScope(exceptionToRethrowName)) - { - DefineVariable(exceptionToRethrowName, typeof(Exception), tryStatementAst.Finally.Extent, null); - } - - WriteLine(""); - IndentLevel(); - WriteLine(""); - IndentLevel(); - } - - // Generate the original try - WriteLine(""); - IndentLevel(); - WriteLine(""); - IndentLevel(); - - // Generate an inner Try/Catch that can unwrap the RuntimeExceptions that we - // get from remoting. - WriteLine(""); - IndentLevel(); - WriteLine(""); - - IndentLevel(); - WriteLine(""); - IndentLevel(); - - tryStatementAst.Body.Visit(this); - - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(""); - WriteLine(""); - IndentLevel(); - Type runtimeExceptionType = typeof(RuntimeException); - string runtimeExceptionTypeFriendlyName = GetConvertedTypeName(runtimeExceptionType); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(@""); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(@""); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(@""); - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - - // Generate the catches - if (tryStatementAst.CatchClauses.Count > 0) - { - WriteLine(""); - IndentLevel(); - string friendlyTypeName = string.Empty; - - if (hasControlFlowException) - { - friendlyTypeName = GetConvertedTypeName(typeof(Microsoft.PowerShell.Workflow.WorkflowReturnException)); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(@""); - WriteLine(@""); - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(@""); - } - - foreach (CatchClauseAst catchClause in tryStatementAst.CatchClauses) - { - List catchTypes = new List(); - - if (catchClause.IsCatchAll) - { - catchTypes.Add(typeof(System.Exception)); - } - else - { - foreach (TypeConstraintAst catchType in catchClause.CatchTypes) - { - Type reflectionType = catchType.TypeName.GetReflectionType(); - if (reflectionType != null) - { - catchTypes.Add(reflectionType); - } - else - { - string error = String.Format(CultureInfo.InvariantCulture, ActivityResources.NewObjectCouldNotFindType, catchType.TypeName); - ReportError("ExceptionTypeNotFound", error, tryStatementAst.Extent); - } - } - } - - foreach (Type actualType in catchTypes) - { - friendlyTypeName = GetConvertedTypeName(actualType); - - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(@""); - - - WriteLine(""); - IndentLevel(); - - catchClause.Body.Visit(this); - - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - - WriteLine(""); - UnindentLevel(); - WriteLine(@""); - } - } - - UnindentLevel(); - WriteLine(""); - } - - // Generate the finally - if (tryStatementAst.Finally != null) - { - // Close the original try / catch. Since we're bringing the original - // finally into a new catch statement, we need to synthesize a dummy one here. - if (tryStatementAst.CatchClauses.Count == 0) - { - WriteLine(""); - } - - UnindentLevel(); - WriteLine(""); - - // Now add our catch statement to emulate a real finally block - UnindentLevel(); - WriteLine(""); - WriteLine(""); - IndentLevel(); - - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - // Store the variable we caught - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(@""); - - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@"[" + exceptionToRethrowName + @"]"); - UnindentLevel(); - WriteLine(@""); - WriteLine(@""); - IndentLevel(); - WriteLine(@"[__UnhandledException]"); - UnindentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(@""); - - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(""); - - WriteLine(""); - IndentLevel(); - WriteLine(""); - IndentLevel(); - - tryStatementAst.Finally.Visit(this); - - // Rethrow any exception we may have eaten - WriteLine(@""); - IndentLevel(); - WriteLine(""); - IndentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(""); - } - - UnindentLevel(); - WriteLine(""); - - return null; - } - - object ICustomAstVisitor.VisitBreakStatement(BreakStatementAst breakStatementAst) - { - ReportError("BreakContinueNotSupported", ActivityResources.BreakContinueNotSupported, breakStatementAst.Extent); - return null; - } - - object ICustomAstVisitor.VisitContinueStatement(ContinueStatementAst continueStatementAst) - { - ReportError("BreakContinueNotSupported", ActivityResources.BreakContinueNotSupported, continueStatementAst.Extent); - return null; - } - - object ICustomAstVisitor.VisitReturnStatement(ReturnStatementAst returnStatementAst) - { - if (returnStatementAst.Pipeline != null) - { - returnStatementAst.Pipeline.Visit(this); - } - - // Throw a hard-coded exception here. Our global exception handler catches this and exits quietly. - WriteLine(@""); - this.hasControlFlowException = true; - - return null; - } - - object ICustomAstVisitor.VisitExitStatement(ExitStatementAst exitStatementAst) - { - // Throw a hard-coded exception here. Our global exception handler catches this and exits quietly. - WriteLine(@""); - this.hasControlFlowException = true; - - return null; - } - - object ICustomAstVisitor.VisitThrowStatement(ThrowStatementAst throwStatementAst) - { - GenerateSymbolicInformation(throwStatementAst.Extent); - - Type exceptionType = typeof(System.Exception); - bool isInParallelBlock = IsThrowStatementInParallelBlock(throwStatementAst); - - if (throwStatementAst.IsRethrow) - { - if (isInParallelBlock) - { - // Define the temp variable - string rethrowExceptionName = GenerateUniqueVariableName("__RethrowException"); - if (!VariableDefinedInCurrentScope(rethrowExceptionName)) - { - DefineVariable(rethrowExceptionName, exceptionType, throwStatementAst.Extent, null); - } - - try - { - // Generate the exception and assign it to the 'rethrowExceptionName' variable - EnterStorage(rethrowExceptionName, false); - GeneratePowerShellValue(exceptionType, "$_", false, true); - } - finally - { - LeaveStorage(); - } - - // Check if the exception is thrown by the 'Throw' statement. If not, add the special key-value pair to - // the 'Data' property. Then we re-throw the exception. - GenerateXamlForThrowStatement(rethrowExceptionName, isRethrow: true); - } - else - { - WriteLine(""); - } - } - else - { - if (throwStatementAst.Pipeline == null) - { - ReportError("ReasonRequiredInThrowStatement", ActivityResources.ReasonRequiredInThrowStatement, throwStatementAst.Extent); - return null; - } - - // Verify it's not a pipeline with commands - HashSet allowedCommands = new HashSet(StringComparer.OrdinalIgnoreCase) { "New-Object", "new" }; - if (AstContainsCommandCall(throwStatementAst.Pipeline, allowedCommands)) - { - string errorTemplate = ActivityResources.ThrowStatementCannotInvokeActivities; - ReportError("ThrowStatementCannotInvokeActivities", errorTemplate, throwStatementAst.Pipeline.Extent); - } - - if (isInParallelBlock) - { - // Define the temp variable - string throwExceptionName = GenerateUniqueVariableName("__ThrowException"); - if (!VariableDefinedInCurrentScope(throwExceptionName)) - { - DefineVariable(throwExceptionName, exceptionType, throwStatementAst.Pipeline.Extent, null); - } - - try - { - // Generate the exception and assign it to the 'throwExceptionName' variable - EnterStorage(throwExceptionName, false); - string expression = GetPowerShellValueExpression(throwStatementAst.Pipeline); - GeneratePowerShellValue(exceptionType, expression, false, true); - } - finally - { - LeaveStorage(); - } - - // Check if the exception is thrown by the 'Throw' statement. If not, add the special key-value pair to - // the 'Data' property. Then we throw the exception. - GenerateXamlForThrowStatement(throwExceptionName, isRethrow: false); - } - else - { - string exceptionTypeFriendlyName = GetConvertedTypeName(exceptionType); - - WriteLine(""); - IndentLevel(); - - WriteLine(""); - IndentLevel(); - - WriteLine(@""); - IndentLevel(); - - string expression = GetPowerShellValueExpression(throwStatementAst.Pipeline); - GeneratePowerShellValue(exceptionType, expression, false, false); - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - - UnindentLevel(); - WriteLine(""); - } - } - - return null; - } - - /// - /// Check if the 'Throw' statement is in a parallel block - /// - /// - /// - private static bool IsThrowStatementInParallelBlock(ThrowStatementAst throwStatementAst) - { - var parent = throwStatementAst.Parent; - while (parent != null) - { - var blockStatementAst = parent as BlockStatementAst; - if (blockStatementAst != null) - { - if (blockStatementAst.Kind.Kind == TokenKind.Parallel) - { - return true; - } - } - else - { - var forEachStatementAst = parent as ForEachStatementAst; - if (forEachStatementAst != null && (forEachStatementAst.Flags & ForEachFlags.Parallel) == ForEachFlags.Parallel) - { - return true; - } - } - - parent = parent.Parent; - } - - return false; - } - - /// - /// Generate the XAML for the 'Throw' statement - /// - /// - /// - private void GenerateXamlForThrowStatement(string variableName, bool isRethrow) - { - // Add a special key value pair to the Data property of the exception, so that we know it's thrown by the 'Throw' statement - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - - AddOrRemoveSpecialKey(variableName, addKey: true); - - UnindentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(@""); - - if (isRethrow) - { - WriteLine(@""); - } - else - { - WriteLine(@""); - } - } - - /// - /// Add or remove the special key-value pair to/from the exception - /// - /// - /// - private void AddOrRemoveSpecialKey(string variableName, bool addKey) - { - string iDictionaryTypeFriendlyName = GetConvertedTypeName(typeof(System.Collections.IDictionary)); - string objectTypeFriendlyName = GetConvertedTypeName(typeof(object)); - string method = addKey ? "Add" : "Remove"; - - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@"[" + variableName + @".Data]"); - UnindentLevel(); - WriteLine(@""); - WriteLine(@"[""" + M3PKeyForThrowStatement + @"""]"); - - if (addKey) - { - WriteLine(@"[""""]"); - } - - UnindentLevel(); - WriteLine(@""); - } - - /// - /// Start to add the Try/Catch block for the parallel statement/block - /// - private void AddTryCatchForParallelStart() - { - WriteLine(""); - IndentLevel(); - WriteLine(""); - IndentLevel(); - } - - /// - /// Finish the Try/Catch block for the parallel statement/block - /// - private void AddTryCatchForParallelEnd() - { - UnindentLevel(); - WriteLine(""); - WriteLine(""); - IndentLevel(); - - string exceptionTypeFriendlyName = GetConvertedTypeName(typeof(Exception)); - string writeErrorFriendlyName = GetFriendlyName(null, typeof(Microsoft.PowerShell.Utility.Activities.WriteError)); - - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(@""); - WriteLine(@""); - IndentLevel(); - WriteLine(@""); - IndentLevel(); - - WriteLine(@""); - IndentLevel(); - AddOrRemoveSpecialKey("__UnhandledException", addKey: false); // Remove the special key-value pair - WriteLine(@""); // Rethrow the exception if it was thrown by the 'Throw' statement - UnindentLevel(); - WriteLine(@""); - - UnindentLevel(); - WriteLine(@""); - WriteLine(@""); - IndentLevel(); - WriteLine(@"<" + writeErrorFriendlyName + @" Exception=""[__UnhandledException]"" />"); // Otherwise, write to the error stream - UnindentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(@""); - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(@""); - - UnindentLevel(); - WriteLine(""); - UnindentLevel(); - WriteLine(""); - } - - object ICustomAstVisitor.VisitVariableExpression(VariableExpressionAst variableExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitDoUntilStatement(DoUntilStatementAst doUntilStatementAst) - { - if (!String.IsNullOrEmpty(doUntilStatementAst.Label)) - { - ReportError("LoopLabelNotSupported", ActivityResources.LoopLabelNotSupported, doUntilStatementAst.Extent); - } - - GenerateSymbolicInformation(doUntilStatementAst.Extent); - GenerateLoop(doUntilStatementAst.Condition, doUntilStatementAst.Body, null, "DoWhile", true); - return null; - } - - object ICustomAstVisitor.VisitMemberExpression(MemberExpressionAst memberExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitHashtable(HashtableAst hashtableAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitScriptBlockExpression(ScriptBlockExpressionAst scriptBlockExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitParenExpression(ParenExpressionAst parenExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitExpandableStringExpression(ExpandableStringExpressionAst expandableStringExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitIndexExpression(IndexExpressionAst indexExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitBlockStatement(BlockStatementAst blockStatementAst) - { - GenerateSymbolicInformation(blockStatementAst.Extent); - - bool isParallelBlock = String.Equals(blockStatementAst.Kind.Text, TokenKind.Parallel.Text(), StringComparison.OrdinalIgnoreCase); - string containerType = isParallelBlock ? "Parallel" : "Sequence"; - WriteLine("<" + containerType + ">"); - IndentLevel(); - EnterScope(); - - try - { - blockStatementAst.Body.Visit(this); - } - finally - { - DumpVariables(containerType); - LeaveScope(); - - UnindentLevel(); - WriteLine(""); - } - - return null; - } - - object ICustomAstVisitor.VisitAttributedExpression(AttributedExpressionAst attributedExpressionAst) - { - throw new NotSupportedException(); - } - - object ICustomAstVisitor.VisitNamedAttributeArgument(NamedAttributeArgumentAst namedAttributeArgumentAst) - { - throw new NotSupportedException(); - } - - private void ReportError(string errorId, string errorText, IScriptExtent extent) - { - _parseErrors.Add(new ParseError(extent, errorId, errorText)); - - if (!this.ValidateOnly) - { - throw new ParseException(_parseErrors.ToArray()); - } - } - - private int uniqueVariableDisambiguator = 0; - private string GenerateUniqueVariableName(string basename) - { - return basename + "_" + (this.uniqueVariableDisambiguator++).ToString(CultureInfo.InvariantCulture); - } - - internal enum ActivityKind - { - /// - /// Delay activity - /// - Delay = 0, - - /// - /// An InlineScript activity - /// - InlineScript = 1, - - /// - /// Xaml injection activity. inline XAML - /// - InvokeExpression = 2, - - /// - /// New-Object activity - /// - NewObject = 3, - - /// - /// Persist activity - /// - Persist = 4, - - /// - /// Other command activity - /// - RegularCommand = 5, - - /// - /// Suspend activity - /// - Suspend = 6, - } - - private enum IterativeCommands - { - None = 0, - ForEachSequence, - WhereSequence - } - } - - class CommandArgumentInfo - { - internal object Value { get; set; } - internal object OriginalValue { get; set; } - internal Ast ArgumentAst { get; set; } - internal bool IsLiteral { get; set; } - } - - class VariableScope - { - internal VariableScope() - { - Variables = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - - internal Dictionary Variables { get; set; } - } - - class StorageVariable - { - internal StorageVariable(string variableName, bool isAggregatingVariable) - { - if (string.IsNullOrEmpty(variableName)) - { - // caller needs to make sure the argument is not null or empty - throw new PSArgumentNullException(variableName); - } - - IsAggregatingVariable = isAggregatingVariable; - VariableName = variableName; - } - - /// - /// This indicates if the variable is one that will aggregate results from a parallel/sequence/foreach block. - /// For example: - /// workflow bar { $a = parallel { Get-Process -Name powershell; Get-Service -Name Dhcp } } - /// $a here will contain all results generated from the parallel block, including a process object "powershell" - /// and a service object "Dhcp". We call $a an aggregating variable. - /// - internal bool IsAggregatingVariable { get; set; } - internal string VariableName { get; private set; } - } - - class VariableDefinition - { - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal string Name { get; set; } - internal string XamlDefinition { get; set; } - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Type Type { get; set; } - } - - /// - /// Used by Script->Xaml converter to give compile time error - /// for variables that are not supported in a workflow context. - /// - internal enum PSWorkflowUnsupportedVariable - { - Args = 0, - Error = 1, - MyInvocation = 2, - PSBoundParameters = 3, - PSCmdlet = 4, - PSCommandPath = 5, - PSDefaultParameterValues = 6, - PSScriptRoot = 7, - StackTrace = 8, - PID = 9 - } - - internal class ExpressionHasUnsupportedVariableOrSideEffectsVisitor : AstVisitor - { - internal bool ExpressionHasSideEffects { get; set; } - internal string NameOfUnsupportedVariableFound { get; set; } - - string variableName; - - internal ExpressionHasUnsupportedVariableOrSideEffectsVisitor(string variableName) - { - ExpressionHasSideEffects = false; - this.variableName = variableName; - } - - public override AstVisitAction VisitBlockStatement(BlockStatementAst blockStatementAst) - { - return AstVisitAction.SkipChildren; - } - - public override AstVisitAction VisitForEachStatement(ForEachStatementAst forEachStatementAst) - { - return AstVisitAction.SkipChildren; - } - - // Stop visiting when we hit a command (like InlineScript) - public override AstVisitAction VisitCommand(CommandAst commandAst) - { - return AstVisitAction.SkipChildren; - } - - // Check if this is an assignment to another variable - public override AstVisitAction VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst) - { - if (assignmentStatementAst != null) - { - VariableExpressionAst leftExpressionAst = assignmentStatementAst.Left as VariableExpressionAst; - if (leftExpressionAst != null) - { - string targetVariableName = leftExpressionAst.VariablePath.ToString(); - if (!String.Equals(variableName, targetVariableName, StringComparison.OrdinalIgnoreCase)) - { - ExpressionHasSideEffects = true; - return AstVisitAction.StopVisit; - } - } - } - - return base.VisitAssignmentStatement(assignmentStatementAst); - } - - public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst) - { - // ignore patterns like $env: - if (variableExpressionAst.VariablePath.IsVariable) - { - // dont allow patterns like "pscmdlet","workflow:pscmdlet" - string tempVariableName = variableExpressionAst.VariablePath.ToString(); - int indexOfColon = tempVariableName.IndexOf(':'); - if (indexOfColon != -1) - { - tempVariableName = tempVariableName.Substring(indexOfColon + 1); - } - - PSWorkflowUnsupportedVariable unused; - if (Enum.TryParse(tempVariableName, true, out unused)) - { - ExpressionHasSideEffects = true; - NameOfUnsupportedVariableFound = tempVariableName; - - return AstVisitAction.StopVisit; - } - } - return AstVisitAction.Continue; - } - - public override AstVisitAction VisitUnaryExpression(UnaryExpressionAst unaryExpressionAst) - { - // ignore cases like "-bnot 100" - switch (unaryExpressionAst.TokenKind) - { - case TokenKind.Minus: - case TokenKind.Plus: - case TokenKind.Not: - case TokenKind.Bnot: - case TokenKind.Exclaim: - case TokenKind.Comma: - case TokenKind.Csplit: - case TokenKind.Isplit: - case TokenKind.Join: - return AstVisitAction.SkipChildren; - } - - if (unaryExpressionAst.Child is VariableExpressionAst) - { - // If needed, check if it assigned to another variable - if (String.IsNullOrEmpty(variableName)) - { - ExpressionHasSideEffects = true; - return AstVisitAction.StopVisit; - } - - VariableExpressionAst referenceVariable = unaryExpressionAst.Child as VariableExpressionAst; - if (!String.Equals(variableName, referenceVariable.VariablePath.ToString(), StringComparison.OrdinalIgnoreCase)) - { - ExpressionHasSideEffects = true; - return AstVisitAction.StopVisit; - } - } - - return base.VisitUnaryExpression(unaryExpressionAst); - } - } -} - - diff --git a/src/Microsoft.PowerShell.Activities/AssemblyInfo.cs b/src/Microsoft.PowerShell.Activities/AssemblyInfo.cs deleted file mode 100644 index 08318ef680c..00000000000 --- a/src/Microsoft.PowerShell.Activities/AssemblyInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Reflection; -using System.Security.Permissions; -using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Diagnostics.CodeAnalysis; - -[assembly: AssemblyVersion("3.0.0.0")] -[assembly: AssemblyFileVersionAttribute("3.0.0.0")] -[assembly: System.Resources.NeutralResourcesLanguage("en")] -[assembly: System.Runtime.InteropServices.ComVisible(false)] - -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("VSTS.ActivityTests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.PowerShell.Utility.Activities,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.PowerShell.Management.Activities,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] - -[assembly: AssemblyConfiguration("")] -[assembly: ReliabilityContractAttribute(Consistency.MayCorruptAppDomain, Cer.MayFail)] -[assembly: AssemblyTitle("Microsoft.PowerShell.Activities")] -[assembly: AssemblyDescription("Microsoft.PowerShell.Activities")] - -[module: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant")] -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="ActivityResources.resources", MessageId="InlineScript")] -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="ActivityResources.resources", MessageId="inlinescript")] -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="ActivityResources.resources", MessageId="foreach")] - diff --git a/src/Microsoft.PowerShell.Activities/Xamls/InlineScriptDesigner.xaml b/src/Microsoft.PowerShell.Activities/Xamls/InlineScriptDesigner.xaml deleted file mode 100644 index 6b5083509e5..00000000000 --- a/src/Microsoft.PowerShell.Activities/Xamls/InlineScriptDesigner.xaml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - This is the collapsed view - - - - - - - - - - - diff --git a/src/Microsoft.PowerShell.Activities/Xamls/PipelineDesigner.xaml b/src/Microsoft.PowerShell.Activities/Xamls/PipelineDesigner.xaml deleted file mode 100644 index f5eb1ae1624..00000000000 --- a/src/Microsoft.PowerShell.Activities/Xamls/PipelineDesigner.xaml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.PowerShell.Activities/map.json b/src/Microsoft.PowerShell.Activities/map.json deleted file mode 100644 index b0e81fdefd6..00000000000 --- a/src/Microsoft.PowerShell.Activities/map.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "monad/src/Activities/WmiActivities.cs": "Activities/WmiActivities.cs", - "monad/src/Activities/GetCimAssociatedInstanceActivity.cs": "Activities/GetCimAssociatedInstanceActivity.cs", - "monad/src/Activities/GetCimClassActivity.cs": "Activities/GetCimClassActivity.cs", - "monad/src/Activities/GetCimInstanceActivity.cs": "Activities/GetCimInstanceActivity.cs", - "monad/src/Activities/InvokeCimMethodActivity.cs": "Activities/InvokeCimMethodActivity.cs", - "monad/src/Activities/NewCimInstanceActivity.cs": "Activities/NewCimInstanceActivity.cs", - "monad/src/Activities/NewCimSessionActivity.cs": "Activities/NewCimSessionActivity.cs", - "monad/src/Activities/NewCimSessionOptionActivity.cs": "Activities/NewCimSessionOptionActivity.cs", - "monad/src/Activities/RemoveCimInstanceActivity.cs": "Activities/RemoveCimInstanceActivity.cs", - "monad/src/Activities/SetCimInstanceActivity.cs": "Activities/SetCimInstanceActivity.cs", - "monad/src/Activities/InlineScript.cs": "Activities/InlineScript.cs", - "monad/src/Activities/IsArgumentSet.cs": "Activities/IsArgumentSet.cs", - "monad/src/Activities/ThrottledParallelForeach.cs": "Activities/ThrottledParallelForeach.cs", - "monad/src/Activities/PSPersist.cs": "Activities/PSPersist.cs", - "monad/src/Activities/GetPSWorkflowData.cs": "Activities/GetPSWorkflowData.cs", - "monad/src/Activities/SetHostValue.cs": "Activities/SetHostValue.cs", - "monad/src/Activities/InlineScriptDesigner.xaml.cs": "Activities/InlineScriptDesigner.xaml.cs", - "monad/src/Activities/ActivityGenerator.cs": "Activities/ActivityGenerator.cs", - "monad/src/Activities/PowerShellValue.cs": "Activities/PowerShellValue.cs", - "monad/src/Activities/Pipeline.cs": "Activities/Pipeline.cs", - "monad/src/Activities/PipelineDesigner.xaml.cs": "Activities/PipelineDesigner.xaml.cs", - "monad/src/Activities/WorkflowJobConverter.cs": "Activities/WorkflowJobConverter.cs", - "monad/src/Activities/PipelineDesigner.xaml": "Xamls/PipelineDesigner.xaml", - "monad/src/Activities/InlineScriptDesigner.xaml": "Xamls/InlineScriptDesigner.xaml", - "monad/src/Activities/resources/ActivityResources.resx": "resources/ActivityResources.resx" -} diff --git a/src/Microsoft.PowerShell.Activities/resources/ActivityResources.resx b/src/Microsoft.PowerShell.Activities/resources/ActivityResources.resx deleted file mode 100644 index 32405e99082..00000000000 --- a/src/Microsoft.PowerShell.Activities/resources/ActivityResources.resx +++ /dev/null @@ -1,561 +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 generate activity. The name '{0}' is reserved. - - - Cannot generate activity. The command name '{0}' could not be found in the default runspace. Use the GenerateFromCommandInfo method to generate activities for non-default commands. - GenerateFromCommandInfo should not be localized. It is the name of a method. - - - Activity-Specific Parameters - - - 'Command' is mutually exclusive with 'CommandName'. Either specify 'CommandName' (optionally with 'Parameters'), or 'Command'. - Command, CommandName and Parameters should not be localized. These are parameters. - - - Cannot supply both connection URI and computer name. - - - - ..\..\..\..\src\cimSupport\cmdletization\xml\cmdlets-over-objects.xsd;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - {Locked} - - - The value of the EnumName attribute does not translate to a valid C# identifier: {0}. Verify the EnumName attribute in Cmdlet Definition XML, and then try again. - {StrContains="EnumName"} - - - The value of the Name attribute is not a valid C# identifier: {0}. Verify the Name attribute in Cmdlet Definition XML and try again. - {StrContains="Enum"} {StrContains="Value"} {StrContains="Name"} - - - A command name is required. - - - The computer name {0} is not valid. If you are trying to supply a Uri, use the Uri parameter. - - - Connectivity - - - Windows PowerShell Workflow cannot continue running the activity because an error occurred while importing dependent module(s) '{0}' specified for activity '{1}'. To fix this problem, make sure that the module exists on the computer. If it is not required, remove references to the module from the activity. - - - Input is defined in Pipeline variable and in the first child activity. Input should be defined only at one place. - - - Result is defined in Pipeline variable and in the last child activity. Result should be defined at one place. - - - Cannot have an empty BaseDirectory for importing localized data. Please specify a valid BaseDirectory and run the command again. - BaseDirectory should not be localized. This is a parameter. - - - Input and Output - - - The input parameter is required. - - - Restart-Computer activity cannot be run because both localhost and managed nodes are provided in the ComputerName parameter. For this scenario please run Restart-Computer activity for managed nodes followed by another Restart-Computer activity for localhost. - Restart-Computer and ComputerName should not be localized. These are a command and parameter. - - - Pipeline activity works with at least one child activity. - - - The following argument can not be null or empty: Expression - - - The result of Windows PowerShell expression evaluation is null or nothing - - - {0} line:{1} char:{2} - - - Parameter 'Wait' cannot be used for Restart-Computer activity when the localhost is being restarted. - Wait and Restart-Computer should not be localized. These are a parameter and command. - - - The activity has exceeded the specified maximum running time of {0} seconds. - - - Cannot generate activity. The name '{0}' is reserved. - - - Cannot generate activity. The command name '{0}' could not be found in the default runspace. Use the GenerateFromCommandInfo method to generate activities for non-default commands. - GenerateFromCommandInfo should not be localized. It is the name of a method. - - - The command name '{0}' is ambiguous and cannot be processed. To use this command, specify a module qualified name such as: 'Microsoft.PowerShell.Management\Get-Process'. - Microsoft.PowerShell.Management\Get-Process should not be localized. It is the qualified command name. - - - This type of assignment is not supported. Only variable names (i.e.: $variable) may be used as the target of an assignment statement. - - - Attributed expression (i.e.: [Parameter()]$x) should be used only when declaring parameters for the script workflow. - Parameter() should not be localized. This is syntax. - - - Begin, Process, and End statements are not supported in a Windows PowerShell Workflow. - Begin, Process, and End should not be localized. These are syntax. - - - Break and Continue statements are not supported in a Windows PowerShell Workflow. Instead, use an 'if' statement to control loop execution. - Break and Continue and 'if' should not be localized. These are syntax. - - - Cannot assign Start-Sleep to a variable. Start-Sleep generates no output. - Start-Sleep should not be localized. This is a command name. - - - Cannot redirect the error stream to the output stream. The target activity '{0}' does not contain the property 'MergeErrorToOutput'. - MergeErrorToOutput should not be localized. It is a property. - - - Cannot process more than one script block. - - - Cannot redirect error stream for the New-Object activity. - New-Object should not be localized. This is a command name. - - - Cannot redirect error stream for the delay activity. Please remove the stream redirection from this activity and try again. - - - Cannot assign the output of the '{1}' activity. It does not contain the 'Result' property. If this is a workflow that calls another workflow, implement a Result property as a [ref] parameter. - Result should not be localized. It is the name of a property. - - - 'Command' is mutually exclusive with 'CommandName'. Either specify 'CommandName' (optionally with 'Parameters'), or 'Command'. - Command, CommandName and Parameters should not be localized. These are parameters. - - - Assigning values to the Result argument is not supported. To store the output of a command, assign it to a variable. For example: $output = Get-Process. - $output = Get-Process should not be localized. This is syntax and command name. Result should not be localized. It is a parameter. - - - Cannot store the results of this type of expression into a variable. Only the results of commands, pipelines, constant expressions, foreach statements, parallel and sequence statements can be stored in variables. - - - Cannot store results in the variable '{0}'. Results are already being collected in the variable '{1}'. - - - The variable with name '{0}' is defined to store results from a parallel or sequence block. Therefore, it cannot be reused inside such blocks. - - - Cannot call the '{0}' command. Other commands from this module have been packaged as workflow activities, but this command was specifically excluded. This is likely because the command requires an interactive Windows PowerShell session, or has behavior not suited for workflows. To run this command anyway, place it within an inline-script (InlineScript {{ {0} }}) where it will be invoked in isolation. - InlineScript should not be localized. This is syntax. - - - A command name is required. - - - Could not find a parameter named '{0}' for the '{1}' command. Windows PowerShell common parameters such as WhatIf and OutVariable are not supported. - WhatIf and OutVariable should not be localized. These are parameter names. - - - The computer name {0} is not valid. If you are trying to supply a Uri, use the Uri parameter. - - - In a Windows PowerShell Workflow, loop conditions that modify variables are not supported. To change a variable, place the modification statement in the loop body itself. - - - Could not find a parameter named '{0}'. Supported parameters are: {1}. - - - Could not find a parameter named '{0}'. Workflow-common parameters such as PSComputerName are not supported in nested workflows that already have nested workflows. - PSComputerName should not be localized. This is a parameter. - - - Could not find a parameter named '{0}'. Note that this activity has the same name as a Windows PowerShell cmdlet, but different parameters. Supported parameters are: {1}. - - - Could not load assembly '{0}' specified in the list of required assemblies. - - - Dot-sourcing (. <command>) and the invocation operator (& <command>) are not supported in a Windows PowerShell Workflow. Wrap this command invocation into an inlinescript { } instead. - - - Input is defined in the Pipeline activity and in the first child activity. Input should be defined in only one place. - - - The result is defined in the Pipeline variable, and in the last child activity. The result should be defined in only one place. - - - Dynamic parameters are not supported in a Windows PowerShell Workflow. - - - The value of the EnumName attribute doesn't translate to a valid C# identifier: {0}. Verify the EnumName attribute in Cmdlet Definition XML and try again. - {StrContains="EnumName"} - - - The value of the Name attribute is not a valid C# identifier: {0}. Verify the Name attribute in Cmdlet Definition XML and try again. - {StrContains="Enum"} {StrContains="Value"} {StrContains="Name"} - - - In a Windows PowerShell Workflow, assignment to environment variables is not supported. - - - Cannot have an empty BaseDirectory for importing localized data. Please specify a valid BaseDirectory and run the command again. - BaseDirectory should not be localized. This is a parameter. - - - In a Windows PowerShell Workflow, the syntax for the InlineScript activity is "InlineScript { <commands> }". - InlineScript should not be localized. This is syntax. - - - The input parameter is required. - - - {0} is not a valid parameter or variable name. Names must start with a letter, and contain only letters, digits, '-', and '_'. - - - A variable scope prefix that is not valid was detected. The only valid scope prefix in the script workflow is "$WORKFLOW:". - $WORKFLOW should not be localized. This is syntax. - - - In a Windows PowerShell Workflow, the syntax for Invoke-Expression is: "Invoke-Expression -Language XAML -Command <string>". - Invoke-Expression -Language XAML -Command should not be localized. This is syntax. - - - The Restart-Computer activity cannot run because both localhost and remote computers are provided in the ComputerName parameter. For this scenario, run the Restart-Computer activity for remote computers first, followed by another Restart-Computer activity for localhost. - Restart-Computer and ComputerName should not be localized. These are a command and parameter. - - - Loop labels are not supported in a Windows PowerShell Workflow. - - - Method invocation is not supported in a Windows PowerShell Workflow. To use .NET scripting, place your commands in an inline script: InlineScript { <commands> }. - - - Invoke-Expression must use "-Language XAML" in a Windows PowerShell Workflow. - Invoke-Expression and -Language XAML should not be localized. These are syntax. - - - Could not find type {0}. Load the type(s) and try again. - - - In a Windows PowerShell Workflow, the syntax for New-Object is: "New-Object -TypeName <TypeName>". - New-Object and -TypeName <TypeName> should not be localized. This is syntax. - - - A pipeline activity must have at least one child activity. - - - The following argument cannot be null or empty: Expression - - - The result of Windows PowerShell expression evaluation is null or nothing. - - - You can provide only one #requires statement per script. - - - Only simple variable references (i.e.: $x) and number constants are supported in a unary expression. - - - Only the merging redirection from the error stream to the output stream is supported. - - - Unary operators '++' and '--' work only on variables in the script workflow. - - - The syntax of a {0} script block is '{0} { <commands> }'. - - - Advanced parameter validation is not supported on nested workflows. - - - Positional parameters are not supported in a Windows PowerShell Workflow. To invoke this command, use explicit parameter names with all values. For example: "Command -Parameter <value>". - - - Parameter 'Wait' cannot be used for Restart-Computer activity when the localhost is being restarted. - Wait and Restart-Computer should not be localized. These are a parameter and command. - - - Script block invocation is not supported in a Windows PowerShell Workflow. To run a set of commands in a similar way as the script block invocation, place your commands in an inline script: InlineScript { <commands> }. - InlineScript should not be localized. This is syntax. - - - In a Windows PowerShell Workflow, the syntax for Start-Sleep is: "Start-Sleep -Seconds <int>" or "Start-Sleep -Milliseconds <int>". - Start-Sleep, -Seconds and -Milliseconds should not be localized. These are a command and parameter names. - - - Sub expression (i.e.: $($x)) should only be used as the parameter value in a Windows PowerShell Workflow. To use .NET scripting, place your commands in an inline script: InlineScript { <commands> }. - InlineScript should not be localized. This is syntax. - - - Case-insensitive switch statements are not supported in a Windows PowerShell Workflow. Supply the -CaseSensitive flag, and ensure that case clauses are written appropriately. To write a case-insensitive case statement, first convert the input to either uppercase or lowercase, and update the case clauses to match. - - - Switch clauses must all be of the same type in a Windows PowerShell Workflow, or the condition expression must be strongly typed. - - - In a Windows PowerShell Workflow, the switch statement supports only the 'CaseSensitive' flag. - CaseSensitive should not be localized, this is a flag. - - - Only constant expressions are supported as switch clauses in a Windows PowerShell Workflow. - - - Trap statements are not supported in a Windows PowerShell Workflow. Instead, use try, catch, or finally. - - - Cannot create workflow. It depends on the type, '{0}', which was generated dynamically. - - - Cannot define variable. A variable with name '{0}' has already been defined. To reference a variable from the top-level scope of this workflow, use the syntax: '$WORKFLOW:{0}'. - $WORKFLOW should not be localized. This is syntax. - - - The Checkpoint-Workflow command accepts no parameters. - Checkpoint-Workflow should not be localized. This is a command name. - - - The Suspend-Workflow command accepts only one optional parameter, the syntax for Suspend-Workflow is: "Suspend-Workflow [-Label <string>]". - Suspend-Workflow should not be localized. This is a command name. - - - Ambiguous properties are found for the property name '{0}'. - - - The generic parameter type '{0}' for the parameter '{1}' cannot be resolved. - - - The value for the parameter '{0}' is not specified. To invoke this command, use explicit parameter names with all values. For example: "Command -Parameter <value>". - - - The parameter cannot be processed because the parameter name '{0}' is ambiguous. Possible matches include: {1}. - - - Data sections are not supported in a Windows PowerShell Workflow. - - - The throw statement requires a reason. - - - In a Windows PowerShell Workflow, switch statements support only expressions that return a single element. - - - In a Windows PowerShell Workflow, loop conditions that invoke activities are not supported. Conditions can use only variable references, and Windows PowerShell language elements that interact with those variables. - - - In a Windows PowerShell Workflow, throw statements that invoke activities (other than New-Object) are not supported. Throw statements can use only strings, variable references, and Windows PowerShell language elements. - New-Object should not be localized. This is a command name. - - - Parameter '{0}' is defined as an InOutArgument or OutArgument and can accept only variable references. - InOutArgument and OutArgument should not be localized. - - - The scope prefix "$WORKFLOW:" cannot be used in an InlineScript activity. To reference a workflow variable in an InlineScript activity, use the prefix "$USING:" instead. Workflow variables cannot be modified from an InlineScript activity. To change a workflow variable, assign the output of the InlineScript activity to that variable. - $USING and $WORKFLOW and InlineScript should not be localized. These are syntax. - - - The '{0}' activity is not supported in a workflow pipeline. - - - The '{0}' command is handled by the built-in '{1}' keyword. Use the built-in keyword instead. - - - In a Windows PowerShell Workflow, parameter defaults may only be simple value types (such as integers) and strings. In addition, the type of the default value must match the type of the parameter. - - - The output of the New-Object cmdlet must be assigned to a variable. - New-Object should not be localized. This is a command. - - - Cannot find the '{0}' command. If this command is defined as a workflow, ensure it is defined before the workflow that calls it. If it is a command intended to run directly within Windows PowerShell (or is not available on this system), place it in an InlineScript: 'InlineScript {{ {0} }}' - InlineScript should not be localized. This is syntax. - - - The value of the variable '{0}' can only be changed using the Set-PSWorkflowData activity. - Set-PSWorkflowData should not be localized. This is a command name. - - - The variable '{0}' is read-only. - - - Cannot start "{0}". Interactive console applications are not supported in a Windows PowerShell Workflow. To run the application, use the Start-Process cmdlet. - - - Cannot bind parameter because parameter '{0}' is specified more than once. To provide multiple values to parameters that can accept multiple values, use the array syntax. For example, "-parameter value1,value2,value3". - - - rootWorkflowName '{0}' is invalid. Make sure it exists in the context. - - - Could not find a parameter named 'ComputerName'. Remote connectivity in this command is handled by the 'PSComputerName' parameter. - - - Cannot define variable. Scope names are only valid in a parallel or sequence block. Within a parallel or sequence block, the only valid scope name is 'workflow'. - - - The '{0}' property does not support elements from the Windows PowerShell language such as parentheses and variables. To use this value, enclose it in a single-quoted string. - - - In a Windows PowerShell Workflow, the CmdletBinding attribute only supports the following values: "DefaultParameterSetName, ConfirmImpact, HelpUri, PositionalBinding". - - - The function or workflow '{0}' cannot be redefined. - - - The session state entry type '{0}' is not supported. - - - A workflow cannot use recursion. - - - The variable '{0}' cannot be used in a script workflow. - - - Cannot create workflow. Inline XAML is not supported in this language mode. - - - The generic type '{0}' cannot be resolved for the parameter '{1}'. - - - Cannot define pipeline. Once a pipeline uses the Sequence parameter of the Foreach-Object or Where-Object cmdlets, all remaining commands must also be either the Foreach-Object or Where-Object cmdlet with the Sequence parameter. - Sequence, Foreach-Object, and Where-Object should not be localized. They are command and parameter names. - - - Cannot call command. When used with the 'Sequence' parameter, the Foreach-Object cmdlet supports only the 'PipelineVariable', 'Begin', 'Sequence', and 'End' parameters. - Sequence, PipelineVariable, Begin, End, and Foreach-Object should not be localized. These are a parameter and command. - - - Cannot create pipeline. The Foreach-Object cmdlet with the -Sequence parameter cannot be used as the first element of a pipeline. - Foreach-Object should not be localized, or -Sequence. They are cmdlet and parameter names. - - - Cannot define pipeline. An iterative pipeline may not be nested within another iterative pipeline. - - - Cannot call command. When used with the 'Sequence' parameter, the Where-Object cmdlet supports only the 'PipelineVariable' and 'Sequence' parameters. - Sequence, PipelineVariable, and Where-Object should not be localized. These are a parameters and command. - - - Cannot create pipeline. The Where-Object cmdlet with the -Sequence parameter cannot be used as the first element of a pipeline. - Sequence and Where-Object should not be localized. These are a parameters and command. - - diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/AssemblyInfo.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/AssemblyInfo.cs deleted file mode 100644 index f5f7aebcaef..00000000000 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/AssemblyInfo.cs +++ /dev/null @@ -1,9 +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.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 b13e2e76ce7..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); } // @@ -514,149 +429,142 @@ private void AccumulatePipelineFileNames() // private void ProcessGetLog() { - EventLogSession eventLogSession = CreateSession(); - - FindLogNamesMatchingWildcards(eventLogSession, _accumulatedLogNames); - if (_logNamesMatchingWildcard.Count == 0) + using (EventLogSession eventLogSession = CreateSession()) { - return; - } + FindLogNamesMatchingWildcards(eventLogSession, _accumulatedLogNames); + if (_logNamesMatchingWildcard.Count == 0) + { + return; + } - EventLogQuery logQuery; - if (_logNamesMatchingWildcard.Count > 1) - { - string query = BuildStructuredQuery(eventLogSession); - logQuery = new EventLogQuery(null, PathType.LogName, query); - logQuery.TolerateQueryErrors = true; - } - else - { - logQuery = new EventLogQuery(_logNamesMatchingWildcard[0], PathType.LogName, _filter); - } - logQuery.Session = eventLogSession; - logQuery.ReverseDirection = !_oldest; + EventLogQuery logQuery; + if (_logNamesMatchingWildcard.Count > 1) + { + string query = BuildStructuredQuery(eventLogSession); + logQuery = new EventLogQuery(null, PathType.LogName, query); + logQuery.TolerateQueryErrors = true; + } + else + { + logQuery = new EventLogQuery(_logNamesMatchingWildcard[0], PathType.LogName, FilterXPath); + } - EventLogReader readerObj = new EventLogReader(logQuery); + logQuery.Session = eventLogSession; + logQuery.ReverseDirection = !_oldest; - if (readerObj != null) - { - ReadEvents(readerObj); + ReadEvents(logQuery); } } - // // Process GetProviderSet parameter set // private void ProcessGetProvider() { - EventLogSession eventLogSession = CreateSession(); - - FindProvidersByLogForWildcardPatterns(eventLogSession, _providerName); - - if (_providersByLogMap.Count == 0) + using (EventLogSession eventLogSession = CreateSession()) { - // - // Just return: errors already written above for each unmatched provider name pattern. - // - return; - } + FindProvidersByLogForWildcardPatterns(eventLogSession, ProviderName); + if (_providersByLogMap.Count == 0) + { + // + // Just return: errors already written above for each unmatched provider name pattern. + // + return; + } - EventLogQuery logQuery = null; - if (_providersByLogMap.Count > 1) - { - string query = BuildStructuredQuery(eventLogSession); - logQuery = new EventLogQuery(null, PathType.LogName, query); - logQuery.TolerateQueryErrors = true; - } - else - { - // - // There's only one key at this point, but we need an enumerator to get to it. - // - foreach (string log in _providersByLogMap.Keys) + EventLogQuery logQuery = null; + if (_providersByLogMap.Count > 1) { - logQuery = new EventLogQuery(log, PathType.LogName, AddProviderPredicatesToFilter(_providersByLogMap[log])); - WriteVerbose(string.Format(CultureInfo.InvariantCulture, "Log {0} will be queried", log)); + string query = BuildStructuredQuery(eventLogSession); + logQuery = new EventLogQuery(null, PathType.LogName, query); + logQuery.TolerateQueryErrors = true; + } + else + { + // + // There's only one key at this point, but we need an enumerator to get to it. + // + foreach (string log in _providersByLogMap.Keys) + { + logQuery = new EventLogQuery(log, PathType.LogName, AddProviderPredicatesToFilter(_providersByLogMap[log])); + WriteVerbose(string.Create(CultureInfo.InvariantCulture, $"Log {log} will be queried")); + } } - } - logQuery.Session = eventLogSession; - logQuery.ReverseDirection = !_oldest; ; - EventLogReader readerObj = new EventLogReader(logQuery); - if (readerObj != null) - { - ReadEvents(readerObj); + logQuery.Session = eventLogSession; + logQuery.ReverseDirection = !_oldest; + + ReadEvents(logQuery); } } - // // Process ListLog parameter set // private void ProcessListLog() { - EventLogSession eventLogSession = CreateSession(); - - foreach (string logPattern in _listLog) + using (EventLogSession eventLogSession = CreateSession()) { - bool bMatchFound = false; - - foreach (string logName in eventLogSession.GetLogNames()) + foreach (string logPattern in ListLog) { - WildcardPattern wildLogPattern = new WildcardPattern(logPattern, WildcardOptions.IgnoreCase); + bool bMatchFound = false; + WildcardPattern wildLogPattern = new(logPattern, WildcardOptions.IgnoreCase); - if (((!WildcardPattern.ContainsWildcardCharacters(logPattern)) - && string.Equals(logPattern, logName, StringComparison.CurrentCultureIgnoreCase)) - || - (wildLogPattern.IsMatch(logName))) + foreach (string logName in eventLogSession.GetLogNames()) { - try + if (((!WildcardPattern.ContainsWildcardCharacters(logPattern)) + && string.Equals(logPattern, logName, StringComparison.OrdinalIgnoreCase)) + || + (wildLogPattern.IsMatch(logName))) { - EventLogConfiguration logObj = new EventLogConfiguration(logName, eventLogSession); - - // - // Skip direct channels matching the wildcard unless -Force is present. - // - if (!Force.IsPresent && - WildcardPattern.ContainsWildcardCharacters(logPattern) && - (logObj.LogType == EventLogType.Debug || - logObj.LogType == EventLogType.Analytical)) + try { - continue; - } + EventLogConfiguration logObj = new(logName, eventLogSession); + + // + // Skip direct channels matching the wildcard unless -Force is present. + // + if (!Force.IsPresent && + WildcardPattern.ContainsWildcardCharacters(logPattern) && + (logObj.LogType == EventLogType.Debug || + logObj.LogType == EventLogType.Analytical)) + { + continue; + } - EventLogInformation logInfoObj = eventLogSession.GetLogInformation(logName, PathType.LogName); + 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)); - outputObj.Properties.Add(new PSNoteProperty("LastAccessTime", logInfoObj.LastAccessTime)); - outputObj.Properties.Add(new PSNoteProperty("LastWriteTime", logInfoObj.LastWriteTime)); - outputObj.Properties.Add(new PSNoteProperty("OldestRecordNumber", logInfoObj.OldestRecordNumber)); - outputObj.Properties.Add(new PSNoteProperty("RecordCount", logInfoObj.RecordCount)); + outputObj.Properties.Add(new PSNoteProperty("FileSize", logInfoObj.FileSize)); + outputObj.Properties.Add(new PSNoteProperty("IsLogFull", logInfoObj.IsLogFull)); + outputObj.Properties.Add(new PSNoteProperty("LastAccessTime", logInfoObj.LastAccessTime)); + outputObj.Properties.Add(new PSNoteProperty("LastWriteTime", logInfoObj.LastWriteTime)); + outputObj.Properties.Add(new PSNoteProperty("OldestRecordNumber", logInfoObj.OldestRecordNumber)); + outputObj.Properties.Add(new PSNoteProperty("RecordCount", logInfoObj.RecordCount)); - WriteObject(outputObj); - bMatchFound = true; - } - catch (Exception exc) - { - string msg = string.Format(CultureInfo.InvariantCulture, - _resourceMgr.GetString("LogInfoUnavailable"), - logName, exc.Message); - Exception outerExc = new Exception(msg, exc); - WriteError(new ErrorRecord(outerExc, "LogInfoUnavailable", ErrorCategory.NotSpecified, null)); - continue; + WriteObject(outputObj); + bMatchFound = true; + } + catch (Exception exc) + { + string msg = string.Format(CultureInfo.InvariantCulture, + _resourceMgr.GetString("LogInfoUnavailable"), + logName, exc.Message); + 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)); - WriteError(new ErrorRecord(exc, "NoMatchingLogsFound", ErrorCategory.ObjectNotFound, null)); + + if (!bMatchFound) + { + string msg = _resourceMgr.GetString("NoMatchingLogsFound"); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, ComputerName, logPattern)); + WriteError(new ErrorRecord(exc, "NoMatchingLogsFound", ErrorCategory.ObjectNotFound, null)); + } } } } @@ -666,45 +574,45 @@ private void ProcessListLog() // private void ProcessListProvider() { - EventLogSession eventLogSession = CreateSession(); - - foreach (string provPattern in _listProvider) + using (EventLogSession eventLogSession = CreateSession()) { - bool bMatchFound = false; - - foreach (string provName in eventLogSession.GetProviderNames()) + foreach (string provPattern in ListProvider) { - WildcardPattern wildProvPattern = new WildcardPattern(provPattern, WildcardOptions.IgnoreCase); + bool bMatchFound = false; + WildcardPattern wildProvPattern = new(provPattern, WildcardOptions.IgnoreCase); - if (((!WildcardPattern.ContainsWildcardCharacters(provPattern)) - && string.Equals(provPattern, provName, StringComparison.CurrentCultureIgnoreCase)) - || - (wildProvPattern.IsMatch(provName))) + foreach (string provName in eventLogSession.GetProviderNames()) { - try - { - ProviderMetadata provObj = new ProviderMetadata(provName, eventLogSession, CultureInfo.CurrentCulture); - WriteObject(provObj); - bMatchFound = true; - } - catch (System.Diagnostics.Eventing.Reader.EventLogException exc) + if (((!WildcardPattern.ContainsWildcardCharacters(provPattern)) + && string.Equals(provPattern, provName, StringComparison.OrdinalIgnoreCase)) + || + (wildProvPattern.IsMatch(provName))) { - string msg = string.Format(CultureInfo.InvariantCulture, - _resourceMgr.GetString("ProviderMetadataUnavailable"), - provName, exc.Message); - Exception outerExc = new Exception(msg, exc); - WriteError(new ErrorRecord(outerExc, "ProviderMetadataUnavailable", ErrorCategory.NotSpecified, null)); - continue; + try + { + ProviderMetadata provObj = new(provName, eventLogSession, CultureInfo.CurrentCulture); + WriteObject(provObj); + bMatchFound = true; + } + catch (System.Diagnostics.Eventing.Reader.EventLogException exc) + { + string msg = string.Format(CultureInfo.InvariantCulture, + _resourceMgr.GetString("ProviderMetadataUnavailable"), + provName, exc.Message); + Exception outerExc = new(msg, exc); + WriteError(new ErrorRecord(outerExc, "ProviderMetadataUnavailable", ErrorCategory.NotSpecified, null)); + continue; + } } } - } - if (!bMatchFound) - { - string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("NoMatchingProvidersFound"), - _computerName, provPattern); - Exception exc = new Exception(msg); - WriteError(new ErrorRecord(exc, "NoMatchingProvidersFound", ErrorCategory.ObjectNotFound, null)); + if (!bMatchFound) + { + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("NoMatchingProvidersFound"), + ComputerName, provPattern); + Exception exc = new(msg); + WriteError(new ErrorRecord(exc, "NoMatchingProvidersFound", ErrorCategory.ObjectNotFound, null)); + } } } } @@ -714,90 +622,84 @@ private void ProcessListProvider() // private void ProcessFilterXml() { - EventLogSession eventLogSession = CreateSession(); - - if (!Oldest.IsPresent) + using (EventLogSession eventLogSession = CreateSession()) { - // - // Do minimal parsing of xmlQuery to determine if any direct channels or ETL files are in it. - // - XmlElement root = _xmlQuery.DocumentElement; - XmlNodeList queryNodes = root.SelectNodes("//Query//Select"); - foreach (XmlNode queryNode in queryNodes) + if (!Oldest.IsPresent) { - XmlAttributeCollection attribs = queryNode.Attributes; - foreach (XmlAttribute attrib in attribs) + // + // Do minimal parsing of xmlQuery to determine if any direct channels or ETL files are in it. + // + XmlElement root = FilterXml.DocumentElement; + XmlNodeList queryNodes = root.SelectNodes("//Query//Select"); + foreach (XmlNode queryNode in queryNodes) { - if (attrib.Name.Equals("Path", StringComparison.OrdinalIgnoreCase)) + XmlAttributeCollection attribs = queryNode.Attributes; + foreach (XmlAttribute attrib in attribs) { - string logName = attrib.Value; - - if (logName.StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase)) + if (attrib.Name.Equals("Path", StringComparison.OrdinalIgnoreCase)) { - TerminateForNonEvtxFileWithoutOldest(logName); - } + string logName = attrib.Value; + + if (logName.StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase)) + { + TerminateForNonEvtxFileWithoutOldest(logName); + } - ValidateLogName(logName, eventLogSession); + ValidateLogName(logName, eventLogSession); + } } } } - } - EventLogQuery logQuery = new EventLogQuery(null, PathType.LogName, _xmlQuery.InnerXml); - logQuery.Session = eventLogSession; - logQuery.ReverseDirection = !_oldest; + EventLogQuery logQuery = new(null, PathType.LogName, FilterXml.InnerXml); + logQuery.Session = eventLogSession; + logQuery.ReverseDirection = !_oldest; - EventLogReader readerObj = new EventLogReader(logQuery); - if (readerObj != null) - { - ReadEvents(readerObj); + ReadEvents(logQuery); } } - // // Process FileSet parameter set // private void ProcessFile() { - EventLogSession eventLogSession = CreateSession(); - - // - // 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++) + using (EventLogSession eventLogSession = CreateSession()) { - StringCollection resolvedPaths = ValidateAndResolveFilePath(_path[i]); - foreach (string resolvedPath in resolvedPaths) + // + // 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++) { - _resolvedPaths.Add(resolvedPath); - WriteVerbose(string.Format(CultureInfo.InvariantCulture, "Found file {0}", resolvedPath)); + StringCollection resolvedPaths = ValidateAndResolveFilePath(Path[i]); + foreach (string resolvedPath in resolvedPaths) + { + _resolvedPaths.Add(resolvedPath); + WriteVerbose(string.Create(CultureInfo.InvariantCulture, $"Found file {resolvedPath}")); + } } - } - EventLogQuery logQuery = null; - if (_resolvedPaths.Count == 0) - { - return; - } - else if (_resolvedPaths.Count > 1) - { - string query = BuildStructuredQuery(eventLogSession); - logQuery = new EventLogQuery(null, PathType.FilePath, query); - logQuery.TolerateQueryErrors = true; - } - else - { - logQuery = new EventLogQuery(_resolvedPaths[0], PathType.FilePath, _filter); - } - logQuery.Session = eventLogSession; - logQuery.ReverseDirection = !_oldest; + EventLogQuery logQuery = null; + if (_resolvedPaths.Count == 0) + { + return; + } + else if (_resolvedPaths.Count > 1) + { + string query = BuildStructuredQuery(eventLogSession); + logQuery = new EventLogQuery(null, PathType.FilePath, query); + logQuery.TolerateQueryErrors = true; + } + else + { + logQuery = new EventLogQuery(_resolvedPaths[0], PathType.FilePath, FilterXPath); + } - EventLogReader readerObj = new EventLogReader(logQuery); - if (readerObj != null) - { - ReadEvents(readerObj); + logQuery.Session = eventLogSession; + logQuery.ReverseDirection = !_oldest; + + ReadEvents(logQuery); } } @@ -808,23 +710,20 @@ private void ProcessHashQuery() { CheckHashTablesForNullValues(); - EventLogSession eventLogSession = CreateSession(); - - string query = BuildStructuredQuery(eventLogSession); - if (query.Length == 0) + using (EventLogSession eventLogSession = CreateSession()) { - return; - } + string query = BuildStructuredQuery(eventLogSession); + if (query.Length == 0) + { + return; + } - EventLogQuery logQuery = new EventLogQuery(null, PathType.FilePath, query); - logQuery.Session = eventLogSession; - logQuery.TolerateQueryErrors = true; - logQuery.ReverseDirection = !_oldest; + EventLogQuery logQuery = new(null, PathType.FilePath, query); + logQuery.Session = eventLogSession; + logQuery.TolerateQueryErrors = true; + logQuery.ReverseDirection = !_oldest; - EventLogReader readerObj = new EventLogReader(logQuery); - if (readerObj != null) - { - ReadEvents(readerObj); + ReadEvents(logQuery); } } @@ -836,106 +735,108 @@ 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(EventLogReader readerObj) + private void ReadEvents(EventLogQuery logQuery) { - Int64 numEvents = 0; - EventRecord evtObj = null; - - while (true) + using (EventLogReader readerObj = new(logQuery)) { - try - { - evtObj = readerObj.ReadEvent(); - } - catch (Exception exc) - { - WriteError(new ErrorRecord(exc, exc.Message, ErrorCategory.NotSpecified, null)); - continue; - } - if (evtObj == null) - { - break; - } - if (_maxEvents != -1 && numEvents >= _maxEvents) + long numEvents = 0; + EventRecord evtObj = null; + + while (true) { - break; - } + try + { + evtObj = readerObj.ReadEvent(); + } + catch (Exception exc) + { + WriteError(new ErrorRecord(exc, exc.Message, ErrorCategory.NotSpecified, null)); + continue; + } - PSObject outputObj = new PSObject(evtObj); + if (evtObj == null) + { + break; + } - string evtMessage = _resourceMgr.GetString("NoEventMessage"); - try - { - evtMessage = evtObj.FormatDescription(); - } - catch (Exception exc) - { - WriteError(new ErrorRecord(exc, exc.Message, ErrorCategory.NotSpecified, null)); - } - outputObj.Properties.Add(new PSNoteProperty("Message", evtMessage)); + if (MaxEvents != -1 && numEvents >= MaxEvents) + { + break; + } + PSObject outputObj = new(evtObj); - // - // Enumerate the object one level to get to event payload - // - WriteObject(outputObj, true); - numEvents++; - } + string evtMessage = _resourceMgr.GetString("NoEventMessage"); + try + { + evtMessage = evtObj.FormatDescription(); + } + catch (Exception exc) + { + WriteError(new ErrorRecord(exc, exc.Message, ErrorCategory.NotSpecified, null)); + } - if (numEvents == 0) - { - string msg = _resourceMgr.GetString("NoMatchingEventsFound"); - Exception exc = new Exception(msg); - WriteError(new ErrorRecord(exc, "NoMatchingEventsFound", ErrorCategory.ObjectNotFound, null)); - } - } + outputObj.Properties.Add(new PSNoteProperty("Message", evtMessage)); + // + // Enumerate the object one level to get to event payload + // + WriteObject(outputObj, true); + numEvents++; + } + if (numEvents == 0) + { + string msg = _resourceMgr.GetString("NoMatchingEventsFound"); + 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) { @@ -955,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": @@ -978,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": @@ -989,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; } @@ -1003,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()) { @@ -1067,8 +982,8 @@ private string BuildXPathFromHashTable(Hashtable hash) // // Fix Issue #2327 added = HandleNamedDataHashValue(key, hash[key]); - } + break; } @@ -1078,9 +993,9 @@ private string BuildXPathFromHashTable(Hashtable hash) { xpathString.Append(" and "); } + xpathString.Append(added); } - } return xpathString.ToString(); @@ -1092,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); @@ -1109,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 @@ -1125,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()); } @@ -1148,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) @@ -1176,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()); } @@ -1211,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) { @@ -1241,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; } @@ -1266,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); } @@ -1284,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 @@ -1303,6 +1219,7 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession { result.Append(" and ").Append(xpathString); } + result.Append(propClose); } @@ -1319,8 +1236,7 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession result.Append(queryCloser); } - } //end foreach hashtable - + } result.Append(queryListClose); @@ -1331,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(); @@ -1360,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(); @@ -1389,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)) { @@ -1409,8 +1324,9 @@ private string HandleKeywordHashValue(Object value) { if (!KeywordStringToInt64(value.ToString(), ref keywordLong)) { - return ""; + return string.Empty; } + keywordsMask |= keywordLong; } @@ -1423,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 @@ -1439,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(); @@ -1474,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(); @@ -1498,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(); @@ -1519,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(); @@ -1548,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()); @@ -1563,7 +1476,8 @@ private string HandleDataHashValue(Object value) ret.Append(" or "); } } - ret.Append(")"); + + ret.Append(')'); } else { @@ -1573,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, @@ -1596,7 +1508,8 @@ private string HandleNamedDataHashValue(String key, Object value) ret.Append(" or "); } } - ret.Append(")"); + + ret.Append(')'); } else { @@ -1608,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. @@ -1622,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)); } } @@ -1638,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)); } } @@ -1658,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; } @@ -1667,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 { @@ -1698,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; } @@ -1720,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; } @@ -1736,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 @@ -1745,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; } @@ -1777,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; } @@ -1794,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; } @@ -1815,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)); } } @@ -1857,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 + "]"; } @@ -1872,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 + "]"; @@ -1886,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']" @@ -1925,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) { @@ -1946,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. @@ -1970,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; @@ -1982,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) @@ -1995,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 @@ -2022,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; } @@ -2048,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))) { @@ -2071,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; } @@ -2090,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)); } } @@ -2115,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 7de1cbfae41..32e0d329886 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj @@ -1,22 +1,14 @@ - - + + - 6.0.0 - netcoreapp2.0 - $(NoWarn);CS1591 - true - true - true + PowerShell's Microsoft.PowerShell.Commands.Diagnostics project + $(NoWarn);CS1591;CA1416 Microsoft.PowerShell.Commands.Diagnostics - ../signing/visualstudiopublic.snk - true - false - false - false + @@ -25,19 +17,20 @@ - + + + - - 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 3a122d8cec7..2fc58cc00e1 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/PdhSafeHandle.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/PdhSafeHandle.cs @@ -1,14 +1,9 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Runtime.InteropServices; -#if !CORECLR -using System.Runtime.ConstrainedExecution; -#endif - namespace Microsoft.Powershell.Commands.GetCounter.PdhNative { internal sealed class PdhSafeDataSourceHandle : SafeHandle @@ -23,19 +18,12 @@ public override bool IsInvalid } } - -#if !CORECLR // ReliabilityContract not supported on CoreCLR - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] -#endif protected override bool ReleaseHandle() { return (PdhHelper.PdhCloseLog(handle, 0) == 0); } } - - - internal sealed class PdhSafeQueryHandle : SafeHandle { private PdhSafeQueryHandle() : base(IntPtr.Zero, true) { } @@ -48,10 +36,6 @@ public override bool IsInvalid } } - -#if !CORECLR // ReliabilityContract not supported on CoreCLR - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] -#endif protected override bool ReleaseHandle() { return (PdhHelper.PdhCloseQuery(handle) == 0); @@ -70,15 +54,9 @@ public override bool IsInvalid } } - -#if !CORECLR // ReliabilityContract not supported on CoreCLR - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] -#endif protected override bool ReleaseHandle() { return (PdhHelper.PdhCloseLog(handle, 0) == 0); } } } - - diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/map.json b/src/Microsoft.PowerShell.Commands.Diagnostics/map.json deleted file mode 100644 index e3fdd2fdec8..00000000000 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/map.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "wmi/Events/GetEvent/src/CommonUtils.cs": "CommonUtils.cs", - "wmi/Events/GetEvent/src/CounterFileInfo.cs": "CounterFileInfo.cs", - "wmi/Events/GetEvent/src/CounterSample.cs": "CounterSample.cs", - "wmi/Events/GetEvent/src/CounterSet.cs": "CounterSet.cs", - "wmi/Events/GetEvent/src/ExportCounterCommand.cs": "ExportCounterCommand.cs", - "wmi/Events/GetEvent/src/GetCounterCommand.cs": "GetCounterCommand.cs", - "wmi/Events/GetEvent/src/GetEventCommand.cs": "GetEventCommand.cs", - "wmi/Events/GetEvent/src/GetEventSnapin.cs": "GetEventSnapin.cs", - "wmi/Events/GetEvent/src/ImportCounterCommand.cs": "ImportCounterCommand.cs", - "wmi/Events/GetEvent/src/NewWinEventCommand.cs": "NewWinEventCommand.cs", - "wmi/Events/GetEvent/src/PdhHelper.cs": "PdhHelper.cs", - "wmi/Events/GetEvent/src/PdhSafeHandle.cs": "PdhSafeHandle.cs", - "wmi/Events/GetEvent/src/resources/GetEventResources.txt": "resources/GetEventResources.txt" -} 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/AssemblyInfo.cs b/src/Microsoft.PowerShell.Commands.Management/AssemblyInfo.cs deleted file mode 100644 index dd4e8f28264..00000000000 --- a/src/Microsoft.PowerShell.Commands.Management/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.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj b/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj index c38477248e6..7d9d61ede96 100644 --- a/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj +++ b/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj @@ -1,19 +1,9 @@ - - + + - 6.0.0 - netcoreapp2.0 - $(NoWarn);CS1570 - true - true - true - true + PowerShell's Microsoft.PowerShell.Commands.Management project + $(NoWarn);CS1570;CA1416 Microsoft.PowerShell.Commands.Management - ../signing/visualstudiopublic.snk - true - false - false - false @@ -25,19 +15,15 @@ - - - - @@ -47,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 6792eb6568e..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; @@ -36,11 +37,9 @@ internal SensitiveString(int numberOfCharacters) { _string = new string('\0', numberOfCharacters); -#if !CORECLR // String.IsInterned Not In CoreCLR Debug.Assert( string.IsInterned(_string) == null, "We will overwrite string contents - we can't / shouldn't do this for interned strings."); -#endif /* The string is pinned (while still being filled with insignificant data) * to prevent copying of sensitive data by garbage collection. @@ -51,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]; + } } } @@ -100,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() { @@ -109,7 +105,7 @@ public void Dispose() } /// - /// Releases resources associated with this object + /// Releases resources associated with this object. /// private void Dispose(bool disposing) { @@ -127,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() { @@ -139,7 +135,7 @@ public void Dispose() } /// - /// Releases resources associated with this object + /// Releases resources associated with this object. /// private void Dispose(bool disposing) { @@ -149,6 +145,7 @@ private void Dispose(bool disposing) { d.Dispose(); } + _trackedDisposables.Clear(); } } @@ -174,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); @@ -185,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; } @@ -202,6 +201,7 @@ internal object ConvertFromDotNetToCim(object dotNetObject) object cimElement = ConvertFromDotNetToCim(dotNetArray.GetValue(i)); cimArray.SetValue(cimElement, i); } + return cimArray; } } @@ -227,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) @@ -245,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; @@ -276,6 +278,7 @@ internal static object ConvertFromDotNetToCim(object dotNetObject) object cimElement = ConvertFromDotNetToCim(dotNetArray.GetValue(i)); cimArray.SetValue(cimElement, i); } + return cimArray; } } @@ -340,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) { @@ -359,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); @@ -376,6 +380,7 @@ internal static object ConvertFromCimToDotNet(object cimObject, Type expectedDot object dotNetElement = ConvertFromCimToDotNet(cimArray.GetValue(i), dotNetElementType); dotNetArray.SetValue(dotNetElement, i); } + return dotNetArray; } } @@ -388,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)) { @@ -420,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 }); } @@ -448,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); }); } @@ -485,6 +492,7 @@ internal static CimType GetCimTypeEnum(Type dotNetType) { return CimType.Reference; } + if (typeof(PSReference[]).IsAssignableFrom(dotNetType)) { return CimType.ReferenceArray; @@ -511,10 +519,12 @@ internal static Type GetCimType(Type dotNetType) { return dotNetType; } + if (dotNetType == typeof(CimInstance)) { return dotNetType; } + if (dotNetType == typeof(PSReference)) { return dotNetType; @@ -571,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. /// /// /// @@ -616,6 +626,7 @@ internal static Type GetElementType(Type arrayType) { return null; } + Type elementType = arrayType.GetElementType(); if (elementType.IsArray) { @@ -668,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 3f92fb8049b..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 ($array[0] -eq $null) - { - $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 a709d21d08d..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 { @@ -92,7 +93,7 @@ public abstract class ControlPanelItemBaseCommand : PSCmdlet private static readonly string[] s_controlPanelItemFilterList = new string[] { "Folder Options", "Taskbar and Start Menu" }; private const string TestHeadlessServerScript = @" $result = $false -$serverManagerModule = Get-Module -ListAvailable | ? {$_.Name -eq 'ServerManager'} +$serverManagerModule = Get-Module -ListAvailable | Where-Object {$_.Name -eq 'ServerManager'} if ($serverManagerModule -ne $null) { Import-Module ServerManager @@ -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 4e925a30b1c..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 @@ -437,7 +430,15 @@ private static DeviceGuardInfo GetDeviceGuard(CimSession session) CIMHelper.ClassNames.DeviceGuard); if (wmiGuard != null) + { + var smartStatus = EnumConverter.Convert((int?)wmiGuard.VirtualizationBasedSecurityStatus ?? 0); + if (smartStatus != null) + { + status = (DeviceGuardSmartStatus)smartStatus; + } + guard = wmiGuard.AsOutputType; + } } return new DeviceGuardInfo @@ -473,7 +474,7 @@ private static DeviceGuardInfo GetDeviceGuard(CimSession session) } /// - /// Retrieve information related to HyperVisor + /// Retrieve information related to HyperVisor. /// /// /// A object representing @@ -485,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; @@ -527,7 +528,7 @@ private static HyperVInfo GetHyperVisorInfo(CimSession session) } /// - /// Retrieve miscellaneous system information + /// Retrieve miscellaneous system information. /// /// /// A object representing @@ -544,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; @@ -558,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; @@ -610,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 { @@ -634,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; } @@ -883,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) @@ -927,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 @@ -982,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 @@ -1043,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); @@ -1098,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. @@ -1137,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; } /// @@ -1212,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. /// @@ -1257,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)) { @@ -1337,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. /// /// @@ -1345,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; @@ -1388,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; @@ -1400,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; @@ -1411,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() @@ -1506,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 { @@ -1533,6 +1513,7 @@ public DeviceGuard AsOutputType if (temp != null) listHardware.Add(temp.Value); } + guard.RequiredSecurityProperties = listHardware.ToArray(); listHardware.Clear(); @@ -1543,6 +1524,7 @@ public DeviceGuard AsOutputType if (temp != null) listHardware.Add(temp.Value); } + guard.AvailableSecurityProperties = listHardware.ToArray(); var listSoftware = new List(); @@ -1553,6 +1535,7 @@ public DeviceGuard AsOutputType if (temp != null) listSoftware.Add(temp.Value); } + guard.SecurityServicesConfigured = listSoftware.ToArray(); listSoftware.Clear(); @@ -1563,6 +1546,7 @@ public DeviceGuard AsOutputType if (temp != null) listSoftware.Add(temp.Value); } + guard.SecurityServicesRunning = listSoftware.ToArray(); } @@ -1577,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; @@ -1589,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; @@ -1764,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; @@ -1782,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; @@ -1809,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; @@ -1820,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; @@ -1828,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 @@ -1893,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; @@ -1911,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(); @@ -1921,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; @@ -2018,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 { @@ -2069,7 +2048,7 @@ public string HotFixID } /// - /// Description of the update + /// Description of the update. /// public string Description { @@ -2079,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 { @@ -2089,7 +2068,7 @@ public string InstalledOn } /// - /// Additional comments that relate to the update + /// Additional comments that relate to the update. /// public string FixComments { @@ -2100,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; } /// @@ -2134,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. /// @@ -2184,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. /// @@ -2200,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 { @@ -2297,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; } @@ -2321,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. @@ -2355,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; } @@ -2400,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; } @@ -2540,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; } @@ -2568,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; } @@ -2582,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 @@ -2642,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. @@ -2661,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. @@ -2672,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; } @@ -2687,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; } @@ -2702,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; } @@ -2719,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; } /// @@ -2730,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; } @@ -2753,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; } @@ -2784,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; } /// @@ -2813,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. @@ -2832,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 @@ -2918,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. @@ -2934,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; } @@ -3007,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; } @@ -3020,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 @@ -3052,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; } @@ -3080,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; } @@ -3091,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; } @@ -3110,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 @@ -3307,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, @@ -3620,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. + /// + SMMSecurityMitigations = 6, + + /// + /// Mode Based Execution Control. /// - SecureMemoryOverwrite = 4 + 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 } @@ -3935,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] @@ -3959,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 } @@ -4031,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, @@ -4181,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, @@ -4221,7 +4222,7 @@ public enum OperatingSystemSKU WindowsServerEnterpriseNoHyperVCore = 41, /// - /// SKU is Microsoft Hyper-V Server + /// SKU is Microsoft Hyper-V Server. /// MicrosoftHyperVServer = 42, @@ -4246,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, @@ -4256,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, @@ -4856,7 +4857,7 @@ public enum ProcessorType CentralProcessor = 3, /// - /// Processor is a Math processor + /// Processor is a Math processor. /// MathProcessor = 4, @@ -4866,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 @@ -4912,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, @@ -4976,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 } @@ -5097,87 +5098,59 @@ public enum SoftwareElementState #endregion Output components #region Native - internal static class Native + internal static partial class Native { private static class PInvokeDllNames { -#if CORECLR 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"; -#else - public const string GetPhysicallyInstalledSystemMemoryDllName = "kernel32.dll"; - public const string LCIDToLocaleNameDllName = "kernel32.dll"; - public const string PowerDeterminePlatformRoleExDllName = "Powrprof.dll"; - public const string GetFirmwareTypeDllName = "kernel32.dll"; -#endif } public const int LOCALE_NAME_MAX_LENGTH = 85; 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 a7a24588708..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(ClrFacade.GetSafeProcessHandle(process), 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 = ClrFacade.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,11 +1165,14 @@ protected override void ProcessRecord() SafeGetProcessName(process), SafeGetProcessId(process)); - if (!ShouldProcess(targetString)) { continue; } + if (!ShouldProcess(targetString)) + { + continue; + } try { - // Many properties including Name are not available if the process has exited. + // Many properties including Name are not available if the process has exited. // If this is the case, we skip the process. If the process is from a remote // machine, then we generate a non-terminating error because .NET doesn't support // terminate a remote process. @@ -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,38 +1267,38 @@ 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; IntPtr ph = IntPtr.Zero; try { - if (Win32Native.OpenProcessToken(ClrFacade.GetSafeProcessHandle(process), TOKEN_QUERY, out ph)) + if (Win32Native.OpenProcessToken(process.Handle, TOKEN_QUERY, out ph)) { if (_currentUserName == null) { @@ -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,50 +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; - private bool _useShellExecute; - private readonly bool _isOnFullWinSku; - - /// - /// Constructor - /// - public StartProcessCommand() - { - _isOnFullWinSku = Platform.IsWindows && !Platform.IsNanoServer && !Platform.IsIoT; - _useShellExecute = _isOnFullWinSku; - } - #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")] @@ -1707,183 +1633,239 @@ public StartProcessCommand() [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; } - set + 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; } + 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() { string message = string.Empty; // -Verb and -WindowStyle are not supported on non-Windows platforms as well as Windows headless SKUs - if (_isOnFullWinSku) + if (Platform.IsWindowsDesktop) { // Parameters '-NoNewWindow' and '-WindowStyle' are both valid on full windows SKUs. 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; } @@ -1901,15 +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(); + 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( @@ -1920,75 +1903,82 @@ 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")) { if (_isDefaultSetParameterSpecified) { - _useShellExecute = false; 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 !CORECLR - //WindowStyle + + if (_environment != null) + { + LoadEnvironmentVariable(startInfo, _environment); + } + startInfo.WindowStyle = _windowstyle; -#endif - //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 = "."; } @@ -1996,77 +1986,151 @@ 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; } } } -#if !CORECLR // Properties 'Verb' and 'WindowStyle' are missing in CoreCLR else if (ParameterSetName.Equals("UseShellExecute")) { - //Verb - if (Verb != null) { startInfo.Verb = Verb; } - //WindowStyle + if (Verb != null) + { + startInfo.Verb = Verb; + } + startInfo.WindowStyle = _windowstyle; } + + 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 - //Starts the Process - Process process = Start(startInfo); + 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) @@ -2076,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); } } @@ -2088,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 } @@ -2112,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); } } @@ -2120,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() { @@ -2143,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) @@ -2180,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 (_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 @@ -2230,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(); @@ -2239,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(); @@ -2259,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) @@ -2323,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 + { + sf = File.OpenHandle(RedirectionPath, mode, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Inheritable, FileOptions.WriteThrough); + } + catch (Win32Exception win32ex) { - int error = Marshal.GetLastWin32Error(); - Win32Exception win32ex = new Win32Exception(error); + 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; } @@ -2381,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 (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) { @@ -2517,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(); @@ -2529,20 +2572,21 @@ private Process StartWithCreateProcess(ProcessStartInfo startinfo) } else if (error == 0x424) { - // The API 'CreateProcessWithLogonW' depends on the 'Secondary Logon' service, but the component 'Microsoft-Windows-SecondaryLogonService' + // The API 'CreateProcessWithLogonW' depends on the 'Secondary Logon' service, but the component 'Microsoft-Windows-SecondaryLogonService' // is not installed in OneCoreUAP. We will get error code 0x424 when the service is not available. message = StringUtil.Format(ProcessResources.ParameterNotSupported, "-Credential", "Start-Process"); er = new ErrorRecord(new NotSupportedException(message), "NotSupportedException", ErrorCategory.NotInstalled, null); } 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 @@ -2552,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; + + 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(); - ProcessNativeMethods.SECURITY_ATTRIBUTES lpThreadAttributes = new ProcessNativeMethods.SECURITY_ATTRIBUTES(); - flag = ProcessNativeMethods.CreateProcess(null, cmdLine, lpProcessAttributes, lpThreadAttributes, true, creationFlags, AddressOfEnvironmentBlock, startinfo.WorkingDirectory, lpStartupInfo, lpProcessInformation); + 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 { @@ -2581,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. @@ -2595,147 +2655,139 @@ private Process StartWithShellExecute(ProcessStartInfo startInfo) Process result = null; try { -#if CORECLR - result = ShellExecuteHelper.Start(startInfo, WindowStyle, Verb); -#else result = Process.Start(startInfo); -#endif } 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; + + 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. + /// Returns completion results for verb parameter. /// - private Microsoft.PowerShell.Commands.SafeJobHandle _jobObjectHandle; - - /// - /// ProcessCollection constructor. - /// - 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; + } - // Methods - // static NativeMethods(); + public void Resume() + { + ProcessNativeMethods.ResumeThread(Thread.DangerousGetHandle()); + } + + 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, @@ -2746,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, @@ -2758,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 @@ -2783,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; @@ -2803,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; @@ -2839,6 +2899,7 @@ internal class STARTUPINFO public SafeFileHandle hStdInput; public SafeFileHandle hStdOutput; public SafeFileHandle hStdError; + public STARTUPINFO() { this.lpReserved = IntPtr.Zero; @@ -2860,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(); @@ -2879,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. /// /// /// @@ -2987,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 213ac8cf27c..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 - TimeZoneHelper.ClearCachedData(); + 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,22 +153,22 @@ 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() { // make sure we've got fresh data, in case the requested time zone was added // to the system (registry) after our process was started - TimeZoneHelper.ClearCachedData(); + TimeZoneInfo.ClearCachedData(); // acquire a TimeZoneInfo if one wasn't supplied. if (this.ParameterSetName.Equals("Id", StringComparison.OrdinalIgnoreCase)) @@ -172,15 +177,7 @@ protected override void ProcessRecord() { InputObject = TimeZoneInfo.FindSystemTimeZoneById(Id); } -#if CORECLR - // TimeZoneNotFoundException is thrown by TimeZoneInfo, but not - // publicly visible (so can't be caught), so for now we're catching - // the parent exception time. This should be removed once the more - // specific exception is available. - catch (Exception e) -#else catch (TimeZoneNotFoundException e) -#endif { ThrowTerminatingError(new ErrorRecord( e, @@ -193,24 +190,17 @@ 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); -#if CORECLR - // Because .NET Core does not currently expose the TimeZoneNotFoundException - // we need to throw the more generic parent exception class for the time being. - // This should be removed once the correct exception class is available. - Exception e = new Exception(message); -#else Exception e = new TimeZoneNotFoundException(message); -#endif ThrowTerminatingError(new ErrorRecord(e, TimeZoneHelper.TimeZoneNotFoundError, ErrorCategory.InvalidArgument, "Name")); } - else if (1 < timeZones.Length) + else if (timeZones.Length > 1) { string message = string.Format(CultureInfo.InvariantCulture, TimeZoneResources.MultipleMatchingTimeZones, Name); @@ -233,15 +223,7 @@ protected override void ProcessRecord() // a backing system time zone, otherwise it's an error condition InputObject = TimeZoneInfo.FindSystemTimeZoneById(InputObject.Id); } -#if CORECLR - // TimeZoneNotFoundException is thrown by TimeZoneInfo, but not - // publicly visible (so can't be caught), so for now we're catching - // the parent exception time. This should be removed once the more - // specific exception is available. - catch (Exception e) -#else catch (TimeZoneNotFoundException e) -#endif { ThrowTerminatingError(new ErrorRecord( e, @@ -274,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(); @@ -299,17 +281,15 @@ protected override void ProcessRecord() ThrowWin32Error(); } -#if !CORECLR // broadcast a WM_SETTINGCHANGE notification message to all top-level windows so that they // know to update their notion of the current system time (and time zone) if applicable int result = 0; NativeMethods.SendMessageTimeout((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_SETTINGCHANGE, (IntPtr)0, "intl", NativeMethods.SMTO_ABORTIFHUNG, 5000, ref result); -#endif // clear the time zone data or this PowerShell session // will not recognize the new time zone settings - TimeZoneHelper.ClearCachedData(); + TimeZoneInfo.ClearCachedData(); if (PassThru.IsPresent) { @@ -342,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. @@ -365,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, @@ -395,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) @@ -412,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, @@ -446,22 +426,14 @@ 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 - -#if CORECLR 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"; private const string GetCurrentProcessApiDllName = "api-ms-win-downlevel-kernel32-l1-1-0.dll"; @@ -471,24 +443,13 @@ private NativeMethods() private const string AdjustTokenPrivilegesApiDllName = "api-ms-win-downlevel-advapi32-l1-1-1.dll"; 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"; -#else - private const string SetDynamicTimeZoneApiDllName = "kernel32.dll"; - private const string GetTimeZoneInformationForYearApiDllName = "kernel32.dll"; - private const string GetCurrentProcessApiDllName = "kernel32.dll"; - private const string OpenProcessTokenApiDllName = "advapi32.dll"; - private const string LookupPrivilegeTokenApiDllName = "advapi32.dll"; - private const string PrivilegeCheckApiDllName = "advapi32.dll"; - private const string AdjustTokenPrivilegesApiDllName = "advapi32.dll"; - private const string CloseHandleApiDllName = "kernel32.dll"; - private const string SendMessageTimeoutApiDllName = "user32.dll"; -#endif -#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 @@ -536,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 @@ -587,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 @@ -628,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. /// /// /// @@ -681,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. /// /// /// @@ -692,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. /// /// /// @@ -702,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. /// /// /// @@ -719,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. /// /// /// @@ -728,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 @@ -740,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 @@ -750,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. /// /// /// @@ -783,42 +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 - - /// - /// Clear the cached TimeZoneInfo data. Note that since TimeZoneInfo.ClearCachedData - /// does not exist in .NET Core there is an alternative mechanism (which doesn't seem - /// to affect clearing out the cache under the full desktop version of .NET) - /// - internal static void ClearCachedData() - { -#if CORECLR - // NOTE: .NET Core does not expose the ClearCachedData method, but executing - // the specific ConvertTime request below should have the same effect. - TimeZoneInfo.ConvertTime(new DateTime(0), TimeZoneInfo.Local); -#else - TimeZoneInfo.ClearCachedData(); -#endif - } + #endregion Error Ids /// /// Find the system time zone by checking first against StandardName and then, @@ -828,8 +774,8 @@ internal static void ClearCachedData() /// 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/map.json b/src/Microsoft.PowerShell.Commands.Management/map.json deleted file mode 100644 index e16a1bad159..00000000000 --- a/src/Microsoft.PowerShell.Commands.Management/map.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "monad/src/cimSupport/cmdletization/cim/QueryJob.cs": "cimSupport/cmdletization/cim/QueryJob.cs", - "monad/src/commands/management/Computer.cs": "commands/management/Computer.cs", - "monad/src/commands/management/PropertyCommandBase.cs": "commands/management/PropertyCommandBase.cs", - "monad/src/commands/management/resources/CmdletizationResources.resx": "resources/CmdletizationResources.resx", - "monad/src/cimSupport/cmdletization/cim/cimOperationOptionsHelper.cs": "cimSupport/cmdletization/cim/cimOperationOptionsHelper.cs", - "monad/src/cimSupport/cmdletization/cim/PropertySettingJob.cs": "cimSupport/cmdletization/cim/PropertySettingJob.cs", - "monad/src/commands/management/SetWMIInstanceCommand.cs": "commands/management/SetWMIInstanceCommand.cs", - "monad/src/commands/management/GetContentCommand.cs": "commands/management/GetContentCommand.cs", - "monad/src/cimSupport/cmdletization/cim/CreateInstanceJob.cs": "cimSupport/cmdletization/cim/CreateInstanceJob.cs", - "monad/src/commands/management/ClearRecycleBinCommand.cs": "commands/management/ClearRecycleBinCommand.cs", - "monad/src/commands/management/WriteContentCommandBase.cs": "commands/management/WriteContentCommandBase.cs", - "monad/src/commands/management/GetPropertyCommand.cs": "commands/management/GetPropertyCommand.cs", - "monad/src/cimSupport/cmdletization/cim/DeleteInstanceJob.cs": "cimSupport/cmdletization/cim/DeleteInstanceJob.cs", - "monad/src/commands/management/InvokeWMIMethodCommand.cs": "commands/management/InvokeWMIMethodCommand.cs", - "monad/src/commands/management/resources/HotFixResources.resx": "resources/HotFixResources.resx", - "monad/src/commands/management/ClearContentCommand.cs": "commands/management/ClearContentCommand.cs", - "monad/src/commands/management/resources/ProcessCommandHelpResources.resx": "resources/ProcessCommandHelpResources.resx", - "monad/src/commands/management/resources/ComputerResources.resx": "resources/ComputerResources.resx", - "monad/src/commands/management/resources/ComputerInfoResources.resx": "resources/ComputerInfoResources.resx", - "monad/src/commands/management/SetClipboardCommand.cs": "commands/management/SetClipboardCommand.cs", - "monad/src/commands/management/ResolvePathCommand.cs": "commands/management/ResolvePathCommand.cs", - "monad/src/commands/management/resources/ProcessResources.resx": "resources/ProcessResources.resx", - "monad/src/commands/management/NewPropertyCommand.cs": "commands/management/NewPropertyCommand.cs", - "monad/src/commands/management/AddContentCommand.cs": "commands/management/AddContentCommand.cs", - "monad/src/cimSupport/cmdletization/cim/cimWrapper.cs": "cimSupport/cmdletization/cim/cimWrapper.cs", - "monad/src/commands/management/RemoveWMIObjectCommand.cs": "commands/management/RemoveWMIObjectCommand.cs", - "monad/src/cimSupport/cmdletization/cim/cimCmdletDefinitionContext.cs": "cimSupport/cmdletization/cim/cimCmdletDefinitionContext.cs", - "monad/src/cimSupport/cmdletization/cim/QueryJobBase.cs": "cimSupport/cmdletization/cim/QueryJobBase.cs", - "monad/src/commands/management/resources/ManagementMshSnapInResources.resx": "resources/ManagementMshSnapInResources.resx", - "monad/src/commands/management/GetClipboardCommand.cs": "commands/management/GetClipboardCommand.cs", - "monad/src/commands/management/WebServiceProxy.cs": "commands/management/WebServiceProxy.cs", - "monad/src/commands/management/GetComputerInfoCommand.cs": "commands/management/GetComputerInfoCommand.cs", - "monad/src/singleshell/installer/MshManagementMshSnapin.cs": "singleshell/installer/MshManagementMshSnapin.cs", - "monad/src/commands/management/ControlPanelItemCommand.cs": "commands/management/ControlPanelItemCommand.cs", - "monad/src/commands/management/PassThroughContentCommandBase.cs": "commands/management/PassThroughContentCommandBase.cs", - "monad/src/commands/management/PassThroughPropertyCommandBase.cs": "commands/management/PassThroughPropertyCommandBase.cs", - "monad/src/commands/management/resources/ClearRecycleBinResources.resx": "resources/ClearRecycleBinResources.resx", - "monad/src/commands/management/resources/TransactionResources.resx": "resources/TransactionResources.resx", - "monad/src/commands/management/resources/ServiceResources.resx": "resources/ServiceResources.resx", - "monad/src/commands/management/RegisterWMIEventCommand.cs": "commands/management/RegisterWMIEventCommand.cs", - "monad/src/commands/management/resources/NavigationResources.resx": "resources/NavigationResources.resx", - "monad/src/cimSupport/cmdletization/cim/cimConverter.cs": "cimSupport/cmdletization/cim/cimConverter.cs", - "monad/src/commands/management/CombinePathCommand.cs": "commands/management/CombinePathCommand.cs", - "monad/src/cimSupport/cmdletization/cim/clientSideQuery.cs": "cimSupport/cmdletization/cim/clientSideQuery.cs", - "monad/src/cimSupport/cmdletization/cim/cimJobContext.cs": "cimSupport/cmdletization/cim/cimJobContext.cs", - "monad/src/commands/management/RollbackTransactionCommand.cs": "commands/management/RollbackTransactionCommand.cs", - "monad/src/cimSupport/cmdletization/SessionBasedWrapper.cs": "cimSupport/cmdletization/SessionBasedWrapper.cs", - "monad/src/commands/management/SetPropertyCommand.cs": "commands/management/SetPropertyCommand.cs", - "monad/src/commands/management/MovePropertyCommand.cs": "commands/management/MovePropertyCommand.cs", - "monad/src/commands/management/resources/EventlogResources.resx": "resources/EventlogResources.resx", - "monad/src/cimSupport/cmdletization/cim/InstanceMethodInvocationJob.cs": "cimSupport/cmdletization/cim/InstanceMethodInvocationJob.cs", - "monad/src/commands/management/UseTransactionCommand.cs": "commands/management/UseTransactionCommand.cs", - "monad/src/commands/management/WMIHelper.cs": "commands/management/WMIHelper.cs", - "monad/src/commands/management/resources/WebServiceResources.resx": "resources/WebServiceResources.resx", - "monad/src/commands/management/Eventlog.cs": "commands/management/Eventlog.cs", - "monad/src/cimSupport/cmdletization/cim/ExtrinsicMethodInvocationJob.cs": "cimSupport/cmdletization/cim/ExtrinsicMethodInvocationJob.cs", - "monad/src/commands/management/Navigation.cs": "commands/management/Navigation.cs", - "monad/src/cimSupport/cmdletization/cim/MethodInvocationJobBase.cs": "cimSupport/cmdletization/cim/MethodInvocationJobBase.cs", - "monad/src/commands/management/GetWMIObjectCommand.cs": "commands/management/GetWMIObjectCommand.cs", - "monad/src/commands/management/PingPathCommand.cs": "commands/management/PingPathCommand.cs", - "monad/src/commands/management/Service.cs": "commands/management/Service.cs", - "monad/src/commands/management/Hotfix.cs": "commands/management/Hotfix.cs", - "monad/src/commands/management/resources/WmiResources.resx": "resources/WmiResources.resx", - "monad/src/cimSupport/cmdletization/cim/ModifyInstanceJob.cs": "cimSupport/cmdletization/cim/ModifyInstanceJob.cs", - "monad/src/commands/management/GetChildrenCommand.cs": "commands/management/GetChildrenCommand.cs", - "monad/src/commands/management/ConvertPathCommand.cs": "commands/management/ConvertPathCommand.cs", - "monad/src/commands/management/resources/ControlPanelResources.resx": "resources/ControlPanelResources.resx", - "monad/src/commands/management/Process.cs": "commands/management/Process.cs", - "monad/src/commands/management/GetTransactionCommand.cs": "commands/management/GetTransactionCommand.cs", - "monad/src/commands/management/SetContentCommand.cs": "commands/management/SetContentCommand.cs", - "monad/src/commands/management/ClearPropertyCommand.cs": "commands/management/ClearPropertyCommand.cs", - "monad/src/commands/management/CommitTransactionCommand.cs": "commands/management/CommitTransactionCommand.cs", - "monad/src/commands/management/StartTransactionCommand.cs": "commands/management/StartTransactionCommand.cs", - "monad/src/commands/management/ContentCommandBase.cs": "commands/management/ContentCommandBase.cs", - "monad/src/commands/management/RenamePropertyCommand.cs": "commands/management/RenamePropertyCommand.cs", - "monad/src/cimSupport/cmdletization/cim/StaticMethodInvocationJob.cs": "cimSupport/cmdletization/cim/StaticMethodInvocationJob.cs", - "monad/src/commands/management/RemovePropertyCommand.cs": "commands/management/RemovePropertyCommand.cs", - "monad/src/cimSupport/cmdletization/cim/cimQuery.cs": "cimSupport/cmdletization/cim/cimQuery.cs", - "monad/src/commands/management/resources/ClipboardResources.resx": "resources/ClipboardResources.resx", - "monad/src/commands/management/CIMHelper.cs": "commands/management/CIMHelper.cs", - "monad/src/cimSupport/cmdletization/cim/CimJobException.cs": "cimSupport/cmdletization/cim/CimJobException.cs", - "monad/src/cimSupport/cmdletization/cim/EnumerateAssociatedInstancesJob.cs": "cimSupport/cmdletization/cim/EnumerateAssociatedInstancesJob.cs", - "monad/src/cimSupport/cmdletization/cim/cimCmdletInvocationContext.cs": "cimSupport/cmdletization/cim/cimCmdletInvocationContext.cs", - "monad/src/commands/management/ParsePathCommand.cs": "commands/management/ParsePathCommand.cs", - "monad/src/commands/management/CopyPropertyCommand.cs": "commands/management/CopyPropertyCommand.cs", - "monad/src/commands/management/TimeZoneCommands.cs": "commands/management/TimeZoneCommands.cs", - "monad/src/commands/management/resources/TimeZoneResources.resx": "resources/TimeZoneResources.resx", - "monad/src/cimSupport/cmdletization/cim/TerminatingErrorTracker.cs": "cimSupport/cmdletization/cim/TerminatingErrorTracker.cs", - "monad/src/cimSupport/cmdletization/cim/cimChildJobBase.cs": "cimSupport/cmdletization/cim/cimChildJobBase.cs" -} 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 1bea462d7f4..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 { @@ -189,7 +190,7 @@ public sealed class ImportPSSessionCommand : ImplicitRemotingCommandBase $sourceIdentifier = [system.management.automation.wildcardpattern]::Escape($eventSubscriber.SourceIdentifier) Unregister-Event -SourceIdentifier $sourceIdentifier -Force -ErrorAction SilentlyContinue - if ($previousScript -ne $null) + if ($null -ne $previousScript) { & $previousScript $args } @@ -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); @@ -2139,11 +2165,11 @@ function Set-PSImplicitRemotingSession [Parameter(Mandatory = $false, Position = 1)] [bool] $createdByModule = $false) - if ($PSSession -ne $null) + if ($null -ne $PSSession) {{ $script:PSSession = $PSSession - if ($createdByModule -and ($script:PSSession -ne $null)) + if ($createdByModule -and ($null -ne $script:PSSession)) {{ $moduleName = Get-PSImplicitRemotingModuleName $script:PSSession.Name = '{0}' -f $moduleName @@ -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); @@ -2184,7 +2211,7 @@ private void GenerateHelperFunctionsSetImplicitRunspace(TextWriter writer) private const string HelperFunctionsGetSessionOptionTemplate = @" function Get-PSImplicitRemotingSessionOption {{ - if ($PSSessionOptionOverride -ne $null) + if ($null -ne $PSSessionOptionOverride) {{ return $PSSessionOptionOverride }} @@ -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(") "); } @@ -2336,14 +2375,15 @@ function Get-PSImplicitRemotingSession $savedImplicitRemotingHash = '{4}' - if (($script:PSSession -eq $null) -or ($script:PSSession.Runspace.RunspaceStateInfo.State -ne 'Opened')) + if (($null -eq $script:PSSession) -or ($script:PSSession.Runspace.RunspaceStateInfo.State -ne 'Opened')) {{ Set-PSImplicitRemotingSession ` (& $script:GetPSSession ` -InstanceId {0} ` -ErrorAction SilentlyContinue ) }} - if (($script:PSSession -ne $null) -and ($script:PSSession.Runspace.RunspaceStateInfo.State -eq 'Disconnected')) + + 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. Set-PSImplicitRemotingSession ` @@ -2351,7 +2391,8 @@ function Get-PSImplicitRemotingSession -Session $script:PSSession ` -ErrorAction SilentlyContinue) }} - if (($script:PSSession -eq $null) -or ($script:PSSession.Runspace.RunspaceStateInfo.State -ne 'Opened')) + + 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 (($script:PSSession -eq $null) -or ($script:PSSession.Runspace.RunspaceStateInfo.State -ne 'Opened')) + + 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 9cfecaf081e..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)) @@ -67,13 +70,12 @@ public string Culture return; } -#if !CORECLR //TODO: CORECLR 'new CultureInfo(Int32)' not available yet. int cultureNumber; string trimmedValue = value.Trim(); 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); @@ -86,22 +88,24 @@ public string Culture _cultureInfo = new CultureInfo(cultureNumber); return; } -#endif + _cultureInfo = new CultureInfo(value); } } + internal CultureInfo _cultureInfo = null; /// - /// /// /// [Parameter] public SwitchParameter CaseSensitive { get { return _caseSensitive; } + set { _caseSensitive = value; } } + private bool _caseSensitive; #endregion Parameters } @@ -114,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. @@ -142,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 { @@ -162,9 +167,7 @@ internal CultureInfo ConvertedCulture #endregion Internal Properties /// - /// - /// Simply accumulates the incoming objects - /// + /// Simply accumulates the incoming objects. /// protected override void ProcessRecord() { @@ -180,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 { @@ -202,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, @@ -211,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()) : @@ -221,6 +224,7 @@ private static void ProcessExpressionParameter( { expr = GetDefaultKeyPropertySet(inputObjects[0]); } + if (expr != null) { List unexpandedParameterList = processor.ProcessParameters(expr, invocationContext); @@ -234,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()) : @@ -248,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); } @@ -271,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) @@ -294,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; @@ -324,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; @@ -356,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, @@ -375,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); } @@ -415,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( @@ -449,6 +453,7 @@ private static OrderByPropertyComparer CreateComparer( } } } + OrderByPropertyComparer comparer = OrderByPropertyComparer.CreateComparer(orderMatrix, ascending, ascendingOverrides, cultureInfo, caseSensitive); @@ -473,7 +478,7 @@ bool caseSensitive } /// - /// OrderByProperty constructor. + /// Initializes a new instance of the class. /// internal OrderByProperty() { @@ -484,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. @@ -502,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); @@ -521,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; @@ -537,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) { @@ -585,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) { @@ -595,7 +602,7 @@ private static void EvaluateSortingExpression( } else { - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( r.Exception, "ExpressionEvaluation", ErrorCategory.InvalidResult, @@ -603,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; @@ -622,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) { @@ -653,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; @@ -666,6 +674,7 @@ internal static OrderByPropertyComparer CreateComparer(List maxEntries) maxEntries = entry.orderValues.Count; } + if (maxEntries == 0) return null; @@ -687,10 +696,10 @@ internal static OrderByPropertyComparer CreateComparer(List + internal sealed class IndexedOrderByPropertyComparer : IComparer { internal IndexedOrderByPropertyComparer(OrderByPropertyComparer orderByPropertyComparer) { @@ -704,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 @@ -715,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 d726eaa870e..6633a66f96f 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnblockFile.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnblockFile.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -8,8 +7,14 @@ using System.Collections.Generic; using System.Collections.ObjectModel; 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 @@ -17,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")] @@ -31,6 +41,7 @@ public string[] Path { return _paths; } + set { _paths = value; @@ -38,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 { @@ -49,6 +60,7 @@ public string[] LiteralPath { return _paths; } + set { _paths = value; @@ -62,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) { @@ -98,7 +110,7 @@ protected override void ProcessRecord() { if (!WildcardPattern.ContainsWildcardCharacters(path)) { - ErrorRecord errorRecord = new ErrorRecord(e, + ErrorRecord errorRecord = new(e, "FileNotFound", ErrorCategory.ObjectNotFound, path); @@ -107,15 +119,47 @@ protected override void ProcessRecord() } } } +#if !UNIX // Unblock files foreach (string path in pathsToProcess) { if (ShouldProcess(path)) { - AlternateDataStreamUtilities.DeleteFileStream(path, "Zone.Identifier"); + try + { + AlternateDataStreamUtilities.DeleteFileStream(path, "Zone.Identifier"); + } + catch (Exception e) + { + 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 } /// @@ -136,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, @@ -145,11 +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 } -} \ No newline at end of file +} 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 7f17c24f07d..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,34 +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; } - private WebCmdletElementCollection _inputFields; + /// + /// Gets the encoding of the text body content of this response. + /// + /// + /// Encoding of the response body from the Content-Type header, + /// or if the encoding could not be determined. + /// + public Encoding? Encoding { get; private set; } + + private WebCmdletElementCollection? _inputFields; /// - /// gets the Fields property + /// Gets the HTML input field elements parsed from . /// public WebCmdletElementCollection InputFields { @@ -36,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")); @@ -52,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 { @@ -63,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")); @@ -79,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 { @@ -90,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")); @@ -108,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)); @@ -172,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; @@ -214,24 +230,22 @@ private void ParseAttributes(string outerHtml, PSObject elementObject) } } - /// - /// Reads the response content from the web response. - /// - private 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)) - { - // fill the Content buffer - string characterSet = WebResponseHelper.GetCharacterSet(BaseResponse); - this.Content = StreamHelper.DecodeStream(RawContentStream, characterSet); - } - 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 0559e35daa5..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,29 +252,30 @@ 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; + xrs.XmlResolver = null; 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); @@ -155,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) @@ -260,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) { @@ -315,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 eef7da897e4..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,58 +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; -#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. @@ -60,650 +50,98 @@ public class ConvertToJsonCommand : PSCmdlet [Parameter] public SwitchParameter Compress { get; set; } - #endregion parameters - - #region overrides - - /// - /// Prerequisite checks - /// - 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 (!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 + /// 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. /// - /// - /// - 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; - } + [Parameter] + public SwitchParameter EnumsAsStrings { get; set; } /// - /// Convert the quoted string. + /// 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. /// - /// - /// - /// - /// - 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) - { - 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; - } - - /// - /// 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 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) + protected override void ProcessRecord() { - 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; + _inputObjects.Add(InputObject); } /// - /// 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. + /// Do the conversion to json and write output. /// - /// 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. + /// Process the Ctrl+C signal. /// - /// - /// - /// - private object ProcessEnumerable(IEnumerable enumerable, int depth) + protected override void StopProcessing() { - 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. - /// - /// - /// - /// - private object ProcessCustomObject(object o, int depth) - { - 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 261b4c98b7e..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs +++ /dev/null @@ -1,52 +0,0 @@ -#if CORECLR - -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Text; -using System.Linq; -using System.Net.Http; - -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(); - } - - foreach (var entry in response.Headers) - { - raw.AppendFormat("{0}: {1}", entry.Key, entry.Value.FirstOrDefault()); - 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 09879c6e3e3..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs +++ /dev/null @@ -1,74 +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 Methods - - 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 7d0d6c2d3bf..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, 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 30cc837933b..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs +++ /dev/null @@ -1,816 +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; } - - #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; - } - - /* - TODO: HttpClientHandler will support client certificate in RTM - See https://github.com/dotnet/corefx/issues/7623 for more details. - if (null != WebSession.Certificates) - { - handler.ClientCertificates = WebSession.Certificates; - }*/ - - if (SkipCertificateCheck) - { - handler.ServerCertificateCustomValidationCallback = delegate { return true; }; - } - - // 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; - } - 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 - { - 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 79fc400af7f..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs +++ /dev/null @@ -1,124 +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; - } - - 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 8b3911810c5..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,180 +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"); - } - } - - internal static string OS - { - get - { -#if CORECLR - return System.Runtime.InteropServices.RuntimeInformation.OSDescription; -#else - OperatingSystem os = Environment.OSVersion; - string platform = GetOSName(os.Platform); - string formattedOS = string.Format(CultureInfo.InvariantCulture, - "{0} {1}.{2}", platform, os.Version.Major, os.Version.Minor); - return (formattedOS); -#endif - } - } + 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 Culture - { - get - { - return (CultureInfo.CurrentCulture.Name); + 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; + } } } -#if !CORECLR - private static string GetOSName(PlatformID platformId) - { - string platform; - switch (platformId) - { - case PlatformID.Win32NT: // is this really the only valid option? - platform = "Windows NT"; - break; - case PlatformID.Win32Windows: - platform = "Windows"; - break; - case PlatformID.Win32S: - case PlatformID.WinCE: - case PlatformID.Xbox: - case PlatformID.MacOSX: - case PlatformID.Unix: - default: - // TODO: should we be doing something more robust here? - platform = platformId.ToString(); - break; - } + internal static string OS => RuntimeInformation.OSDescription.Trim(); - return (platform); - } -#endif + 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 0c905c2f968..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,19 +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.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 { @@ -23,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 { @@ -105,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); } /// - /// /// /// /// @@ -131,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() @@ -155,7 +125,6 @@ public override int ReadByte() } /// - /// /// /// public override void SetLength(long value) @@ -165,7 +134,6 @@ public override void SetLength(long value) } /// - /// /// /// public override byte[] ToArray() @@ -175,7 +143,6 @@ public override byte[] ToArray() } /// - /// /// /// /// @@ -187,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) @@ -211,7 +176,6 @@ public override void WriteByte(byte value) } /// - /// /// /// public override void WriteTo(Stream stream) @@ -221,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) @@ -290,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 @@ -325,87 +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); } - internal static string DecodeStream(Stream stream, string characterSet) + private static string StreamToString(Stream stream, Encoding encoding, TimeSpan perReadTimeout, CancellationToken cancellationToken) { - Encoding encoding = ContentHelper.GetEncodingOrDefault(characterSet); - return DecodeStream(stream, encoding); - } - - internal static string DecodeStream(Stream stream, Encoding encoding) - { - if (null == encoding) - { - // just use the default encoding if one wasn't provided - encoding = ContentHelper.GetDefaultEncoding(); - } - - StringBuilder result = new StringBuilder(capacity: ChunkSize); + StringBuilder result = new(capacity: ChunkSize); Decoder decoder = encoding.GetDecoder(); int useBufferSize = 64; @@ -413,103 +430,127 @@ internal static string DecodeStream(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 Byte[] EncodeToBytes(String str, Encoding encoding) - { - if (null == encoding) + return result.ToString(); + } + finally { - // just use the default encoding if one wasn't provided - encoding = ContentHelper.GetDefaultEncoding(); + ArrayPool.Shared.Return(chars); + ArrayPool.Shared.Return(bytes); } - - return encoding.GetBytes(str); } - internal static Byte[] EncodeToBytes(String str) + internal static string DecodeStream(Stream stream, string? characterSet, out Encoding encoding, TimeSpan perReadTimeout, CancellationToken cancellationToken) { - return EncodeToBytes(str, null); - } - -#if CORECLR - internal static Stream GetResponseStream(HttpResponseMessage response) - { - Stream responseStream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult(); - var contentEncoding = response.Content.Headers.ContentEncoding; + bool isDefaultEncoding = !TryGetEncoding(characterSet, out encoding); - // 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) + string content = StreamToString(stream, encoding, perReadTimeout, cancellationToken); + if (isDefaultEncoding) { - if (contentEncoding.Contains("gzip")) + // 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)); + + // Check for a charset attribute on the meta element to override the default + Match match = s_metaRegex.Match(substring); + + // Check for a encoding attribute on the xml declaration to override the default + if (!match.Success) { - responseStream = new GZipStream(responseStream, CompressionMode.Decompress); + match = s_xmlRegex.Match(substring); } - else if (contentEncoding.Contains("deflate")) + + if (match.Success) { - responseStream = new DeflateStream(responseStream, CompressionMode.Decompress); + characterSet = match.Groups["charset"].Value; + + if (TryGetEncoding(characterSet, out Encoding localEncoding)) + { + stream.Seek(0, SeekOrigin.Begin); + content = StreamToString(stream, localEncoding, perReadTimeout, cancellationToken); + encoding = localEncoding; + } } } - return responseStream; + return content; } -#else - internal static Stream GetResponseStream(WebResponse response) - { - 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) + internal static bool TryGetEncoding(string? characterSet, out Encoding encoding) + { + bool result = false; + try { - 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); - } + encoding = Encoding.GetEncoding(characterSet!); + result = true; + } + catch (ArgumentException) + { + // Use the default encoding if one wasn't provided + encoding = ContentHelper.GetDefaultEncoding(); } - return responseStream; + return result; } -#endif + + 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 + ); + + 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 + ); + + internal static byte[] EncodeToBytes(string str, Encoding encoding) + { + // Just use the default encoding if one wasn't provided + encoding ??= ContentHelper.GetDefaultEncoding(); + + return encoding.GetBytes(str); + } + + 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 71dfd9e2758..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,15 +256,10 @@ private void CreateFileStream() _xw.Dispose(); _xw = null; } + _fs.Dispose(); _fs = null; } - // reset the read-only attribute - if (null != _readOnlyFileInfo) - { - _readOnlyFileInfo.Attributes |= FileAttributes.ReadOnly; - _readOnlyFileInfo = null; - } } #endregion file @@ -253,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; } @@ -275,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 @@ -316,7 +331,7 @@ public String[] LiteralPath private bool _disposed = false; /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { @@ -328,6 +343,7 @@ public void Dispose() _helper.Dispose(); _helper = null; } + _disposed = true; } } @@ -337,7 +353,7 @@ public void Dispose() private ImportXmlHelper _helper; /// - /// ProcessRecord overload + /// ProcessRecord overload. /// protected override void ProcessRecord() { @@ -352,7 +368,6 @@ protected override void ProcessRecord() } /// - /// /// protected override void StopProcessing() { @@ -361,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. /// @@ -399,11 +410,13 @@ public SwitchParameter NoTypeInformation { return _notypeinformation; } + set { _notypeinformation = value; } } + private bool _notypeinformation; /// @@ -416,11 +429,10 @@ public SwitchParameter NoTypeInformation #endregion Command Line Parameters - #region Overrides /// - /// BeginProcessing override + /// BeginProcessing override. /// protected override void BeginProcessing() { @@ -430,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() { @@ -445,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; @@ -487,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() { @@ -521,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; @@ -572,16 +579,16 @@ private void CreateMemoryStream() if (Depth == 0) { - _serializer = new CustomSerialization(_xw, _notypeinformation); + _serializer = new CustomSerialization(_xw, NoTypeInformation); } else { - _serializer = new CustomSerialization(_xw, _notypeinformation, Depth); + _serializer = new CustomSerialization(_xw, NoTypeInformation, Depth); } } /// - ///Cleaning up the MemoryStream + ///Cleaning up the MemoryStream. /// private void CleanUp() { @@ -603,56 +610,130 @@ 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) { - if (fileName == null) - { - throw PSTraceSource.NewArgumentNullException("fileName"); - } - if (cmdlet == null) - { - throw PSTraceSource.NewArgumentNullException("cmdlet"); - } + Dbg.Assert(fileName != null, "filename is mandatory"); + Dbg.Assert(cmdlet != null, "cmdlet is mandatory"); _path = fileName; _cmdlet = cmdlet; _isLiteralPath = isLiteralPath; @@ -663,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; @@ -710,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); } @@ -742,24 +824,16 @@ internal void Import() _cmdlet.WriteObject(totalCount); } - ulong skip = _cmdlet.PagingParameters.Skip; ulong first = _cmdlet.PagingParameters.First; // if paging is not specified then keep the old V2 behavior if (skip == 0 && first == ulong.MaxValue) { - ulong item = 0; while (!_deserializer.Done()) { object result = _deserializer.Deserialize(); - if (item++ < skip) - continue; - if (first == 0) - break; - _cmdlet.WriteObject(result); - first--; } } // else try to flatten the output if possible @@ -770,99 +844,92 @@ internal void Import() while (!_deserializer.Done() && count < first) { object result = _deserializer.Deserialize(); - PSObject psObject = result as PSObject; - - if (psObject == null && skipped++ >= skip) - { - count++; - _cmdlet.WriteObject(result); - continue; - } - ICollection c = psObject.BaseObject as ICollection; - if (c != null) + if (result is PSObject psObject) { - foreach (object o in c) + if (psObject.BaseObject is ICollection c) + { + foreach (object o in c) + { + if (count >= first) + break; + + if (skipped++ >= skip) + { + count++; + _cmdlet.WriteObject(o); + } + } + } + else { - if (count >= first) - break; - if (skipped++ >= skip) { count++; - _cmdlet.WriteObject(o); + _cmdlet.WriteObject(result); } } } - else + else if (skipped++ >= skip) { - if (skipped++ >= skip) - { - count++; - _cmdlet.WriteObject(result); - } + count++; + _cmdlet.WriteObject(result); + continue; } } } } - 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; } @@ -870,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] @@ -886,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) { @@ -904,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; @@ -927,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( @@ -979,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); } @@ -1007,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); } } @@ -1023,7 +1088,7 @@ private XmlNamespaceManager AddNameSpaceTable(string parametersetname, XmlDocume return xmlns; } - # endregion private + #endregion private #region override @@ -1043,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) @@ -1058,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); @@ -1095,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. @@ -1113,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; } /// @@ -1127,9 +1192,10 @@ public string Path { return _path; } + set { - if (String.IsNullOrEmpty(value)) + if (string.IsNullOrEmpty(value)) { _path = inputStream; } @@ -1139,10 +1205,11 @@ public string Path } } } + private string _path; /// - /// The pattern used to search + /// The pattern used to search. /// public string Pattern { get; set; } @@ -1157,7 +1224,7 @@ public override string ToString() } /// - /// Return String representation of the object + /// Return String representation of the object. /// /// /// @@ -1173,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) @@ -1185,6 +1252,7 @@ internal string GetNodeText() nodeText = Node.InnerXml.Trim(); } } + return nodeText; } @@ -1202,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) @@ -1214,6 +1282,7 @@ private string RelativePath(string directory) } } } + return relPath; } @@ -1235,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 55479410b8e..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/convert-HTML.cs +++ /dev/null @@ -1,623 +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); - if (resolvedNames.Count == 1) - { - Hashtable ht = CreateAuxPropertyHT(label, alignment, width); - if (ex.Script != null) - ht.Add(FormatParameterDefinitionKeys.ExpressionEntryKey, ex.Script); - else - ht.Add(FormatParameterDefinitionKeys.ExpressionEntryKey, ex.ToString()); - resolvedNameProperty.Add(ht); - } - else - { - 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 9ccc64b3fe7..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,21 +46,13 @@ 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) { -#if CORECLR - base.Dispose(disposing); -#else try { if (disposing) @@ -76,32 +64,16 @@ protected override void Dispose(bool disposing) { base.Dispose(disposing); } -#endif } -#if !CORECLR - /// - /// Closes the dialog and then calls the base class Close - /// - [SecurityPermission(SecurityAction.LinkDemand)] - public override void Close() - { - // Call the base class close - - base.Close(); - } -#endif - #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 @@ -114,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); @@ -142,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/map.json b/src/Microsoft.PowerShell.Commands.Utility/map.json deleted file mode 100644 index 02bf3c81713..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/map.json +++ /dev/null @@ -1,175 +0,0 @@ -{ - "monad/src/commands/utility/AddMember.cs": "commands/utility/AddMember.cs", - "monad/src/commands/utility/AddType.cs": "commands/utility/AddType.cs", - "monad/src/commands/utility/compare-object.cs": "commands/utility/compare-object.cs", - "monad/src/commands/utility/ConsoleColorCmdlet.cs": "commands/utility/ConsoleColorCmdlet.cs", - "monad/src/commands/utility/convert-HTML.cs": "commands/utility/convert-HTML.cs", - "monad/src/commands/utility/ConvertFrom-StringData.cs": "commands/utility/ConvertFrom-StringData.cs", - "monad/src/commands/utility/Csv.cs": "commands/utility/Csv.cs", - "monad/src/commands/utility/CSVCommands.cs": "commands/utility/CSVCommands.cs", - "monad/src/commands/utility/CustomSerialization.cs": "commands/utility/CustomSerialization.cs", - "monad/src/commands/utility/CustomSerializationStrings.cs": "commands/utility/CustomSerializationStrings.cs", - "monad/src/commands/utility/DebugRunspaceCommand.cs": "commands/utility/DebugRunspaceCommand.cs", - "monad/src/commands/utility/Disable-PSBreakpoint.cs": "commands/utility/Disable-PSBreakpoint.cs", - "monad/src/commands/utility/Enable-PSBreakpoint.cs": "commands/utility/Enable-PSBreakpoint.cs", - "monad/src/commands/utility/EnableDisableRunspaceDebugCommand.cs": "commands/utility/EnableDisableRunspaceDebugCommand.cs", - "monad/src/commands/utility/ExportAliasCommand.cs": "commands/utility/ExportAliasCommand.cs", - "monad/src/commands/utility/FormatAndOutput/common/GetFormatDataCommand.cs": "commands/utility/FormatAndOutput/common/GetFormatDataCommand.cs", - "monad/src/commands/utility/FormatAndOutput/common/WriteFormatDataCommand.cs": "commands/utility/FormatAndOutput/common/WriteFormatDataCommand.cs", - "monad/src/commands/utility/FormatAndOutput/format-list/Format-List.cs": "commands/utility/FormatAndOutput/format-list/Format-List.cs", - "monad/src/commands/utility/FormatAndOutput/format-object/format-object.cs": "commands/utility/FormatAndOutput/format-object/format-object.cs", - "monad/src/commands/utility/FormatAndOutput/format-table/Format-Table.cs": "commands/utility/FormatAndOutput/format-table/Format-Table.cs", - "monad/src/commands/utility/FormatAndOutput/format-wide/Format-Wide.cs": "commands/utility/FormatAndOutput/format-wide/Format-Wide.cs", - "monad/src/commands/utility/FormatAndOutput/out-file/Out-File.cs": "commands/utility/FormatAndOutput/out-file/Out-File.cs", - "monad/src/commands/utility/FormatAndOutput/out-printer/out-printer.cs": "commands/utility/FormatAndOutput/out-printer/out-printer.cs", - "monad/src/commands/utility/FormatAndOutput/out-printer/PrinterLineOutput.cs": "commands/utility/FormatAndOutput/out-printer/PrinterLineOutput.cs", - "monad/src/commands/utility/FormatAndOutput/out-string/out-string.cs": "commands/utility/FormatAndOutput/out-string/out-string.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/ColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/ColumnInfo.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/ExpressionColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/ExpressionColumnInfo.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/HeaderInfo.cs": "commands/utility/FormatAndOutput/OutGridView/HeaderInfo.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/OriginalColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/OriginalColumnInfo.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs": "commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/OutWindowProxy.cs": "commands/utility/FormatAndOutput/OutGridView/OutWindowProxy.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/ScalarTypeColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/ScalarTypeColumnInfo.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/TableView.cs": "commands/utility/FormatAndOutput/OutGridView/TableView.cs", - "monad/src/commands/utility/Get-PSBreakpoint.cs": "commands/utility/Get-PSBreakpoint.cs", - "monad/src/commands/utility/Get-PSCallStack.cs": "commands/utility/Get-PSCallStack.cs", - "monad/src/commands/utility/GetAliasCommand.cs": "commands/utility/GetAliasCommand.cs", - "monad/src/commands/utility/GetCultureCommand.cs": "commands/utility/GetCultureCommand.cs", - "monad/src/commands/utility/GetDateCommand.cs": "commands/utility/GetDateCommand.cs", - "monad/src/commands/utility/GetEventCommand.cs": "commands/utility/GetEventCommand.cs", - "monad/src/commands/utility/GetEventSubscriberCommand.cs": "commands/utility/GetEventSubscriberCommand.cs", - "monad/src/commands/utility/GetHostCmdlet.cs": "commands/utility/GetHostCmdlet.cs", - "monad/src/commands/utility/GetMember.cs": "commands/utility/GetMember.cs", - "monad/src/commands/utility/GetRandomCommand.cs": "commands/utility/GetRandomCommand.cs", - "monad/src/commands/utility/GetRunspaceCommand.cs": "commands/utility/GetRunspaceCommand.cs", - "monad/src/commands/utility/GetUICultureCommand.cs": "commands/utility/GetUICultureCommand.cs", - "monad/src/commands/utility/GetUnique.cs": "commands/utility/GetUnique.cs", - "monad/src/commands/utility/group-object.cs": "commands/utility/group-object.cs", - "monad/src/commands/utility/ImplicitRemotingCommands.cs": "commands/utility/ImplicitRemotingCommands.cs", - "monad/src/commands/utility/Import-LocalizedData.cs": "commands/utility/Import-LocalizedData.cs", - "monad/src/commands/utility/ImportAliasCommand.cs": "commands/utility/ImportAliasCommand.cs", - "monad/src/commands/utility/InvokeCommandCmdlet.cs": "commands/utility/InvokeCommandCmdlet.cs", - "monad/src/commands/utility/MatchString.cs": "commands/utility/MatchString.cs", - "monad/src/commands/utility/Measure-Object.cs": "commands/utility/Measure-Object.cs", - "monad/src/commands/utility/new-object.cs": "commands/utility/new-object.cs", - "monad/src/commands/utility/NewAliasCommand.cs": "commands/utility/NewAliasCommand.cs", - "monad/src/commands/utility/neweventcommand.cs": "commands/utility/neweventcommand.cs", - "monad/src/commands/utility/NewTimeSpanCommand.cs": "commands/utility/NewTimeSpanCommand.cs", - "monad/src/commands/utility/ObjectCommandComparer.cs": "commands/utility/ObjectCommandComparer.cs", - "monad/src/commands/utility/OrderObjectBase.cs": "commands/utility/OrderObjectBase.cs", - "monad/src/commands/utility/ReadConsoleCmdlet.cs": "commands/utility/ReadConsoleCmdlet.cs", - "monad/src/commands/utility/RegisterObjectEventCommand.cs": "commands/utility/RegisterObjectEventCommand.cs", - "monad/src/commands/utility/RegisterPSEventCommand.cs": "commands/utility/RegisterPSEventCommand.cs", - "monad/src/commands/utility/Remove-PSBreakpoint.cs": "commands/utility/Remove-PSBreakpoint.cs", - "monad/src/commands/utility/RemoveEventCommand.cs": "commands/utility/RemoveEventCommand.cs", - "monad/src/commands/utility/resources/AddMember.resx": "resources/AddMember.resx", - "monad/src/commands/utility/resources/AddTypeStrings.resx": "resources/AddTypeStrings.resx", - "monad/src/commands/utility/resources/AliasCommandStrings.resx": "resources/AliasCommandStrings.resx", - "monad/src/commands/utility/resources/ConvertFromStringData.resx": "resources/ConvertFromStringData.resx", - "monad/src/commands/utility/resources/ConvertFromStringResources.resx": "resources/ConvertFromStringResources.resx", - "monad/src/commands/utility/resources/ConvertStringResources.resx": "resources/ConvertStringResources.resx", - "monad/src/commands/utility/resources/CsvCommandStrings.resx": "resources/CsvCommandStrings.resx", - "monad/src/commands/utility/resources/Debugger.resx": "resources/Debugger.resx", - "monad/src/commands/utility/resources/EventingStrings.resx": "resources/EventingStrings.resx", - "monad/src/commands/utility/resources/FlashExtractStrings.resx": "resources/FlashExtractStrings.resx", - "monad/src/commands/utility/resources/FormatAndOut_out_gridview.resx": "resources/FormatAndOut_out_gridview.resx", - "monad/src/commands/utility/resources/GetMember.resx": "resources/GetMember.resx", - "monad/src/commands/utility/resources/GetRandomCommandStrings.resx": "resources/GetRandomCommandStrings.resx", - "monad/src/commands/utility/resources/HostStrings.resx": "resources/HostStrings.resx", - "monad/src/commands/utility/resources/HttpCommandStrings.resx": "resources/HttpCommandStrings.resx", - "monad/src/commands/utility/resources/ImmutableStrings.resx": "resources/ImmutableStrings.resx", - "monad/src/commands/utility/resources/ImplicitRemotingStrings.resx": "resources/ImplicitRemotingStrings.resx", - "monad/src/commands/utility/resources/ImportLocalizedDataStrings.resx": "resources/ImportLocalizedDataStrings.resx", - "monad/src/commands/utility/resources/MatchStringStrings.resx": "resources/MatchStringStrings.resx", - "monad/src/commands/utility/resources/MeasureObjectStrings.resx": "resources/MeasureObjectStrings.resx", - "monad/src/commands/utility/resources/NewObjectStrings.resx": "resources/NewObjectStrings.resx", - "monad/src/commands/utility/resources/OutPrinterDisplayStrings.resx": "resources/OutPrinterDisplayStrings.resx", - "monad/src/commands/utility/resources/SelectObjectStrings.resx": "resources/SelectObjectStrings.resx", - "monad/src/commands/utility/resources/SendMailMessageStrings.resx": "resources/SendMailMessageStrings.resx", - "monad/src/commands/utility/resources/SortObjectStrings.resx": "resources/SortObjectStrings.resx", - "monad/src/commands/utility/resources/TraceCommandStrings.resx": "resources/TraceCommandStrings.resx", - "monad/src/commands/utility/resources/UpdateDataStrings.resx": "resources/UpdateDataStrings.resx", - "monad/src/commands/utility/resources/UpdateListStrings.resx": "resources/UpdateListStrings.resx", - "monad/src/commands/utility/resources/UtilityCommonStrings.resx": "resources/UtilityCommonStrings.resx", - "monad/src/commands/utility/resources/UtilityMshSnapinResources.resx": "resources/UtilityMshSnapinResources.resx", - "monad/src/commands/utility/resources/VariableCommandStrings.resx": "resources/VariableCommandStrings.resx", - "monad/src/commands/utility/resources/WebCmdletStrings.resx": "resources/WebCmdletStrings.resx", - "monad/src/commands/utility/resources/WriteErrorStrings.resx": "resources/WriteErrorStrings.resx", - "monad/src/commands/utility/resources/WriteProgressResourceStrings.resx": "resources/WriteProgressResourceStrings.resx", - "monad/src/commands/utility/select-object.cs": "commands/utility/select-object.cs", - "monad/src/commands/utility/Send-MailMessage.cs": "commands/utility/Send-MailMessage.cs", - "monad/src/commands/utility/Set-PSBreakpoint.cs": "commands/utility/Set-PSBreakpoint.cs", - "monad/src/commands/utility/SetAliasCommand.cs": "commands/utility/SetAliasCommand.cs", - "monad/src/commands/utility/SetDateCommand.cs": "commands/utility/SetDateCommand.cs", - "monad/src/commands/utility/ShowCommand/ShowCommand.cs": "commands/utility/ShowCommand/ShowCommand.cs", - "monad/src/commands/utility/ShowCommand/ShowCommandCommandInfo.cs": "commands/utility/ShowCommand/ShowCommandCommandInfo.cs", - "monad/src/commands/utility/ShowCommand/ShowCommandModuleInfo.cs": "commands/utility/ShowCommand/ShowCommandModuleInfo.cs", - "monad/src/commands/utility/ShowCommand/ShowCommandParameterInfo.cs": "commands/utility/ShowCommand/ShowCommandParameterInfo.cs", - "monad/src/commands/utility/ShowCommand/ShowCommandParameterSetInfo.cs": "commands/utility/ShowCommand/ShowCommandParameterSetInfo.cs", - "monad/src/commands/utility/ShowCommand/ShowCommandParameterType.cs": "commands/utility/ShowCommand/ShowCommandParameterType.cs", - "monad/src/commands/utility/ShowCommand/ShowCommandProxy.cs": "commands/utility/ShowCommand/ShowCommandProxy.cs", - "monad/src/commands/utility/sort-object.cs": "commands/utility/sort-object.cs", - "monad/src/commands/utility/StartSleepCommand.cs": "commands/utility/StartSleepCommand.cs", - "monad/src/commands/utility/tee-object.cs": "commands/utility/tee-object.cs", - "monad/src/commands/utility/TimeExpressionCommand.cs": "commands/utility/TimeExpressionCommand.cs", - "monad/src/commands/utility/trace/GetTracerCommand.cs": "commands/utility/trace/GetTracerCommand.cs", - "monad/src/commands/utility/trace/MshHostTraceListener.cs": "commands/utility/trace/MshHostTraceListener.cs", - "monad/src/commands/utility/trace/SetTracerCommand.cs": "commands/utility/trace/SetTracerCommand.cs", - "monad/src/commands/utility/trace/TraceCommandBase.cs": "commands/utility/trace/TraceCommandBase.cs", - "monad/src/commands/utility/trace/TraceExpressionCommand.cs": "commands/utility/trace/TraceExpressionCommand.cs", - "monad/src/commands/utility/trace/TraceListenerCommandBase.cs": "commands/utility/trace/TraceListenerCommandBase.cs", - "monad/src/commands/utility/UnblockFile.cs": "commands/utility/UnblockFile.cs", - "monad/src/commands/utility/UnregisterEventCommand.cs": "commands/utility/UnregisterEventCommand.cs", - "monad/src/commands/utility/Update-Data.cs": "commands/utility/Update-Data.cs", - "monad/src/commands/utility/update-list.cs": "commands/utility/update-list.cs", - "monad/src/commands/utility/Update-TypeData.cs": "commands/utility/Update-TypeData.cs", - "monad/src/commands/utility/UtilityCommon.cs": "commands/utility/UtilityCommon.cs", - "monad/src/commands/utility/Var.cs": "commands/utility/Var.cs", - "monad/src/commands/utility/WaitEventCommand.cs": "commands/utility/WaitEventCommand.cs", - "monad/src/commands/utility/Write-Object.cs": "commands/utility/Write-Object.cs", - "monad/src/commands/utility/write.cs": "commands/utility/write.cs", - "monad/src/commands/utility/WriteAliasCommandBase.cs": "commands/utility/WriteAliasCommandBase.cs", - "monad/src/commands/utility/WriteConsoleCmdlet.cs": "commands/utility/WriteConsoleCmdlet.cs", - "monad/src/commands/utility/WriteProgressCmdlet.cs": "commands/utility/WriteProgressCmdlet.cs", - "monad/src/commands/utility/XmlCommands.cs": "commands/utility/XmlCommands.cs", - "monad/src/singleshell/installer/MshUtilityMshSnapin.cs": "singleshell/installer/MshUtilityMshSnapin.cs", - - "monad/src/commands/utility/WebCmdlet/ConvertFromJsonCommand.cs": "commands/utility/WebCmdlet/ConvertFromJsonCommand.cs", - "monad/src/commands/utility/WebCmdlet/ConvertToJsonCommand.cs": "commands/utility/WebCmdlet/ConvertToJsonCommand.cs", - "monad/src/commands/utility/WebCmdlet/FormObject.cs": "commands/utility/WebCmdlet/FormObject.cs", - "monad/src/commands/utility/WebCmdlet/FormObjectCollection.cs": "commands/utility/WebCmdlet/FormObjectCollection.cs", - "monad/src/commands/utility/WebCmdlet/JsonObject.cs": "commands/utility/WebCmdlet/JsonObject.cs", - "monad/src/commands/utility/WebCmdlet/PSUserAgent.cs": "commands/utility/WebCmdlet/PSUserAgent.cs", - "monad/src/commands/utility/WebCmdlet/StreamHelper.cs": "commands/utility/WebCmdlet/StreamHelper.cs", - "monad/src/commands/utility/WebCmdlet/WebCmdletElementCollection.cs": "commands/utility/WebCmdlet/WebCmdletElementCollection.cs", - "monad/src/commands/utility/WebCmdlet/WebRequestMethod.cs": "commands/utility/WebCmdlet/WebRequestMethod.cs", - "monad/src/commands/utility/WebCmdlet/WebRequestSession.cs": "commands/utility/WebCmdlet/WebRequestSession.cs", - "monad/src/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs": "commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs", - "monad/src/commands/utility/WebCmdlet/Common/ContentHelper.Common.cs": "commands/utility/WebCmdlet/Common/ContentHelper.Common.cs", - "monad/src/commands/utility/WebCmdlet/Common/HtmlWebResponseObject.Common.cs": "commands/utility/WebCmdlet/Common/HtmlWebResponseObject.Common.cs", - "monad/src/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs": "commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs", - "monad/src/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs": "commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs", - "monad/src/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs": "commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs", - "monad/src/commands/utility/WebCmdlet/CoreCLR/BasicHtmlWebResponseObject.CoreClr.cs": "commands/utility/WebCmdlet/CoreCLR/BasicHtmlWebResponseObject.CoreClr.cs", - "monad/src/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs": "commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs", - "monad/src/commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs": "commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs", - "monad/src/commands/utility/WebCmdlet/CoreCLR/HttpKnownHeaderNames.cs": "commands/utility/WebCmdlet/CoreCLR/HttpKnownHeaderNames.cs", - "monad/src/commands/utility/WebCmdlet/CoreCLR/InvokeRestMethodCommand.CoreClr.cs": "commands/utility/WebCmdlet/CoreCLR/InvokeRestMethodCommand.CoreClr.cs", - "monad/src/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs": "commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs", - "monad/src/commands/utility/WebCmdlet/CoreCLR/WebProxy.cs": "commands/utility/WebCmdlet/CoreCLR/WebProxy.cs", - "monad/src/commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs": "commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs", - "monad/src/commands/utility/WebCmdlet/CoreCLR/WebResponseHelper.CoreClr.cs": "commands/utility/WebCmdlet/CoreCLR/WebResponseHelper.CoreClr.cs", - "monad/src/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs": "commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs", - "monad/src/commands/utility/WebCmdlet/CoreCLR/WebResponseObjectFactory.CoreClr.cs": "commands/utility/WebCmdlet/CoreCLR/WebResponseObjectFactory.CoreClr.cs", - "monad/src/commands/utility/WebCmdlet/FullClr/BasicHtmlWebResponseObject.FullClr.cs": "commands/utility/WebCmdlet/FullClr/BasicHtmlWebResponseObject.FullClr.cs", - "monad/src/commands/utility/WebCmdlet/FullClr/ContentHelper.FullClr.cs": "commands/utility/WebCmdlet/FullClr/ContentHelper.FullClr.cs", - "monad/src/commands/utility/WebCmdlet/FullClr/HtmlWebResponseObject.FullClr.cs": "commands/utility/WebCmdlet/FullClr/HtmlWebResponseObject.FullClr.cs", - "monad/src/commands/utility/WebCmdlet/FullClr/InvokeRestMethodCommand.FullClr.cs": "commands/utility/WebCmdlet/FullClr/InvokeRestMethodCommand.FullClr.cs", - "monad/src/commands/utility/WebCmdlet/FullClr/InvokeWebRequestCommand.FullClr.cs": "commands/utility/WebCmdlet/FullClr/InvokeWebRequestCommand.FullClr.cs", - "monad/src/commands/utility/WebCmdlet/FullClr/JsonObjectTypeResolver.cs": "commands/utility/WebCmdlet/FullClr/JsonObjectTypeResolver.cs", - "monad/src/commands/utility/WebCmdlet/FullClr/WebRequestPSCmdlet.FullClr.cs": "commands/utility/WebCmdlet/FullClr/WebRequestPSCmdlet.FullClr.cs", - "monad/src/commands/utility/WebCmdlet/FullClr/WebResponseHelper.FullClr.cs": "commands/utility/WebCmdlet/FullClr/WebResponseHelper.FullClr.cs", - "monad/src/commands/utility/WebCmdlet/FullClr/WebResponseObject.FullClr.cs": "commands/utility/WebCmdlet/FullClr/WebResponseObject.FullClr.cs", - "monad/src/commands/utility/WebCmdlet/FullClr/WebResponseObjectFactory.FullClr.cs": "commands/utility/WebCmdlet/FullClr/WebResponseObjectFactory.FullClr.cs" -} 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.CoreCLR.Eventing/map.json b/src/Microsoft.PowerShell.CoreCLR.Eventing/map.json deleted file mode 100644 index aed28f932f2..00000000000 --- a/src/Microsoft.PowerShell.CoreCLR.Eventing/map.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "monad/src/DotNetCode/Eventing/Reader/ProviderMetadataCachedInformation.cs": "DotNetCode/Eventing/Reader/ProviderMetadataCachedInformation.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLogStatus.cs": "DotNetCode/Eventing/Reader/EventLogStatus.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLogPropertySelector.cs": "DotNetCode/Eventing/Reader/EventLogPropertySelector.cs", - "monad/src/DotNetCode/Eventing/Reader/EventOpcode.cs": "DotNetCode/Eventing/Reader/EventOpcode.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLogHandle.cs": "DotNetCode/Eventing/Reader/EventLogHandle.cs", - "monad/src/DotNetCode/Eventing/Reader/EventKeyword.cs": "DotNetCode/Eventing/Reader/EventKeyword.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLogQuery.cs": "DotNetCode/Eventing/Reader/EventLogQuery.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLogLink.cs": "DotNetCode/Eventing/Reader/EventLogLink.cs", - "monad/src/DotNetCode/Eventing/Reader/EventMetadata.cs": "DotNetCode/Eventing/Reader/EventMetadata.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLogReader.cs": "DotNetCode/Eventing/Reader/EventLogReader.cs", - "monad/src/DotNetCode/Eventing/Reader/EventProperty.cs": "DotNetCode/Eventing/Reader/EventProperty.cs", - "monad/src/DotNetCode/Eventing/Reader/EventBookmark.cs": "DotNetCode/Eventing/Reader/EventBookmark.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLogInformation.cs": "DotNetCode/Eventing/Reader/EventLogInformation.cs", - "monad/src/DotNetCode/Eventing/EventProvider.cs": "DotNetCode/Eventing/EventProvider.cs", - "monad/src/DotNetCode/Eventing/Reader/NativeWrapper.cs": "DotNetCode/Eventing/Reader/NativeWrapper.cs", - "monad/src/DotNetCode/Eventing/Reader/Winmeta.cs": "DotNetCode/Eventing/Reader/Winmeta.cs", - "monad/src/DotNetCode/Eventing/Reader/EventTask.cs": "DotNetCode/Eventing/Reader/EventTask.cs", - "monad/src/DotNetCode/Eventing/Reader/EventRecord.cs": "DotNetCode/Eventing/Reader/EventRecord.cs", - "monad/src/DotNetCode/Eventing/Reader/CoTaskMemSafeHandle.cs": "DotNetCode/Eventing/Reader/CoTaskMemSafeHandle.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLogRecord.cs": "DotNetCode/Eventing/Reader/EventLogRecord.cs", - "monad/src/DotNetCode/Eventing/EventProviderTraceListener.cs": "DotNetCode/Eventing/EventProviderTraceListener.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLogException.cs": "DotNetCode/Eventing/Reader/EventLogException.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLogSession.cs": "DotNetCode/Eventing/Reader/EventLogSession.cs", - "monad/src/DotNetCode/Eventing/EventDescriptor.cs": "DotNetCode/Eventing/EventDescriptor.cs", - "monad/src/DotNetCode/Eventing/UnsafeNativeMethods.cs": "DotNetCode/Eventing/UnsafeNativeMethods.cs", - "monad/src/DotNetCode/Eventing/resources/DotNetEventingStrings.resx": "resources/DotNetEventingStrings.resx", - "monad/src/DotNetCode/Eventing/Reader/EventPropertyContext.cs": "DotNetCode/Eventing/Reader/EventPropertyContext.cs", - "monad/src/DotNetCode/Eventing/Reader/CoTaskMemUnicodeSafeHandle.cs": "DotNetCode/Eventing/Reader/CoTaskMemUnicodeSafeHandle.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLevel.cs": "DotNetCode/Eventing/Reader/EventLevel.cs", - "monad/src/DotNetCode/Eventing/Reader/EventLogConfiguration.cs": "DotNetCode/Eventing/Reader/EventLogConfiguration.cs", - "monad/src/DotNetCode/Eventing/Reader/ProviderMetadata.cs": "DotNetCode/Eventing/Reader/ProviderMetadata.cs" -} diff --git a/src/Microsoft.PowerShell.Diagnostics.Activities/AssemblyInfo.cs b/src/Microsoft.PowerShell.Diagnostics.Activities/AssemblyInfo.cs deleted file mode 100644 index 883e8e324a8..00000000000 --- a/src/Microsoft.PowerShell.Diagnostics.Activities/AssemblyInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Reflection; -using System.Security.Permissions; -using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Diagnostics.CodeAnalysis; - - -[assembly: AssemblyVersion("3.0.0.0")] -[assembly: AssemblyFileVersionAttribute("3.0.0.0")] -[assembly: System.Runtime.InteropServices.ComVisible(false)] - -[assembly: AssemblyConfiguration("")] -[assembly: ReliabilityContractAttribute(Consistency.MayCorruptAppDomain, Cer.MayFail)] -[assembly: AssemblyTitle("Microsoft.PowerShell.Diagnostics.Activities")] -[assembly: AssemblyDescription("Microsoft.PowerShell.Diagnostics.Activities")] - -[module: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant")] - diff --git a/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/ExportCounterActivity.cs b/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/ExportCounterActivity.cs deleted file mode 100644 index 8b2c8edc6e7..00000000000 --- a/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/ExportCounterActivity.cs +++ /dev/null @@ -1,130 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Diagnostics.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Diagnostics\Export-Counter command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class ExportCounter : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public ExportCounter() - { - this.DisplayName = "Export-Counter"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Diagnostics\\Export-Counter"; } } - - // Arguments - - /// - /// Provides access to the Path parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Path { get; set; } - - /// - /// Provides access to the FileFormat parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument FileFormat { get; set; } - - /// - /// Provides access to the MaxSize parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument MaxSize { get; set; } - - /// - /// Provides access to the InputObject parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument InputObject { get; set; } - - /// - /// Provides access to the Force parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Force { get; set; } - - /// - /// Provides access to the Circular parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Circular { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(Path.Expression != null) - { - targetCommand.AddParameter("Path", Path.Get(context)); - } - - if(FileFormat.Expression != null) - { - targetCommand.AddParameter("FileFormat", FileFormat.Get(context)); - } - - if(MaxSize.Expression != null) - { - targetCommand.AddParameter("MaxSize", MaxSize.Get(context)); - } - - if(InputObject.Expression != null) - { - targetCommand.AddParameter("InputObject", InputObject.Get(context)); - } - - if(Force.Expression != null) - { - targetCommand.AddParameter("Force", Force.Get(context)); - } - - if(Circular.Expression != null) - { - targetCommand.AddParameter("Circular", Circular.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/GetCounterActivity.cs b/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/GetCounterActivity.cs deleted file mode 100644 index b1edbaa9810..00000000000 --- a/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/GetCounterActivity.cs +++ /dev/null @@ -1,128 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Diagnostics.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Diagnostics\Get-Counter command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetCounter : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetCounter() - { - this.DisplayName = "Get-Counter"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Diagnostics\\Get-Counter"; } } - - // Arguments - - /// - /// Provides access to the ListSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ListSet { get; set; } - - /// - /// Provides access to the Counter parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Counter { get; set; } - - /// - /// Provides access to the SampleInterval parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SampleInterval { get; set; } - - /// - /// Provides access to the MaxSamples parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument MaxSamples { get; set; } - - /// - /// Provides access to the Continuous parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Continuous { get; set; } - - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ListSet.Expression != null) - { - targetCommand.AddParameter("ListSet", ListSet.Get(context)); - } - - if(Counter.Expression != null) - { - targetCommand.AddParameter("Counter", Counter.Get(context)); - } - - if(SampleInterval.Expression != null) - { - targetCommand.AddParameter("SampleInterval", SampleInterval.Get(context)); - } - - if(MaxSamples.Expression != null) - { - targetCommand.AddParameter("MaxSamples", MaxSamples.Get(context)); - } - - if(Continuous.Expression != null) - { - targetCommand.AddParameter("Continuous", Continuous.Get(context)); - } - - if(GetIsComputerNameSpecified(context) && (PSRemotingBehavior.Get(context) == RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", PSComputerName.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/GetWinEventActivity.cs b/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/GetWinEventActivity.cs deleted file mode 100644 index e4093744893..00000000000 --- a/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/GetWinEventActivity.cs +++ /dev/null @@ -1,212 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Diagnostics.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Diagnostics\Get-WinEvent command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetWinEvent : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetWinEvent() - { - this.DisplayName = "Get-WinEvent"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Diagnostics\\Get-WinEvent"; } } - - // Arguments - - /// - /// Provides access to the ListLog parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ListLog { get; set; } - - /// - /// Provides access to the LogName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument LogName { get; set; } - - /// - /// Provides access to the ListProvider parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ListProvider { get; set; } - - /// - /// Provides access to the ProviderName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ProviderName { get; set; } - - /// - /// Provides access to the Path parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Path { get; set; } - - /// - /// Provides access to the MaxEvents parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument MaxEvents { get; set; } - - /// - /// Provides access to the Credential parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Credential { get; set; } - - /// - /// Provides access to the FilterXPath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument FilterXPath { get; set; } - - /// - /// Provides access to the FilterXml parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument FilterXml { get; set; } - - /// - /// Provides access to the FilterHashtable parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument FilterHashtable { get; set; } - - /// - /// Provides access to the Force parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Force { get; set; } - - /// - /// Provides access to the Oldest parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Oldest { get; set; } - - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ListLog.Expression != null) - { - targetCommand.AddParameter("ListLog", ListLog.Get(context)); - } - - if(LogName.Expression != null) - { - targetCommand.AddParameter("LogName", LogName.Get(context)); - } - - if(ListProvider.Expression != null) - { - targetCommand.AddParameter("ListProvider", ListProvider.Get(context)); - } - - if(ProviderName.Expression != null) - { - targetCommand.AddParameter("ProviderName", ProviderName.Get(context)); - } - - if(Path.Expression != null) - { - targetCommand.AddParameter("Path", Path.Get(context)); - } - - if(MaxEvents.Expression != null) - { - targetCommand.AddParameter("MaxEvents", MaxEvents.Get(context)); - } - - if(Credential.Expression != null) - { - targetCommand.AddParameter("Credential", Credential.Get(context)); - } - - if(FilterXPath.Expression != null) - { - targetCommand.AddParameter("FilterXPath", FilterXPath.Get(context)); - } - - if(FilterXml.Expression != null) - { - targetCommand.AddParameter("FilterXml", FilterXml.Get(context)); - } - - if(FilterHashtable.Expression != null) - { - targetCommand.AddParameter("FilterHashtable", FilterHashtable.Get(context)); - } - - if(Force.Expression != null) - { - targetCommand.AddParameter("Force", Force.Get(context)); - } - - if(Oldest.Expression != null) - { - targetCommand.AddParameter("Oldest", Oldest.Get(context)); - } - - if(GetIsComputerNameSpecified(context) && (PSRemotingBehavior.Get(context) == RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", PSComputerName.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/ImportCounterActivity.cs b/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/ImportCounterActivity.cs deleted file mode 100644 index 6bc7e91451f..00000000000 --- a/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/ImportCounterActivity.cs +++ /dev/null @@ -1,142 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Diagnostics.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Diagnostics\Import-Counter command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class ImportCounter : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public ImportCounter() - { - this.DisplayName = "Import-Counter"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Diagnostics\\Import-Counter"; } } - - // Arguments - - /// - /// Provides access to the Path parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Path { get; set; } - - /// - /// Provides access to the ListSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ListSet { get; set; } - - /// - /// Provides access to the StartTime parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument StartTime { get; set; } - - /// - /// Provides access to the EndTime parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument EndTime { get; set; } - - /// - /// Provides access to the Counter parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Counter { get; set; } - - /// - /// Provides access to the Summary parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Summary { get; set; } - - /// - /// Provides access to the MaxSamples parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument MaxSamples { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(Path.Expression != null) - { - targetCommand.AddParameter("Path", Path.Get(context)); - } - - if(ListSet.Expression != null) - { - targetCommand.AddParameter("ListSet", ListSet.Get(context)); - } - - if(StartTime.Expression != null) - { - targetCommand.AddParameter("StartTime", StartTime.Get(context)); - } - - if(EndTime.Expression != null) - { - targetCommand.AddParameter("EndTime", EndTime.Get(context)); - } - - if(Counter.Expression != null) - { - targetCommand.AddParameter("Counter", Counter.Get(context)); - } - - if(Summary.Expression != null) - { - targetCommand.AddParameter("Summary", Summary.Get(context)); - } - - if(MaxSamples.Expression != null) - { - targetCommand.AddParameter("MaxSamples", MaxSamples.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/NewWinEventActivity.cs b/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/NewWinEventActivity.cs deleted file mode 100644 index 1822c050fda..00000000000 --- a/src/Microsoft.PowerShell.Diagnostics.Activities/Generated/NewWinEventActivity.cs +++ /dev/null @@ -1,106 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Diagnostics.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Diagnostics\New-WinEvent command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class NewWinEvent : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public NewWinEvent() - { - this.DisplayName = "New-WinEvent"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Diagnostics\\New-WinEvent"; } } - - // Arguments - - /// - /// Provides access to the ProviderName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ProviderName { get; set; } - - /// - /// Provides access to the WinEventId parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument WinEventId { get; set; } - - /// - /// Provides access to the Version parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Version { get; set; } - - /// - /// Provides access to the Payload parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Payload { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ProviderName.Expression != null) - { - targetCommand.AddParameter("ProviderName", ProviderName.Get(context)); - } - - if(WinEventId.Expression != null) - { - targetCommand.AddParameter("Id", WinEventId.Get(context)); - } - - if(Version.Expression != null) - { - targetCommand.AddParameter("Version", Version.Get(context)); - } - - if(Payload.Expression != null) - { - targetCommand.AddParameter("Payload", Payload.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Diagnostics.Activities/map.json b/src/Microsoft.PowerShell.Diagnostics.Activities/map.json deleted file mode 100644 index cba12a7c6c9..00000000000 --- a/src/Microsoft.PowerShell.Diagnostics.Activities/map.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "monad/src/Activities/Generated/Microsoft_PowerShell_Diagnostics_Activities/ExportCounterActivity.cs": "Generated/ExportCounterActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Diagnostics_Activities/GetCounterActivity.cs": "Generated/GetCounterActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Diagnostics_Activities/GetWinEventActivity.cs": "Generated/GetWinEventActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Diagnostics_Activities/NewWinEventActivity.cs": "Generated/NewWinEventActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Diagnostics_Activities/ImportCounterActivity.cs": "Generated/ImportCounterActivity.cs" -} 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.GraphicalHost/AssemblyInfo.cs b/src/Microsoft.PowerShell.GraphicalHost/AssemblyInfo.cs deleted file mode 100644 index 0a5564682a2..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/AssemblyInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Reflection; -using System.Resources; -using System.Runtime.CompilerServices; - -[assembly: AssemblyFileVersionAttribute("3.0.0.0")] -[assembly: AssemblyVersion("3.0.0.0")] - -[assembly: AssemblyCulture("")] -[assembly: NeutralResourcesLanguage("en-US")] - -[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.GPowerShell"+@",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] \ No newline at end of file diff --git a/src/Microsoft.PowerShell.GraphicalHost/CommonHelper.cs b/src/Microsoft.PowerShell.GraphicalHost/CommonHelper.cs deleted file mode 100644 index 37066d70bd5..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/CommonHelper.cs +++ /dev/null @@ -1,74 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements HelpWindow. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI -{ - using System.Windows; - - /// - /// 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.PowerShell.GraphicalHost/HelpWindow/HelpWindow.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/HelpWindow/HelpWindow.xaml.cs deleted file mode 100644 index a8239c49402..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/HelpWindow/HelpWindow.xaml.cs +++ /dev/null @@ -1,309 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements HelpWindow. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI -{ - using System.Globalization; - using System.Management.Automation; - using System.Windows; - using System.Windows.Documents; - using System.Windows.Input; - using Microsoft.Management.UI.Internal; - - /// - /// A window displaying help content and allowing search - /// - public partial class HelpWindow : Window - { - /// - /// Minimum zoom in the slider - /// - public const double MinimumZoom = 20; - - /// - /// Maximum zoom in the slider - /// - public const double MaximumZoom = 300; - - /// - /// Zoom interval - /// - public const double ZoomInterval = 10; - - /// - /// The ViewModel for the dialog - /// - private 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 += new System.ComponentModel.PropertyChangedEventHandler(this.ViewModel_PropertyChanged); - this.DataContext = this.viewModel; - - this.Loaded += new RoutedEventHandler(this.HelpDialog_Loaded); - this.Closed += new System.EventHandler(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/Down 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); - if (e.Handled) - { - return; - } - } - } - - /// - /// 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 += new System.EventHandler(this.HelpDialog_StateChanged); - this.LocationChanged += new System.EventHandler(this.HelpDialog_LocationChanged); - this.SizeChanged += new SizeChangedEventHandler(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.PowerShell.GraphicalHost/HelpWindow/ParagraphBuilder.cs b/src/Microsoft.PowerShell.GraphicalHost/HelpWindow/ParagraphBuilder.cs deleted file mode 100644 index c459b71f343..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/HelpWindow/ParagraphBuilder.cs +++ /dev/null @@ -1,383 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements ParagraphBuilder. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.Text; - using System.Windows.Documents; - using System.Windows.Media; - - /// - /// Builds a paragraph based on Text + Bold + Highlight information. - /// Bold are the segments of thexct that should be bold, and Highlight are - /// the segments of thext that should be highlighted (like search results). - /// - internal class ParagraphBuilder : INotifyPropertyChanged - { - /// - /// The text spans that should be bold - /// - private List boldSpans; - - /// - /// The text spans that should be highlighted - /// - private List highlightedSpans; - - /// - /// The text displayed - /// - private StringBuilder textBuilder; - - /// - /// Paragraph built in BuildParagraph - /// - private Paragraph paragraph; - - /// - /// Initializes a new instance of the ParagraphBuilder class - /// - /// paragraph we will be adding lines to in BuildParagraph - internal ParagraphBuilder(Paragraph paragraph) - { - if (paragraph == null) - { - throw new ArgumentNullException("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) - { - if (str == null) - { - throw new ArgumentNullException("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 - /// propper span in according to the - /// This is an auxiliar method in BuildParagraph. - /// - /// current index within - /// current span within - /// caracter 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) - { - if (start < 0) - { - throw new ArgumentOutOfRangeException("start"); - } - - if (start + length > this.textBuilder.Length) - { - throw new ArgumentOutOfRangeException("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 int start; - - /// - /// Index of the last character in the span - /// - private 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) - { - if (start < 0) - { - throw new ArgumentOutOfRangeException("start"); - } - - if (length < 1) - { - throw new ArgumentOutOfRangeException("length"); - } - - 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.PowerShell.GraphicalHost/HelpWindow/ParagraphSearcher.cs b/src/Microsoft.PowerShell.GraphicalHost/HelpWindow/ParagraphSearcher.cs deleted file mode 100644 index c1bed454a24..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/HelpWindow/ParagraphSearcher.cs +++ /dev/null @@ -1,247 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements ParagraphSearcher. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System.Diagnostics; - using System.Windows.Documents; - using System.Windows.Media; - - /// - /// 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 current 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 wrap arround - currentRun = ParagraphSearcher.GetFirstOrLastRun(caretRun, forward); - currentRun = ParagraphSearcher.GetNextMatch(currentRun, forward); - } - - this.currentHighlightedMatch = currentRun; - if (this.currentHighlightedMatch != null) - { - // restore the current 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 ParagraphBuilder 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 searching 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.PowerShell.GraphicalHost/ManagementList/Common/AutomationTextBlock.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/AutomationTextBlock.cs deleted file mode 100644 index 9f159e3486f..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/AutomationTextBlock.cs +++ /dev/null @@ -1,50 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - #region Using Directives - - using System.ComponentModel; - using System.Diagnostics.CodeAnalysis; - using System.Windows.Automation.Peers; - using System.Windows.Controls; - - #endregion - - /// - /// 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 Overrides - - /// - /// 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.PowerShell.GraphicalHost/ManagementList/Common/AutomationTextBlockAutomationPeer.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/AutomationTextBlockAutomationPeer.cs deleted file mode 100644 index ac171da6ec2..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/AutomationTextBlockAutomationPeer.cs +++ /dev/null @@ -1,62 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Provides an automation peer for AutomationTextBlock. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - #region Using Directives - - using System.Diagnostics.CodeAnalysis; - using System.Windows.Automation.Peers; - using System.Windows.Controls; - - #endregion - - /// - /// 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.PowerShell.GraphicalHost/ManagementList/Common/BooleanBoxes.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/BooleanBoxes.cs deleted file mode 100644 index 28dbe8e7d74..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/BooleanBoxes.cs +++ /dev/null @@ -1,47 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ----------------------------------------------------------------------- - -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.PowerShell.GraphicalHost/ManagementList/Common/CommandHelper.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/CommandHelper.cs deleted file mode 100644 index 04737d14128..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/CommandHelper.cs +++ /dev/null @@ -1,57 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Helper routines for executing commands. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Collections.Generic; - using System.Text; - using System.Windows; - using System.Windows.Input; - using System.Security; - - 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.PowerShell.GraphicalHost/ManagementList/Common/DataRoutedEventArgs.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/DataRoutedEventArgs.cs deleted file mode 100644 index d42301c665d..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/DataRoutedEventArgs.cs +++ /dev/null @@ -1,44 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Collections.Generic; - using System.Text; - using System.Windows; - using System.Diagnostics.CodeAnalysis; - - /// - /// 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.PowerShell.GraphicalHost/ManagementList/Common/IAsyncProgress.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/IAsyncProgress.cs deleted file mode 100644 index 00773d9ddbd..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/IAsyncProgress.cs +++ /dev/null @@ -1,45 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - #region Using Directives - - using System; - using System.Diagnostics.CodeAnalysis; - using System.Collections.Generic; - using System.Text; - using System.Windows; - - #endregion - - /// - /// 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.PowerShell.GraphicalHost/ManagementList/Common/IStateDescriptorFactory.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/IStateDescriptorFactory.cs deleted file mode 100644 index 24ed5f442db..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/IStateDescriptorFactory.cs +++ /dev/null @@ -1,27 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics.CodeAnalysis; - - /// - /// 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.PowerShell.GraphicalHost/ManagementList/Common/IsEqualConverter.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/IsEqualConverter.cs deleted file mode 100644 index 6fb0d52e1be..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/IsEqualConverter.cs +++ /dev/null @@ -1,80 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Used to determine whether two objects are equal. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Collections.Generic; - using System.Text; - using System.Windows.Data; - using System.Diagnostics.CodeAnalysis; - using System.Windows; - - /// - /// 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) - { - if (values == null) - { - throw new ArgumentNullException("values"); - } - - if (2 != values.Length) - { - throw new ArgumentException("Two values expected", "values"); - } - - object item1 = values[0]; - object item2 = values[1]; - - if (null == item1) - { - return (null == item2); - } - - if (null == item2) - { - 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.PowerShell.GraphicalHost/ManagementList/Common/MessageTextBox.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/MessageTextBox.cs deleted file mode 100644 index 6a69f003248..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/MessageTextBox.cs +++ /dev/null @@ -1,65 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// A textbox modified to show Default text when no user text -// is supplied -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics.CodeAnalysis; - using System.Windows; - using System.Windows.Controls; - - /// - /// Partial class implementation for MessageTextBox control. - /// - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public partial class MessageTextBox : TextBox - { - static partial void StaticConstructorImplementation() - { - TextProperty.OverrideMetadata( - typeof(MessageTextBox), - new FrameworkPropertyMetadata( - String.Empty, - null, - new CoerceValueCallback(OnTextBoxTextCoerce))); - } - - #region Non-Public Methods - - private void UpdateIsBackgroundTextShown(string text) - { - if (String.IsNullOrEmpty(text) == false && this.IsBackgroundTextShown) - { - this.IsBackgroundTextShown = false; - } - else if (String.IsNullOrEmpty(text) && this.IsBackgroundTextShown == false) - { - this.IsBackgroundTextShown = true; - } - } - - private static object OnTextBoxTextCoerce(DependencyObject o, object baseValue) - { - MessageTextBox mtb = (MessageTextBox)o; - - mtb.UpdateIsBackgroundTextShown((string)baseValue); - - if (baseValue == null) - { - return string.Empty; - } - - return baseValue; - } - - #endregion Non-Public Methods - } -} - diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PickerBase.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PickerBase.cs deleted file mode 100644 index e04930419af..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/PickerBase.cs +++ /dev/null @@ -1,137 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Windows; - using System.Collections.Generic; - using System.Text; - using System.Windows.Controls; - using System.Windows.Input; - using System.Windows.Data; - using System.Windows.Controls.Primitives; - using System.Diagnostics; - using System.Windows.Threading; - using System.Diagnostics.CodeAnalysis; - - /// - /// Implements a re-usable base component useful for showing - /// Picker-like controls. - /// - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public partial class PickerBase : HeaderedContentControl - { - /// - /// Creates a new instance of the PickerBase class. - /// - public PickerBase() - { - // empty - } - - partial void OnCloseDropDownExecutedImplementation(ExecutedRoutedEventArgs e) - { - this.IsOpen = false; - } - - #region DropDownButtonTemplate Changed - - partial void OnDropDownButtonTemplateChangedImplementation(PropertyChangedEventArgs e) - { - this.ApplyDropDownButtonTemplate(); - } - - private void ApplyDropDownButtonTemplate() - { - if (!this.IsLoaded) - { - this.ApplyTemplate(); - this.Loaded += new RoutedEventHandler(this.PickerBase_Loaded_ApplyDropDownButtonTemplate); - return; - } - - if (null != this.DropDownButtonTemplate && !ReferenceEquals(this.dropDownButton.Template, this.DropDownButtonTemplate)) - { - this.dropDownButton.Template = this.DropDownButtonTemplate; - } - } - - private void PickerBase_Loaded_ApplyDropDownButtonTemplate(object sender, RoutedEventArgs e) - { - this.Loaded -= this.PickerBase_Loaded_ApplyDropDownButtonTemplate; - this.ApplyDropDownButtonTemplate(); - } - - #endregion DropDownButtonTemplate Changed - - #region DropDown IsOpen Handlers - - private void DropDown_Opened(object sender, EventArgs e) - { - this.FocusDropDown(); - } - - private void FocusDropDown() - { - if (!this.dropDown.IsLoaded) - { - this.dropDown.Loaded += new RoutedEventHandler(this.DropDown_Loaded_FocusDropDown); - } - - if (null != this.dropDown.Child && !this.dropDown.IsAncestorOf((DependencyObject)Keyboard.FocusedElement)) - { - this.dropDown.Child.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); - } - } - - private void DropDown_Loaded_FocusDropDown(object sender, RoutedEventArgs e) - { - this.Loaded -= this.DropDown_Loaded_FocusDropDown; - this.FocusDropDown(); - } - - private void DropDown_Closed(object sender, EventArgs e) - { - if (this.dropDown.IsKeyboardFocusWithin || Keyboard.FocusedElement == null) - { - this.dropDownButton.Focus(); - } - } - - #endregion DropDown IsOpen Handlers - - #region Apply Template - - partial void PostOnApplyTemplate() - { - this.AttachToVisualTree(); - this.ApplyDropDownButtonTemplate(); - } - - partial void PreOnApplyTemplate() - { - this.DetachFromVisualTree(); - } - - private void AttachToVisualTree() - { - this.dropDown.Opened += new EventHandler(this.DropDown_Opened); - this.dropDown.Closed += new EventHandler(this.DropDown_Closed); - } - - private void DetachFromVisualTree() - { - if (null != this.dropDown) - { - this.dropDown.Opened -= this.DropDown_Opened; - this.dropDown.Closed -= this.DropDown_Closed; - } - } - - #endregion Apply Template - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ScalableImageSource.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ScalableImageSource.cs deleted file mode 100644 index befc54009e2..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/ScalableImageSource.cs +++ /dev/null @@ -1,57 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Represents the source of an image that can render as a vector or as a bitmap. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - #region Using Directives - - using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.Diagnostics.CodeAnalysis; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Media; - using System.Windows.Media.Imaging; - using System.Windows.Media.Animation; - - #endregion - - /// - /// Partial class implementation for SeparatedList control. - /// - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public partial class ScalableImageSource : Freezable - { - #region Structors - - /// - /// Initializes a new instance of the class. - /// - public ScalableImageSource() - { - // This constructor intentionally left blank - } - - #endregion - - #region Overrides - - /// - /// Creates a new instance of the Freezable derived class. - /// - /// The new instance of the Freezable derived class. - protected override Freezable CreateInstanceCore() - { - return new ScalableImageSource(); - } - - #endregion Overrides - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/TextBlockService.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/TextBlockService.cs deleted file mode 100644 index 189811982c3..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/TextBlockService.cs +++ /dev/null @@ -1,95 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Provides attached properties for TextBlock control. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Media; - using System.Diagnostics.CodeAnalysis; - using System.ComponentModel; - using System.Diagnostics; - - /// - /// Attached property provider to control. - /// - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public static partial class TextBlockService - { - static partial void IsTextTrimmedMonitoringEnabledProperty_PropertyChangedImplementation(DependencyObject o, DependencyPropertyChangedEventArgs e) - { - TextBlock tb = o as TextBlock; - if (tb == null) - { - return; - } - - if ((bool)e.OldValue == true) - { - tb.SizeChanged -= OnTextBlockSizeChanged; - } - else - { - tb.SizeChanged += OnTextBlockSizeChanged; - } - } - - private static void OnTextBlockSizeChanged(object sender, SizeChangedEventArgs e) - { - var textBlock = (TextBlock)sender; - UpdateIsTextTrimmed(textBlock); - } - - static void OnTextBlockPropertyChanged(object sender, EventArgs e) - { - var textBlock = (TextBlock)sender; - UpdateIsTextTrimmed(textBlock); - } - - static void UpdateIsTextTrimmed(TextBlock textBlock) - { - Debug.Assert(textBlock != null); - - if (textBlock.TextWrapping != TextWrapping.NoWrap || textBlock.TextTrimming == TextTrimming.None) - { - SetIsTextTrimmed(textBlock, false); - } - else - { - SetIsTextTrimmed(textBlock, CalculateIsTextTrimmed(textBlock)); - } - } - - private static bool CalculateIsTextTrimmed(TextBlock textBlock) - { - if (!textBlock.IsArrangeValid) - { - return GetIsTextTrimmed(textBlock); - } - - Typeface typeface = new Typeface( - textBlock.FontFamily, - textBlock.FontStyle, - textBlock.FontWeight, - textBlock.FontStretch); - - // FormattedText is used to measure the whole width of the text held up by TextBlock container - FormattedText formattedText = new FormattedText( - textBlock.Text, - System.Threading.Thread.CurrentThread.CurrentCulture, - textBlock.FlowDirection, - typeface, - textBlock.FontSize, - textBlock.Foreground); - - return (formattedText.Width > textBlock.ActualWidth); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/WeakEventListener.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/WeakEventListener.cs deleted file mode 100644 index 3a95c477bd2..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/Common/WeakEventListener.cs +++ /dev/null @@ -1,52 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Windows; - - /// - /// A common weak event listener which can be used for different kinds of events. - /// - /// The EventArgs type for the event. - class WeakEventListener : IWeakEventListener where TEventArgs : EventArgs - { - EventHandler realHander; - - /// - /// Constructs an instance of WeakEventListener. - /// - /// The handler for the event. - public WeakEventListener(EventHandler handler) - { - if (handler == null) - { - throw new ArgumentNullException("handler"); - } - - this.realHander = handler; - } - - /// - /// Receives events from the centralized event manager. - /// - /// The type of the WeakEventManager calling this method. - /// Object that originated the event. - /// Event data. - /// - /// true if the listener handled the event. It is considered an error by the WeakEventManager handling in WPF to register a listener for an event that the listener does not handle. Regardless, the method should return false if it receives an event that it does not recognize or handle. - /// - public bool ReceiveWeakEvent(Type managerType, Object sender, EventArgs e) - { - TEventArgs realArgs = (TEventArgs)e; - - this.realHander(sender, realArgs); - - return true; - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/AutomationGroup.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/AutomationGroup.cs deleted file mode 100644 index bd2f839dc11..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/AutomationGroup.cs +++ /dev/null @@ -1,27 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System.Windows.Automation.Peers; - using System.Windows.Controls; - - /// - /// Represents a decorator that is always visible in the automation tree, indicating that its descendents belong to a logical group. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class AutomationGroup : ContentControl - { - /// - /// Returns the implementations for this control. - /// - /// The implementations for this control. - protected override AutomationPeer OnCreateAutomationPeer() - { - return new ExtendedFrameworkElementAutomationPeer(this, AutomationControlType.Group, true); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ExpanderButton.Generated.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ExpanderButton.Generated.cs deleted file mode 100644 index bf9c34e5d00..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ExpanderButton.Generated.cs +++ /dev/null @@ -1,44 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// 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.ComponentModel; -using System.Windows; - -namespace Microsoft.Management.UI.Internal -{ - - /// - /// Represents a toggle button used to expand or collapse elements. - /// - [Localizability(LocalizationCategory.None)] - partial class ExpanderButton - { - // - // CreateAutomationPeer - // - /// - /// Create an instance of the AutomationPeer. - /// - /// - /// An instance of the AutomationPeer. - /// - protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() - { - return new ExpanderButtonAutomationPeer(this); - } - - } -} -#endregion diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ExpanderButton.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ExpanderButton.cs deleted file mode 100644 index 785fd21ce8c..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/CommonControls/ExpanderButton.cs +++ /dev/null @@ -1,65 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System.Windows; - using System.Windows.Automation; - using System.Windows.Automation.Peers; - using System.Windows.Controls.Primitives; - - /// - /// Represents a toggle button used to expand or collapse elements. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public partial class ExpanderButton : ToggleButton - { - /// - /// Initializes a new instance of the class. - /// - public ExpanderButton() - { - // This constructor intentionally left blank - } - - /// - /// Invoked whenever the effective value of any dependency property on this has been updated. The specific dependency property that changed is reported in the arguments parameter. Overrides . - /// - /// The event data that describes the property that changed, as well as old and new values. - protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) - { - base.OnPropertyChanged(e); - - if (e.Property == ExpanderButton.IsCheckedProperty) - { - this.OnIsCheckedChanged(e); - } - } - - /// - /// Called when the property changes. - /// - /// The event data that describes the property that changed, as well as old and new values. - protected void OnIsCheckedChanged(DependencyPropertyChangedEventArgs args) - { - if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged)) - { - var peer = UIElementAutomationPeer.CreatePeerForElement(this); - - if (peer != null) - { - var oldValue = (bool?)args.OldValue; - var newValue = (bool?)args.NewValue; - - peer.RaisePropertyChangedEvent( - ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, - (oldValue == true) ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed, - (newValue == true) ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed); - } - } - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExceptionEventArgs.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExceptionEventArgs.cs deleted file mode 100644 index 48d5962c667..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExceptionEventArgs.cs +++ /dev/null @@ -1,46 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics.CodeAnalysis; - - /// - /// The EventArgs detailing the exception raised while - /// evaluating the filter. - /// - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class FilterExceptionEventArgs : EventArgs - { - /// - /// Gets the Exception that was raised when filtering was - /// evaluated. - /// - public Exception Exception - { - get; - private set; - } - - /// - /// Initializes a new instance of the FilterExceptionEventArgs - /// class. - /// - /// - /// The Exception that was raised when filtering was evaluated. - /// - public FilterExceptionEventArgs(Exception exception) - { - if (null == exception) - { - throw new ArgumentNullException("exception"); - } - - this.Exception = exception; - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOperandNode.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOperandNode.cs deleted file mode 100644 index 45e5a7930a6..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOperandNode.cs +++ /dev/null @@ -1,75 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - - /// - /// The FilterExpressionOperandNode class is responsible for holding a - /// FilterRule within the FilterExpression tree. - /// - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class FilterExpressionOperandNode : FilterExpressionNode - { - #region Properties - - /// - /// The FilterRule to evaluate. - /// - public FilterRule Rule - { - get; - protected set; - } - - #endregion Properties - - #region Ctor - - /// - /// Initializes a new instance of the FilterExpressionOperandNode - /// class. - /// - /// - /// The FilterRule to hold for evaluation. - /// - public FilterExpressionOperandNode(FilterRule rule) - { - if (null == rule) - { - throw new ArgumentNullException("rule"); - } - - this.Rule = rule; - } - - #endregion Ctor - - #region Public Methods - - /// - /// Evaluates the item against the contained FilterRule. - /// - /// - /// The item to pass to the contained FilterRule. - /// - /// - /// Returns true if the contained FilterRule evaluates to - /// true, false otherwise. - /// - public override bool Evaluate(object item) - { - Debug.Assert(null != this.Rule); - - return this.Rule.Evaluate(item); - } - - #endregion Public Methods - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs deleted file mode 100644 index 42ca4e54d1e..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs +++ /dev/null @@ -1,81 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - - /// - /// The ComparableValueFilterRule provides support for derived classes - /// that evaluate against IComparable values. - /// - /// - /// The generic parameter. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public abstract class ComparableValueFilterRule : FilterRule where T : IComparable - { - #region Properties - - /// - /// Gets or sets a value indicating whether null objects passed to Evaluate will - /// evaluate to true or false. - /// - protected bool DefaultNullValueEvaluation - { - get; - set; - } - - #endregion Properties - - #region Public Methods - - /// - /// Determines if item matches a derived classes criteria. - /// - /// - /// The item to match evaluate. - /// - /// - /// Returns true if the item matches, false otherwise. - /// - public override bool Evaluate(object item) - { - if (null == item) - { - return this.DefaultNullValueEvaluation; - } - - if (!this.IsValid) - { - return false; - } - - T castItem; - if (!FilterUtilities.TryCastItem(item, out castItem)) - { - return false; - } - - return this.Evaluate(castItem); - } - - /// - /// Determines if item matches a derived classes criteria. - /// - /// - /// The item to match evaluate. - /// - /// - /// Returns true if the item matches, false otherwise. - /// - protected abstract bool Evaluate(T data); - - #endregion Public Methods - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs deleted file mode 100644 index 2d89e5c6006..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs +++ /dev/null @@ -1,45 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - - /// - /// The DoesNotEqualFilterRule class evaluates an IComparable item to - /// check if it is not equal to the rule's value. - /// - /// - /// The generic parameter. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class DoesNotEqualFilterRule : EqualsFilterRule where T : IComparable - { - /// - /// Initializes a new instance of the DoesNotEqualFilterRule class. - /// - public DoesNotEqualFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_DoesNotEqual; - this.DefaultNullValueEvaluation = true; - } - - /// - /// Determines if item is not equal to Value. - /// - /// - /// The data to compare against. - /// - /// - /// Returns true if data is not equal to Value, false otherwise. - /// - protected override bool Evaluate(T data) - { - return !base.Evaluate(data); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs deleted file mode 100644 index 671f2cf5cca..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs +++ /dev/null @@ -1,48 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics; - - /// - /// The EqualsFilterRule class evaluates an IComparable item to - /// check if it is equal to the rule's value. - /// - /// - /// The generic parameter. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class EqualsFilterRule : SingleValueComparableValueFilterRule where T : IComparable - { - /// - /// Initializes a new instance of the EqualsFilterRule class. - /// - public EqualsFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_Equals; - } - - /// - /// Determines if item is equal to Value. - /// - /// - /// The data to compare against. - /// - /// - /// Returns true if data is equal to Value. - /// - protected override bool Evaluate(T data) - { - Debug.Assert(this.IsValid); - - int result = CustomTypeComparer.Compare(this.Value.GetCastValue(), data); - return (0 == result); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/FilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/FilterRule.cs deleted file mode 100644 index ef23092e2f3..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/FilterRule.cs +++ /dev/null @@ -1,82 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - - /// - /// The base class for all filtering rules. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public abstract class FilterRule : IEvaluate - { - /// - /// Gets a value indicating whether the FilterRule can be - /// evaluated in its current state. - /// - public virtual bool IsValid - { - get - { - return true; - } - } - - /// - /// Gets a display friendly name for the FilterRule. - /// - public string DisplayName - { - get; - protected set; - } - - /// - /// Initializes a new instance of the FilterRule class. - /// - protected FilterRule() - { - // HACK : Is there a way to statically enforce this? No... not ISerializable... - if (!this.GetType().IsSerializable) - { - throw new InvalidOperationException("FilterRules must be serializable."); - } - } - - /// - /// Gets a value indicating whether the supplied item meets the - /// criteria specified by this rule. - /// - /// The item to evaluate. - /// Returns true if the item meets the criteria. False otherwise. - public abstract bool Evaluate(object item); - - #region EvaluationResultInvalidated - - /// - /// Occurs when the values of this rule changes. - /// - [field:NonSerialized] - public event EventHandler EvaluationResultInvalidated; - - /// - /// Fires . - /// - protected void NotifyEvaluationResultInvalidated() - { - var eh = this.EvaluationResultInvalidated; - - if (null != eh) - { - eh(this, new EventArgs()); - } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs deleted file mode 100644 index 513aab8a8e3..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs +++ /dev/null @@ -1,59 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics; - using System.IO; - using System.Runtime.Serialization; - using System.Runtime.Serialization.Formatters.Binary; - - /// - /// The FilterRuleExtensions class provides extension methods - /// for FilterRule classes. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public static class FilterRuleExtensions - { - /// - /// Creates a deep copy of a FilterRule. - /// - /// - /// The FilterRule to clone. - /// - /// - /// Returns a deep copy of the passed in rule. - /// - public static FilterRule DeepCopy(this FilterRule rule) - { - if (null == rule) - { - throw new ArgumentNullException("rule"); - } - - Debug.Assert(rule.GetType().IsSerializable); - - BinaryFormatter formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone)); - MemoryStream ms = new MemoryStream(); - - FilterRule copy = null; - try - { - formatter.Serialize(ms, rule); - - ms.Position = 0; - copy = (FilterRule)formatter.Deserialize(ms); - } - finally - { - ms.Close(); - } - - return copy; - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs deleted file mode 100644 index f7561e9c951..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs +++ /dev/null @@ -1,123 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Runtime.Serialization; - using System.Diagnostics; - using System.ComponentModel; - - /// - /// The IsBetweenFilterRule class evaluates an item to see if it is between - /// the StartValue and EndValue of the rule. - /// - /// - /// The generic parameter. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class IsBetweenFilterRule : ComparableValueFilterRule where T : IComparable - { - #region Properties - - /// - /// Gets a value indicating whether the FilterRule can be - /// evaluated in its current state. - /// - public override bool IsValid - { - get - { - return this.StartValue.IsValid && this.EndValue.IsValid; - } - } - - /// - /// Gets the start value for the range. - /// - public ValidatingValue StartValue - { - get; - protected set; - } - - /// - /// Gets the end value for the range. - /// - public ValidatingValue EndValue - { - get; - protected set; - } - - #endregion Properties - - #region Ctor - - /// - /// Initializes a new instance of the IsBetweenFilterRule class. - /// - public IsBetweenFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_IsBetween; - - this.StartValue = new ValidatingValue(); - this.StartValue.PropertyChanged += new PropertyChangedEventHandler(this.Value_PropertyChanged); - - this.EndValue = new ValidatingValue(); - this.EndValue.PropertyChanged += new PropertyChangedEventHandler(this.Value_PropertyChanged); - } - - #endregion Ctor - - #region Public Methods - - /// - /// Evaluates data and determines if it is between - /// StartValue and EndValue. - /// - /// - /// The data to evaluate. - /// - /// - /// Returns true if data is between StartValue and EndValue, - /// false otherwise. - /// - protected override bool Evaluate(T data) - { - Debug.Assert(this.IsValid); - int startValueComparedToData = CustomTypeComparer.Compare(this.StartValue.GetCastValue(), data); - int endValueComparedToData = CustomTypeComparer.Compare(this.EndValue.GetCastValue(), data); - - bool isBetweenForward = startValueComparedToData < 0 && endValueComparedToData > 0; - bool isBetweenBackwards = endValueComparedToData < 0 && startValueComparedToData > 0; - - return isBetweenForward || isBetweenBackwards; - } - - #endregion Public Methods - - #region Value Change Handlers - - private void Value_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "Value") - { - this.NotifyEvaluationResultInvalidated(); - } - } - - [OnDeserialized] - private void Initialize(StreamingContext context) - { - this.StartValue.PropertyChanged += new PropertyChangedEventHandler(this.Value_PropertyChanged); - this.EndValue.PropertyChanged += new PropertyChangedEventHandler(this.Value_PropertyChanged); - } - - #endregion Value Change Handlers - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs deleted file mode 100644 index 0219ae79143..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs +++ /dev/null @@ -1,52 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - - /// - /// The IsEmptyFilterRule evaluates an item to determine whether it - /// is empty or not. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class IsEmptyFilterRule : FilterRule - { - /// - /// Initializes a new instance of the IsEmptyFilterRule class. - /// - public IsEmptyFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_IsEmpty; - } - - /// - /// Gets a values indicating whether the supplied item is empty. - /// - /// The item to evaluate. - /// - /// Returns true if the item is null or if the item is a string - /// composed of whitespace. False otherwise. - /// - public override bool Evaluate(object item) - { - if (null == item) - { - return true; - } - - Type type = item.GetType(); - - if (typeof(string) == type) - { - return 0 == ((string)item).Trim().Length; - } - - return false; - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs deleted file mode 100644 index 8d23a278f43..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs +++ /dev/null @@ -1,48 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics; - - /// - /// The IsGreaterThanFilterRule class evaluates an IComparable item to - /// check if it is greater than its value. - /// - /// - /// The generic parameter. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class IsGreaterThanFilterRule : SingleValueComparableValueFilterRule where T : IComparable - { - /// - /// Initializes a new instance of the IsGreaterThanFilterRule class. - /// - public IsGreaterThanFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_GreaterThanOrEqual; - } - - /// - /// Determines if item is greater than Value. - /// - /// - /// The data to compare against. - /// - /// - /// Returns true if data is greater than Value. - /// - protected override bool Evaluate(T data) - { - Debug.Assert(this.IsValid); - - int result = CustomTypeComparer.Compare(this.Value.GetCastValue(), data); - return (result <= 0); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs deleted file mode 100644 index 2160e20918e..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs +++ /dev/null @@ -1,48 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics; - - /// - /// The IsLessThanFilterRule class evaluates an IComparable item to - /// check if it is less than the rule's value. - /// - /// - /// The generic parameter. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class IsLessThanFilterRule : SingleValueComparableValueFilterRule where T : IComparable - { - /// - /// Initializes a new instance of the IsLessThanFilterRule class. - /// - public IsLessThanFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_LessThanOrEqual; - } - - /// - /// Determines if item is less than Value. - /// - /// - /// The data to compare against. - /// - /// - /// Returns true if data is less than Value. - /// - protected override bool Evaluate(T item) - { - Debug.Assert(this.IsValid); - - int result = CustomTypeComparer.Compare(this.Value.GetCastValue(), item); - return (result >= 0); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs deleted file mode 100644 index 680562195ef..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs +++ /dev/null @@ -1,40 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - - /// - /// The IsNotEmptyFilterRule evaluates an item to determine whether it - /// is empty or not. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class IsNotEmptyFilterRule : IsEmptyFilterRule - { - /// - /// Initializes a new instance of the IsNotEmptyFilterRule class. - /// - public IsNotEmptyFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_IsNotEmpty; - } - - /// - /// Gets a values indicating whether the supplied item is not empty. - /// - /// The item to evaluate. - /// - /// Returns false if the item is null or if the item is a string - /// composed of whitespace. True otherwise. - /// - public override bool Evaluate(object item) - { - return !base.Evaluate(item); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs deleted file mode 100644 index f97f90d541f..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs +++ /dev/null @@ -1,122 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Runtime.Serialization; - - /// - /// The SelectorFilterRule represents a rule composed of other rules. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class SelectorFilterRule : FilterRule - { - #region Properties - - /// - /// Gets a value indicating whether the rule can be evaluated. - /// - public override bool IsValid - { - get - { - return (this.AvailableRules.IsValid && this.AvailableRules.SelectedValue.IsValid); - } - } - - /// - /// Gets the collection of available rules. - /// - public ValidatingSelectorValue AvailableRules - { - get; - protected set; - } - - #endregion Properties - - #region Ctor - - /// - /// Creates a new SelectorFilterRule instance. - /// - public SelectorFilterRule() - { - this.AvailableRules = new ValidatingSelectorValue(); - this.AvailableRules.SelectedValueChanged += new EventHandler>(this.AvailableRules_SelectedValueChanged); - } - - #endregion Ctor - - #region Public Methods - - /// - /// Evaluates whether the item is inclusive. - /// - /// - /// The item to evaluate. - /// - /// - /// Returns true if the item matches the filtering criteria, false otherwise. - /// - public override bool Evaluate(object item) - { - if (!this.IsValid) - { - return false; - } - - return this.AvailableRules.SelectedValue.Evaluate(item); - } - - /// - /// Called when the SelectedValue within AvailableRules changes. - /// - /// - /// The old FilterRule. - /// - /// - /// The new FilterRule. - /// - protected void OnSelectedValueChanged(FilterRule oldValue, FilterRule newValue) - { - FilterRuleCustomizationFactory.FactoryInstance.ClearValues(newValue); - FilterRuleCustomizationFactory.FactoryInstance.TransferValues(oldValue, newValue); - FilterRuleCustomizationFactory.FactoryInstance.ClearValues(oldValue); - - newValue.EvaluationResultInvalidated += new EventHandler(this.SelectedValue_EvaluationResultInvalidated); - oldValue.EvaluationResultInvalidated -= new EventHandler(this.SelectedValue_EvaluationResultInvalidated); - - this.NotifyEvaluationResultInvalidated(); - } - - private void SelectedValue_EvaluationResultInvalidated(object sender, EventArgs e) - { - this.NotifyEvaluationResultInvalidated(); - } - - #endregion Public Methods - - #region Private Methods - - [OnDeserialized] - private void Initialize(StreamingContext context) - { - this.AvailableRules.SelectedValueChanged += new EventHandler>(this.AvailableRules_SelectedValueChanged); - this.AvailableRules.SelectedValue.EvaluationResultInvalidated += new EventHandler(this.SelectedValue_EvaluationResultInvalidated); - } - - private void AvailableRules_SelectedValueChanged(object sender, PropertyChangedEventArgs e) - { - this.OnSelectedValueChanged(e.OldValue, e.NewValue); - } - - - #endregion Private Methods - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs deleted file mode 100644 index f48462217c7..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs +++ /dev/null @@ -1,75 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Runtime.Serialization; - using System.ComponentModel; - - /// - /// The SingleValueComparableValueFilterRule provides support for derived classes - /// that take a single input and evaluate against IComparable values. - /// - /// The generic parameter. - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public abstract class SingleValueComparableValueFilterRule : ComparableValueFilterRule where T : IComparable - { - #region Properties - - /// - /// Gets a value that holds user input. - /// - public ValidatingValue Value - { - get; - protected set; - } - - /// - /// Gets a value indicating whether the FilterRule can be - /// evaluated in its current state. - /// - public override bool IsValid - { - get - { - return this.Value.IsValid; - } - } - - #endregion Properties - - #region Ctor - - /// - /// Initializes a new instance of the SingleValueComparableValueFilterRule class. - /// - protected SingleValueComparableValueFilterRule() - { - this.Value = new ValidatingValue(); - this.Value.PropertyChanged += new PropertyChangedEventHandler(this.Value_PropertyChanged); - } - - #endregion Ctor - - private void Value_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "Value") - { - this.NotifyEvaluationResultInvalidated(); - } - } - - [OnDeserialized] - private void Initialize(StreamingContext context) - { - this.Value.PropertyChanged += new PropertyChangedEventHandler(this.Value_PropertyChanged); - } - } -} - diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs deleted file mode 100644 index 67d510c6cd5..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs +++ /dev/null @@ -1,48 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics; - - /// - /// The TextContainsFilterRule class evaluates a string item to - /// check if it is contains the rule's value within it. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class TextContainsFilterRule : TextFilterRule - { - private static readonly string TextContainsCharactersRegexPattern = "{0}"; - private static readonly string TextContainsWordsRegexPattern = WordBoundaryRegexPattern + TextContainsCharactersRegexPattern + WordBoundaryRegexPattern; - - /// - /// Initializes a new instance of the TextContainsFilterRule class. - /// - public TextContainsFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_Contains; - } - - /// - /// Determines if Value is contained within data. - /// - /// - /// The data to compare with. - /// - /// - /// Returns true if data contains Value, false otherwise. - /// - protected override bool Evaluate(string data) - { - Debug.Assert(this.IsValid); - - // True "text contains": \\ - return this.ExactMatchEvaluate(data, TextContainsCharactersRegexPattern, TextContainsWordsRegexPattern); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs deleted file mode 100644 index 55b3b1c5b07..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs +++ /dev/null @@ -1,42 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - - /// - /// The TextDoesNotContainFilterRule class evaluates a string item to - /// check if it is does not contain the rule's value within it. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class TextDoesNotContainFilterRule : TextContainsFilterRule - { - /// - /// Initializes a new instance of the TextDoesNotContainFilterRule class. - /// - public TextDoesNotContainFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_DoesNotContain; - this.DefaultNullValueEvaluation = true; - } - - /// - /// Determines if Value is not contained within data. - /// - /// - /// The data to compare with. - /// - /// - /// Returns true if data does not contain Value, false otherwise. - /// - protected override bool Evaluate(string data) - { - return !base.Evaluate(data); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs deleted file mode 100644 index 17f9df00568..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs +++ /dev/null @@ -1,42 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - - /// - /// The TextDoesNotEqualFilterRule class evaluates a string item to - /// check if it is not equal to the rule's value. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class TextDoesNotEqualFilterRule : TextEqualsFilterRule - { - /// - /// Initializes a new instance of the TextDoesNotEqualFilterRule class. - /// - public TextDoesNotEqualFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_DoesNotEqual; - this.DefaultNullValueEvaluation = true; - } - - /// - /// Determines if data is not equal to Value. - /// - /// - /// The value to compare against. - /// - /// - /// Returns true is data does not equal Value, false otherwise. - /// - protected override bool Evaluate(string data) - { - return !base.Evaluate(data); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs deleted file mode 100644 index 50b8b0b8798..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs +++ /dev/null @@ -1,47 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics; - - /// - /// The TextEndsWithFilterRule class evaluates a string item to - /// check if it ends with the rule's value. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class TextEndsWithFilterRule : TextFilterRule - { - private static readonly string TextEndsWithCharactersRegexPattern = "{0}$"; - private static readonly string TextEndsWithWordsRegexPattern = WordBoundaryRegexPattern + TextEndsWithCharactersRegexPattern; - - /// - /// Initializes a new instance of the TextEndsWithFilterRule class. - /// - public TextEndsWithFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_TextEndsWith; - } - - /// - /// Determines if data ends with Value. - /// - /// - /// The value to compare with. - /// - /// - /// Returns true is data ends with Value, false otherwise. - /// - protected override bool Evaluate(string data) - { - Debug.Assert(this.IsValid); - - return this.ExactMatchEvaluate(data, TextEndsWithCharactersRegexPattern, TextEndsWithWordsRegexPattern); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs deleted file mode 100644 index 98bde614129..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs +++ /dev/null @@ -1,46 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics; - - /// - /// The TextEqualsFilterRule class evaluates a string item to - /// check if it is equal to the rule's value. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class TextEqualsFilterRule : TextFilterRule - { - private static readonly string TextEqualsCharactersRegexPattern = "^{0}$"; - - /// - /// Initializes a new instance of the TextEqualsFilterRule class. - /// - public TextEqualsFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_Equals; - } - - /// - /// Determines if data is equal to Value. - /// - /// - /// The value to compare against. - /// - /// - /// Returns true is data equals Value, false otherwise. - /// - protected override bool Evaluate(string data) - { - Debug.Assert(this.IsValid); - - return this.ExactMatchEvaluate(data, TextEqualsCharactersRegexPattern, TextEqualsCharactersRegexPattern); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs deleted file mode 100644 index 5440a299114..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs +++ /dev/null @@ -1,48 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics; - - /// - /// The TextStartsWithFilterRule class evaluates a string item to - /// check if it starts with the rule's value. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class TextStartsWithFilterRule : TextFilterRule - { - private static readonly string TextStartsWithCharactersRegexPattern = "^{0}"; - private static readonly string TextStartsWithWordsRegexPattern = TextStartsWithCharactersRegexPattern + WordBoundaryRegexPattern; - - /// - /// Initializes a new instance of the TextStartsWithFilterRule class. - /// - public TextStartsWithFilterRule() - { - this.DisplayName = UICultureResources.FilterRule_TextStartsWith; - } - - /// - /// Determines if data starts with Value. - /// - /// - /// The value to compare with. - /// - /// - /// Returns true is data starts with Value, false otherwise. - /// - protected override bool Evaluate(string data) - { - Debug.Assert(this.IsValid); - - return this.ExactMatchEvaluate(data, TextStartsWithCharactersRegexPattern, TextStartsWithWordsRegexPattern); - } - } - -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterUtilities.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterUtilities.cs deleted file mode 100644 index 436cb1c594e..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/FilterUtilities.cs +++ /dev/null @@ -1,50 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics; - - /// - /// Provides common utilities for filtering. - /// - internal static class FilterUtilities - { - internal static bool TryCastItem(object item, out T castItem) - { - castItem = default(T); - - bool isItemUncastable = null == item && typeof(T).IsValueType; - if (isItemUncastable) - { - return false; - } - - bool shouldCastToString = null != item && typeof(string) == typeof(T); - if (shouldCastToString) - { - // NOTE: string => T doesn't compile. We confuse the type system - // and use string => object => T to make this work. - object stringPropertyValue = item.ToString(); - castItem = (T)stringPropertyValue; - return true; - } - - try - { - castItem = (T)item; - return true; - } - catch (InvalidCastException e) - { - Debug.Print(e.ToString()); - } - - return false; - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/IEvaluate.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/IEvaluate.cs deleted file mode 100644 index 03e4cc74859..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/IEvaluate.cs +++ /dev/null @@ -1,29 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - /// - /// The IEvaluate interface provides the most basic - /// support for the evaluation of an item against - /// criteria defined in a derived class. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public interface IEvaluate - { - /// - /// Gets a values indicating whether the supplied item has meet the - /// criteria rule specificed by the rule. - /// - /// - /// The item to evaluate. - /// - /// - /// Returns true if the item meets the criteria. False otherwise. - /// - bool Evaluate(object item); - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs deleted file mode 100644 index ddccb268549..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs +++ /dev/null @@ -1,32 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - - /// - /// Provides a way to create a custom rule in order to check the validity of user input. - /// - [Serializable] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public abstract class DataErrorInfoValidationRule - { - /// - /// When overridden in a derived class, performs validation checks on a value. - /// - /// - /// The value to check. - /// - /// - /// The culture to use in this rule. - /// - /// - /// A DataErrorInfoValidationResult object. - /// - public abstract DataErrorInfoValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo); - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchTextParseResult.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchTextParseResult.cs deleted file mode 100644 index 2e590935c69..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/FilterProviders/SearchTextParseResult.cs +++ /dev/null @@ -1,48 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Represents the result of search text parsing. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - #region Using Directives - - using System; - - #endregion - - /// - /// Represents the result of search text parsing. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class SearchTextParseResult - { - /// - /// Initializes a new instance of with the specified . - /// - /// The rule that resulted from parsing the search text. - /// The specified value is a null reference. - public SearchTextParseResult(FilterRule rule) - { - if (rule == null) - { - throw new ArgumentNullException("rule"); - } - - this.FilterRule = rule; - } - - /// - /// Gets the rule that resulted from parsing the search text. - /// - public FilterRule FilterRule - { - get; - private set; - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ManagementListStateDescriptorFactory.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ManagementListStateDescriptorFactory.cs deleted file mode 100644 index 01e8c704fb1..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ManagementListStateDescriptorFactory.cs +++ /dev/null @@ -1,27 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Diagnostics.CodeAnalysis; - - /// - /// Defines a factory which returns ManagementListStateDescriptors. - /// - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public class ManagementListStateDescriptorFactory : IStateDescriptorFactory - { - /// - /// Factory method that creates a ManagementListStateDescriptor. - /// - /// A new ManagementListStateDescriptor. - public StateDescriptor Create() - { - return new ManagementListStateDescriptor(); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ManagementListTitle.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ManagementListTitle.cs deleted file mode 100644 index dff27badb27..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ManagementListTitle.cs +++ /dev/null @@ -1,25 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System.Windows.Controls; - - /// - /// Partial class implementation for ManagementListTitle control. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public partial class ManagementListTitle : Control - { - /// - /// Initializes a new instance of the class. - /// - public ManagementListTitle() - { - // This constructor intentionally left blank - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ViewGroupToStringConverter.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ViewGroupToStringConverter.cs deleted file mode 100644 index 7808099479f..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/ViewGroupToStringConverter.cs +++ /dev/null @@ -1,69 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements group title as name plus (count). -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Data; - using System.Collections.Generic; - using System.Text; - using System.Globalization; - using System.Diagnostics.CodeAnalysis; - - /// - /// Converter from ViewGroup to group title string. - /// - [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] - internal class ViewGroupToStringConverter : IValueConverter - { - /// - /// Convert each ViewGroup into its name and its count. - /// - /// Value to be converted. - /// Type to convert the value to. - /// The conversion parameter. - /// Conversion culture. - /// The converted string. - public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) - { - CollectionViewGroup cvg = value as CollectionViewGroup; - if (null == cvg) - { - throw new ArgumentException("value must be of type CollectionViewGroup", "value"); - } - - string name = (!String.IsNullOrEmpty(cvg.Name.ToString())) ? cvg.Name.ToString() : UICultureResources.GroupTitleNone; - string display = String.Format(CultureInfo.CurrentCulture, "{0} ({1})", name, cvg.ItemCount); - - return display; - } - - /// - /// ConvertBack is not supported. - /// - /// Value to be converted. - /// Type to convert the value to. - /// The conversion parameter. - /// Conversion culture. - /// This method is not supported. - /// when calling the method. - public object ConvertBack( - object value, - Type targetType, - object parameter, - System.Globalization.CultureInfo culture) - { - // I can't think of nothing that could be added to the exception message - // that would be of further help - throw new NotSupportedException(); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/WaitRing.cs b/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/WaitRing.cs deleted file mode 100644 index c1a177f7e16..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ManagementList/ManagementList/WaitRing.cs +++ /dev/null @@ -1,40 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Waiting Ring definition -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Collections.Generic; - using System.Text; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Controls.Primitives; - using System.Windows.Data; - using System.Windows.Documents; - using System.Windows.Input; - using System.Windows.Media; - using System.Windows.Media.Imaging; - using System.Windows.Shapes; - - /// - /// Waiting Ring class. - /// - public class WaitRing : Control - { - /// - /// Static constructor for WaitRing. - /// - static WaitRing() - { - // This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class. - // This style is defined in themes\generic.xaml - DefaultStyleKeyProperty.OverrideMetadata(typeof(WaitRing), new FrameworkPropertyMetadata(typeof(WaitRing))); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/AllModulesControl.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/AllModulesControl.xaml.cs deleted file mode 100644 index 1a7cc18f61c..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/AllModulesControl.xaml.cs +++ /dev/null @@ -1,45 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System.Windows.Controls; - - /// - /// 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(); - } - - #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.PowerShell.GraphicalHost/ShowCommand/Controls/CmdletControl.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/CmdletControl.xaml.cs deleted file mode 100644 index 1b9e1899b04..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/CmdletControl.xaml.cs +++ /dev/null @@ -1,104 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System.Windows; - using System.Windows.Controls; - - /// - /// 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 += new RoutedEventHandler(ImportModuleButton_Click); - this.ParameterSetTabControl.DataContextChanged += new DependencyPropertyChangedEventHandler(this.ParameterSetTabControl_DataContextChanged); - this.KeyDown += new System.Windows.Input.KeyEventHandler(this.CmdletControl_KeyDown); - this.helpButton.innerButton.Click += new RoutedEventHandler(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.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageButton.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageButton.xaml.cs deleted file mode 100644 index 4cf9ff5e229..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageButton.xaml.cs +++ /dev/null @@ -1,44 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements ImageButton. -// -//----------------------------------------------------------------------- - -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System.Diagnostics.CodeAnalysis; - using System.Windows.Automation; - - /// - /// 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 += new System.Windows.RoutedEventHandler(this.ImageButton_Loaded); - } - - /// - /// Copies the automation id 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); - } - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageButtonBase.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageButtonBase.cs deleted file mode 100644 index 2406f0f4ee4..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageButtonBase.cs +++ /dev/null @@ -1,70 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements the ImageButtonBase base class to the ImageButton and ImageToggleButton. -// -//----------------------------------------------------------------------- - -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System; - using System.Diagnostics.CodeAnalysis; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Input; - using System.Windows.Media; - - /// - /// 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.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageButtonTooltipConverter.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageButtonTooltipConverter.cs deleted file mode 100644 index cb40184a84a..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageButtonTooltipConverter.cs +++ /dev/null @@ -1,79 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements ImageButtonToolTipConverter. -// -//----------------------------------------------------------------------- - -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System; - using System.Diagnostics.CodeAnalysis; - using System.Windows.Controls; - using System.Windows.Data; - - /// - /// 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.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml.cs deleted file mode 100644 index 11144fa71f8..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml.cs +++ /dev/null @@ -1,60 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements ImageToggleButton. -// -//----------------------------------------------------------------------- - -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System.Diagnostics.CodeAnalysis; - using System.Windows; - using System.Windows.Automation; - - /// - /// 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 += new System.Windows.RoutedEventHandler(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 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); - } - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/MultipleSelectionControl.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/MultipleSelectionControl.xaml.cs deleted file mode 100644 index c0cf9d3367e..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/MultipleSelectionControl.xaml.cs +++ /dev/null @@ -1,60 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System.Globalization; - using System.Management.Automation; - using System.Text; - using System.Windows; - using System.Windows.Controls; - - /// - /// 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.AppendFormat(CultureInfo.InvariantCulture, "{0},", selectedItem.ToString()); - } - - if (newComboText.Length > 1) - { - newComboText.Remove(newComboText.Length - 1, 1); - } - - comboxParameter.Text = newComboText.ToString(); - } - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/NotImportedCmdletControl.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/NotImportedCmdletControl.xaml.cs deleted file mode 100644 index 3d96e1268be..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/NotImportedCmdletControl.xaml.cs +++ /dev/null @@ -1,26 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System.Windows.Controls; - - /// - /// 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.PowerShell.GraphicalHost/ShowCommand/Controls/ParameterSetControl.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ParameterSetControl.xaml.cs deleted file mode 100644 index a98a91ed666..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ParameterSetControl.xaml.cs +++ /dev/null @@ -1,379 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Globalization; - using System.Management.Automation; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Data; - using Microsoft.Management.UI.Internal; - using Microsoft.PowerShell.Commands.ShowCommandExtension; - - /// - /// Interaction logic for ParameterSetControl.xaml - /// - public partial class ParameterSetControl : UserControl - { - /// - /// 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 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")); - 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.Format(CultureInfo.CurrentCulture, "chk{0}", 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.Format( - CultureInfo.CurrentCulture, - "combox{0}", - 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.Format("combox{0}", 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.Format(CultureInfo.CurrentCulture, "txt{0}", 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 += new RoutedEventHandler(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 -= new RoutedEventHandler(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.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) - { - 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.Format(CultureInfo.CurrentCulture, "lbl{0}", parameterViewModel.Name)); - - return label; - } - #endregion - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ShowModuleControl.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ShowModuleControl.xaml.cs deleted file mode 100644 index 9e602a515a0..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Controls/ShowModuleControl.xaml.cs +++ /dev/null @@ -1,89 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System.Management.Automation; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Input; - - /// - /// Control that 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 += new MouseEventHandler(this.CommandList_PreviewMouseMove); - - // See comment in method summary to understand why this event is handled - this.CommandList.SelectionChanged += new SelectionChangedEventHandler(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 it happened 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); - this.CommandList.Focus(); - } - #endregion Events Handlers - } -} diff --git a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/CommandEventArgs.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/CommandEventArgs.cs deleted file mode 100644 index c9a1a9edf3d..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/CommandEventArgs.cs +++ /dev/null @@ -1,38 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System; - - /// - /// 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; } - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/HelpNeededEventArgs.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/HelpNeededEventArgs.cs deleted file mode 100644 index 1182a68eccc..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/ViewModel/HelpNeededEventArgs.cs +++ /dev/null @@ -1,38 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System; - - /// - /// 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.PowerShell.GraphicalHost/ShowCommand/Windows/MultipleSelectionDialog.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Windows/MultipleSelectionDialog.xaml.cs deleted file mode 100644 index 910a8ef9552..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Windows/MultipleSelectionDialog.xaml.cs +++ /dev/null @@ -1,46 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- - -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System.Windows; - - /// - /// 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.PowerShell.GraphicalHost/ShowCommand/Windows/ShowAllModulesWindow.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Windows/ShowAllModulesWindow.xaml.cs deleted file mode 100644 index 0372510e2bc..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Windows/ShowAllModulesWindow.xaml.cs +++ /dev/null @@ -1,203 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using Microsoft.Management.UI.Internal; - using Microsoft.Management.UI.Internal.ShowCommand; - using System; - using System.Windows; - using System.Windows.Input; - - - /// - /// 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 += new SizeChangedEventHandler(this.ShowAllModulesWindow_SizeChanged); - this.LocationChanged += new System.EventHandler(this.ShowAllModulesWindow_LocationChanged); - this.StateChanged += new System.EventHandler(this.ShowAllModulesWindow_StateChanged); - this.Loaded += new RoutedEventHandler(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 - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Windows/ShowCommandWindow.xaml.cs b/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Windows/ShowCommandWindow.xaml.cs deleted file mode 100644 index 9ff3e1a28cb..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/ShowCommand/Windows/ShowCommandWindow.xaml.cs +++ /dev/null @@ -1,72 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - using System.Windows; - using Microsoft.Management.UI.Internal.ShowCommand; - - /// - /// 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 += new SizeChangedEventHandler(this.ShowCommandWindow_SizeChanged); - this.LocationChanged += new System.EventHandler(this.ShowCommandWindow_LocationChanged); - this.StateChanged += new System.EventHandler(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.PowerShell.GraphicalHost/commandHelpers/HelpWindowHelper.cs b/src/Microsoft.PowerShell.GraphicalHost/commandHelpers/HelpWindowHelper.cs deleted file mode 100644 index 551548d35e6..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/commandHelpers/HelpWindowHelper.cs +++ /dev/null @@ -1,60 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements HelpWindowHelper -// -//----------------------------------------------------------------------- - -namespace Microsoft.PowerShell.Commands.Internal -{ - 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; - - /// - /// Implements the WPF window part of the 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( - delegate(object ignored) - { - HelpWindow helpWindow = new HelpWindow(helpObj); - helpWindow.Owner = ownerWindow; - helpWindow.Show(); - - helpWindow.Closed += new EventHandler(delegate(object sender, EventArgs 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.PowerShell.GraphicalHost/commandHelpers/ShowCommandHelper.cs b/src/Microsoft.PowerShell.GraphicalHost/commandHelpers/ShowCommandHelper.cs deleted file mode 100644 index 93e94551b23..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/commandHelpers/ShowCommandHelper.cs +++ /dev/null @@ -1,1349 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// Implements ShowCommandHelper -// -//----------------------------------------------------------------------- - -namespace Microsoft.PowerShell.Commands.ShowCommandInternal -{ - 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; - - /// - /// 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, Workflow"; - - /// - /// 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 ShowCommandHelper 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 IDisposable - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - #endregion public Dispose - - #region internal static methods called using reflection from show-command and directly from ISE - /// - /// 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 to in order to import a module and refresh the command data - /// - /// module we want to import - /// 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 in order to import a module and refresh the command data - internal static string GetImportModuleCommand(string module, bool isRemoteRunspace = false, bool isFirstChance = true) - { - string scriptBase = "Import-Module " + ShowCommandHelper.SingleQuote(module); - - if (isRemoteRunspace) - { - if (isFirstChance) - { - scriptBase += ";@(Get-Command " + ShowCommandHelper.CommandTypeSegment + @" -ShowCommandInfo )"; - } - else - { - scriptBase += GetSerializedCommandScript(); - } - } - else - { - scriptBase += ";@(Get-Command " + ShowCommandHelper.CommandTypeSegment + ")"; - } - - scriptBase += ShowCommandHelper.GetGetModuleSuffix(); - return scriptBase; - } - - /// - /// 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[]; - if(objectArray == null) - { - objectArray = ((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(delegate(ModuleViewModel 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(delegate(CommandViewModel 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 and directly from ISE - - #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, new object[] { }); - } - 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, new object[] { }); - } - 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( - delegate(object ignored) - { - window.Activate(); - }), - String.Empty); - } - - /// - /// Sets a property in ISE that will allow the command to be run - /// - /// command to be run - /// true if it was possible to set the pending ISE command - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] - private bool SetPendingISECommand(string command) - { - Diagnostics.Assert(this.hostWindow != null, "The caller checks that"); - - Type internalHostType = this.hostWindow.GetType().Assembly.GetType("Microsoft.Windows.PowerShell.Gui.Internal.PSGInternalHost"); - if (internalHostType == null) - { - return false; - } - - object current = GetPropertyValue(internalHostType, null, "Current", BindingFlags.Public | BindingFlags.Static); - if (current == null) - { - return false; - } - - object powerShellTabs = GetPropertyValue(current.GetType(), current, "PowerShellTabs", BindingFlags.Public | BindingFlags.Instance); - if (powerShellTabs == null) - { - return false; - } - - object selectedPowerShellTab = GetPropertyValue(powerShellTabs.GetType(), powerShellTabs, "SelectedPowerShellTab", BindingFlags.Public | BindingFlags.Instance); - if (selectedPowerShellTab == null) - { - return false; - } - - return SetPropertyValue(selectedPowerShellTab.GetType(), selectedPowerShellTab, "PendingCommand", command, BindingFlags.NonPublic | BindingFlags.Instance); - } - - /// - /// 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(delegate(object ignored) - { - Diagnostics.Assert(commands.GetEnumerator().MoveNext(), "there is always at least one command"); - - 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( - delegate(object ignored) - { - Window childWindow = (Window)this.methodThatReturnsDialog.Invoke(null); - childWindow.Owner = this.hostWindow; - childWindow.Show(); - }), - String.Empty); - } - - /// - /// Called from CallMethodThatShowsDialog as the thread 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(delegate(object ignored) - { - Diagnostics.Assert(commandViewModelObj != null, "verified by caller"); - this.commandViewModel = (CommandViewModel)commandViewModelObj; - ShowCommandWindow showCommandWindow = new ShowCommandWindow(); - - this.commandViewModel.HelpNeeded += new EventHandler(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( - delegate(object ignored) - { - 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 += new EventHandler(this.Window_Closed); - this.window.Loaded += new RoutedEventHandler(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 -= new RoutedEventHandler(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 += new RoutedEventHandler(this.Buttons_RunClick); - copy.Click += new RoutedEventHandler(this.Buttons_CopyClick); - cancel.Click += new RoutedEventHandler(this.Buttons_CancelClick); - } - - /// - /// Sets up event listening for a new viewModel - /// - private void SetupViewModel() - { - this.allModulesViewModel.SelectedCommandInSelectedModuleNeedsHelp += new EventHandler(this.CommandNeedsHelp); - this.allModulesViewModel.SelectedCommandInSelectedModuleNeedsImportModule += new EventHandler(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.PowerShell.GraphicalHost/commandHelpers/outgridview.cs b/src/Microsoft.PowerShell.GraphicalHost/commandHelpers/outgridview.cs deleted file mode 100644 index 3010b90a1f8..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/commandHelpers/outgridview.cs +++ /dev/null @@ -1,601 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// PowerShell Graphical Out-GridView WPF support -// -//----------------------------------------------------------------------- - -namespace Microsoft.Management.UI.Internal -{ - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Diagnostics.CodeAnalysis; - using System.Management.Automation; - using System.Threading; - using System.Windows; - using System.Windows.Automation; - using System.Windows.Controls; - using System.Windows.Input; - using System.Windows.Media; - - /// - /// 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 += new RoutedEventHandler(this.GridViewWindowLoaded); - this.gridViewWindow.Content = CreateMainGrid(outputModeOptions); - this.gridViewWindow.Title = invocation; - this.gridViewWindow.Closed += new EventHandler(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 = 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 = 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 += new RoutedEventHandler(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 += new RoutedEventHandler(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( - delegate() - { - // 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( - delegate() - { - try - { - 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.PowerShell.GraphicalHost/map.json b/src/Microsoft.PowerShell.GraphicalHost/map.json deleted file mode 100644 index acdac2568b1..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/map.json +++ /dev/null @@ -1,185 +0,0 @@ -{ - "monad/src/graphicalhost/ResourcesHelpers/GraphicalHostResources.resx": "resources/GraphicalHostResources.resx", - "monad/src/graphicalhost/ResourcesHelpers/InvariantResources.resx": "resources/InvariantResources.resx", - "monad/src/graphicalhost/ResourcesHelpers/UICultureResources.resx": "resources/UICultureResources.resx", - "monad/src/graphicalhost/ResourcesHelpers/XamlLocalizableResources.resx": "resources/XamlLocalizableResources.resx", - "monad/src/graphicalhost/ResourcesHelpers/HelpWindowResources.resx": "resources/HelpWindowResources.resx", - "monad/src/graphicalhost/ResourcesHelpers/ShowCommandResources.resx": "resources/ShowCommandResources.resx", - "monad/src/graphicalhost/ManagementList/Common/AutomationTextBlock.cs": "ManagementList/Common/AutomationTextBlock.cs", - "monad/src/graphicalhost/ManagementList/Common/AutomationTextBlockAutomationPeer.cs": "ManagementList/Common/AutomationTextBlockAutomationPeer.cs", - "monad/src/graphicalhost/ManagementList/Common/BooleanBoxes.cs": "ManagementList/Common/BooleanBoxes.cs", - "monad/src/graphicalhost/ManagementList/Common/CommandHelper.cs": "ManagementList/Common/CommandHelper.cs", - "monad/src/graphicalhost/ManagementList/Common/CustomTypeComparer.cs": "ManagementList/Common/CustomTypeComparer.cs", - "monad/src/graphicalhost/ManagementList/Common/DataRoutedEventArgs.cs": "ManagementList/Common/DataRoutedEventArgs.cs", - "monad/src/graphicalhost/ManagementList/Common/DateTimeApproximationComparer.cs": "ManagementList/Common/DateTimeApproximationComparer.cs", - "monad/src/graphicalhost/ManagementList/Common/DismissiblePopup.cs": "ManagementList/Common/DismissiblePopup.cs", - "monad/src/graphicalhost/ManagementList/Common/DismissiblePopup.Generated.cs": "ManagementList/Common/DismissiblePopup.Generated.cs", - "monad/src/graphicalhost/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs": "ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs", - "monad/src/graphicalhost/ManagementList/Common/IAsyncProgress.cs": "ManagementList/Common/IAsyncProgress.cs", - "monad/src/graphicalhost/ManagementList/Common/IntegralConverter.cs": "ManagementList/Common/IntegralConverter.cs", - "monad/src/graphicalhost/ManagementList/Common/InverseBooleanConverter.cs": "ManagementList/Common/InverseBooleanConverter.cs", - "monad/src/graphicalhost/ManagementList/Common/IsEqualConverter.cs": "ManagementList/Common/IsEqualConverter.cs", - "monad/src/graphicalhost/ManagementList/Common/IsNotNullConverter.cs": "ManagementList/Common/IsNotNullConverter.cs", - "monad/src/graphicalhost/ManagementList/Common/IStateDescriptorFactory.cs": "ManagementList/Common/IStateDescriptorFactory.cs", - "monad/src/graphicalhost/ManagementList/Common/KeyboardHelp.cs": "ManagementList/Common/KeyboardHelp.cs", - "monad/src/graphicalhost/ManagementList/Common/ListOrganizer.cs": "ManagementList/Common/ListOrganizer.cs", - "monad/src/graphicalhost/ManagementList/Common/ListOrganizer.Generated.cs": "ManagementList/Common/ListOrganizer.Generated.cs", - "monad/src/graphicalhost/ManagementList/Common/ListOrganizerItem.cs": "ManagementList/Common/ListOrganizerItem.cs", - "monad/src/graphicalhost/ManagementList/Common/ListOrganizerItem.Generated.cs": "ManagementList/Common/ListOrganizerItem.Generated.cs", - "monad/src/graphicalhost/ManagementList/Common/MessageTextBox.cs": "ManagementList/Common/MessageTextBox.cs", - "monad/src/graphicalhost/ManagementList/Common/MessageTextBox.Generated.cs": "ManagementList/Common/MessageTextBox.Generated.cs", - "monad/src/graphicalhost/ManagementList/Common/PickerBase.cs": "ManagementList/Common/PickerBase.cs", - "monad/src/graphicalhost/ManagementList/Common/PickerBase.Generated.cs": "ManagementList/Common/PickerBase.Generated.cs", - "monad/src/graphicalhost/ManagementList/Common/PopupControlButton.cs": "ManagementList/Common/PopupControlButton.cs", - "monad/src/graphicalhost/ManagementList/Common/PopupControlButton.Generated.cs": "ManagementList/Common/PopupControlButton.Generated.cs", - "monad/src/graphicalhost/ManagementList/Common/PropertyChangedEventArgs.cs": "ManagementList/Common/PropertyChangedEventArgs.cs", - "monad/src/graphicalhost/ManagementList/Common/ReadOnlyObservableAsyncCollection.cs": "ManagementList/Common/ReadOnlyObservableAsyncCollection.cs", - "monad/src/graphicalhost/ManagementList/Common/ScalableImage.cs": "ManagementList/Common/ScalableImage.cs", - "monad/src/graphicalhost/ManagementList/Common/ScalableImage.Generated.cs": "ManagementList/Common/ScalableImage.Generated.cs", - "monad/src/graphicalhost/ManagementList/Common/ScalableImageSource.cs": "ManagementList/Common/ScalableImageSource.cs", - "monad/src/graphicalhost/ManagementList/Common/ScalableImageSource.Generated.cs": "ManagementList/Common/ScalableImageSource.Generated.cs", - "monad/src/graphicalhost/ManagementList/Common/StateDescriptor.cs": "ManagementList/Common/StateDescriptor.cs", - "monad/src/graphicalhost/ManagementList/Common/StringFormatConverter.cs": "ManagementList/Common/StringFormatConverter.cs", - "monad/src/graphicalhost/ManagementList/Common/TextBlockService.cs": "ManagementList/Common/TextBlockService.cs", - "monad/src/graphicalhost/ManagementList/Common/TextBlockService.Generated.cs": "ManagementList/Common/TextBlockService.Generated.cs", - "monad/src/graphicalhost/ManagementList/Common/TextTrimConverter.cs": "ManagementList/Common/TextTrimConverter.cs", - "monad/src/graphicalhost/ManagementList/Common/Utilities.cs": "ManagementList/Common/Utilities.cs", - "monad/src/graphicalhost/ManagementList/Common/VisualToAncestorDataConverter.cs": "ManagementList/Common/VisualToAncestorDataConverter.cs", - "monad/src/graphicalhost/ManagementList/Common/WeakEventListener.cs": "ManagementList/Common/WeakEventListener.cs", - "monad/src/graphicalhost/ManagementList/Common/WpfHelp.cs": "ManagementList/Common/WpfHelp.cs", - "monad/src/graphicalhost/ManagementList/CommonControls/AutomationGroup.cs": "ManagementList/CommonControls/AutomationGroup.cs", - "monad/src/graphicalhost/ManagementList/CommonControls/ExpanderButton.cs": "ManagementList/CommonControls/ExpanderButton.cs", - "monad/src/graphicalhost/ManagementList/CommonControls/ExpanderButton.Generated.cs": "ManagementList/CommonControls/ExpanderButton.Generated.cs", - "monad/src/graphicalhost/ManagementList/CommonControls/ExpanderButtonAutomationPeer.cs": "ManagementList/CommonControls/ExpanderButtonAutomationPeer.cs", - "monad/src/graphicalhost/ManagementList/CommonControls/Resizer.cs": "ManagementList/CommonControls/Resizer.cs", - "monad/src/graphicalhost/ManagementList/CommonControls/Resizer.Generated.cs": "ManagementList/CommonControls/Resizer.Generated.cs", - "monad/src/graphicalhost/ManagementList/CommonControls/ResizerGripThicknessConverter.cs": "ManagementList/CommonControls/ResizerGripThicknessConverter.cs", - "monad/src/graphicalhost/ManagementList/CommonControls/UIElementAdorner.cs": "ManagementList/CommonControls/UIElementAdorner.cs", - "monad/src/graphicalhost/ManagementList/CommonControls/UIElementAdorner.Generated.cs": "ManagementList/CommonControls/UIElementAdorner.Generated.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionAndOperatorNode.cs": "ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionAndOperatorNode.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionNode.cs": "ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionNode.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOperandNode.cs": "ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOperandNode.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOrOperatorNode.cs": "ManagementList/FilterCore/FilterExpressionNodes/FilterExpressionOrOperatorNode.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs": "ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs": "ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs": "ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/FilterRule.cs": "ManagementList/FilterCore/FilterRules/FilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs": "ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs": "ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs": "ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs": "ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs": "ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs": "ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs": "ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs": "ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs": "ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs": "ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs": "ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs": "ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs": "ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs": "ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs": "ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs": "ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/TextFilterRule.cs": "ManagementList/FilterCore/FilterRules/TextFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs": "ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationResult.cs": "ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationResult.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs": "ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/DefaultFilterRuleCustomizationFactory.cs": "ManagementList/FilterCore/DefaultFilterRuleCustomizationFactory.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterEvaluator.cs": "ManagementList/FilterCore/FilterEvaluator.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterExceptionEventArgs.cs": "ManagementList/FilterCore/FilterExceptionEventArgs.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterRuleCustomizationFactory.cs": "ManagementList/FilterCore/FilterRuleCustomizationFactory.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterStatus.cs": "ManagementList/FilterCore/FilterStatus.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/FilterUtilities.cs": "ManagementList/FilterCore/FilterUtilities.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/IEvaluate.cs": "ManagementList/FilterCore/IEvaluate.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/IFilterExpressionProvider.cs": "ManagementList/FilterCore/IFilterExpressionProvider.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/ItemsControlFilterEvaluator.cs": "ManagementList/FilterCore/ItemsControlFilterEvaluator.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/ValidatingSelectorValue.cs": "ManagementList/FilterCore/ValidatingSelectorValue.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/ValidatingValue.cs": "ManagementList/FilterCore/ValidatingValue.cs", - "monad/src/graphicalhost/ManagementList/FilterCore/ValidatingValueBase.cs": "ManagementList/FilterCore/ValidatingValueBase.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/AddFilterRulePicker.cs": "ManagementList/FilterProviders/AddFilterRulePicker.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/AddFilterRulePicker.Generated.cs": "ManagementList/FilterProviders/AddFilterRulePicker.Generated.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/AddFilterRulePickerItem.cs": "ManagementList/FilterProviders/AddFilterRulePickerItem.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/InputFieldBackgroundTextConverter.cs": "ManagementList/FilterProviders/InputFieldBackgroundTextConverter.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/IsValidatingValueValidConverter.cs": "ManagementList/FilterProviders/IsValidatingValueValidConverter.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/FilterRulePanel.cs": "ManagementList/FilterProviders/FilterRulePanel.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/FilterRulePanel.Generated.cs": "ManagementList/FilterProviders/FilterRulePanel.Generated.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/FilterRulePanelContentPresenter.cs": "ManagementList/FilterProviders/FilterRulePanelContentPresenter.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/FilterRulePanelController.cs": "ManagementList/FilterProviders/FilterRulePanelController.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/FilterRulePanelItem.cs": "ManagementList/FilterProviders/FilterRulePanelItem.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/FilterRulePanelItemType.cs": "ManagementList/FilterProviders/FilterRulePanelItemType.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/FilterRuleTemplateSelector.cs": "ManagementList/FilterProviders/FilterRuleTemplateSelector.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs": "ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/SearchBox.cs": "ManagementList/FilterProviders/SearchBox.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/SearchBox.Generated.cs": "ManagementList/FilterProviders/SearchBox.Generated.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/SearchTextParser.cs": "ManagementList/FilterProviders/SearchTextParser.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/SearchTextParseResult.cs": "ManagementList/FilterProviders/SearchTextParseResult.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/ValidatingSelectorValueToDisplayNameConverter.cs": "ManagementList/FilterProviders/ValidatingSelectorValueToDisplayNameConverter.cs", - "monad/src/graphicalhost/ManagementList/FilterProviders/ValidatingValueToGenericParameterTypeConverter.cs": "ManagementList/FilterProviders/ValidatingValueToGenericParameterTypeConverter.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/ColumnPicker.xaml.cs": "ManagementList/ManagementList/ColumnPicker.xaml.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/DefaultStringConverter.cs": "ManagementList/ManagementList/DefaultStringConverter.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/Innerlist.cs": "ManagementList/ManagementList/Innerlist.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/InnerList.Generated.cs": "ManagementList/ManagementList/InnerList.Generated.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/innerlistcolumn.cs": "ManagementList/ManagementList/innerlistcolumn.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/InnerListColumn.Generated.cs": "ManagementList/ManagementList/InnerListColumn.Generated.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/InnerListGridView.cs": "ManagementList/ManagementList/InnerListGridView.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/IPropertyValueGetter.cs": "ManagementList/ManagementList/IPropertyValueGetter.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/managementlist.cs": "ManagementList/ManagementList/managementlist.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/ManagementList.Generated.cs": "ManagementList/ManagementList/ManagementList.Generated.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/ManagementListStateDescriptor.cs": "ManagementList/ManagementList/ManagementListStateDescriptor.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/ManagementListStateDescriptorFactory.cs": "ManagementList/ManagementList/ManagementListStateDescriptorFactory.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/ManagementListTitle.cs": "ManagementList/ManagementList/ManagementListTitle.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/ManagementListTitle.Generated.cs": "ManagementList/ManagementList/ManagementListTitle.Generated.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/PropertyValueComparer.cs": "ManagementList/ManagementList/PropertyValueComparer.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/PropertyValueGetter.cs": "ManagementList/ManagementList/PropertyValueGetter.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/UIPropertyGroupDescription.cs": "ManagementList/ManagementList/UIPropertyGroupDescription.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/ViewGroupToStringConverter.cs": "ManagementList/ManagementList/ViewGroupToStringConverter.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/WaitRing.cs": "ManagementList/ManagementList/WaitRing.cs", - "monad/src/graphicalhost/HelpWindow/HelpParagraphBuilder.cs": "HelpWindow/HelpParagraphBuilder.cs", - "monad/src/graphicalhost/HelpWindow/HelpViewModel.cs": "HelpWindow/HelpViewModel.cs", - "monad/src/graphicalhost/HelpWindow/HelpWindow.xaml.cs": "HelpWindow/HelpWindow.xaml.cs", - "monad/src/graphicalhost/HelpWindow/HelpWindowSettings.Designer.cs": "HelpWindow/HelpWindowSettings.Designer.cs", - "monad/src/graphicalhost/HelpWindow/ParagraphBuilder.cs": "HelpWindow/ParagraphBuilder.cs", - "monad/src/graphicalhost/HelpWindow/ParagraphSearcher.cs": "HelpWindow/ParagraphSearcher.cs", - "monad/src/graphicalhost/HelpWindow/SettingsDialog.xaml.cs": "HelpWindow/SettingsDialog.xaml.cs", - "monad/src/graphicalhost/commandHelpers/outgridview.cs": "commandHelpers/outgridview.cs", - "monad/src/graphicalhost/CommonHelper.cs": "CommonHelper.cs", - "monad/src/graphicalhost/ShowCommand/ShowCommandSettings.Designer.cs": "ShowCommand/ShowCommandSettings.Designer.cs", - "monad/src/graphicalhost/ShowCommand/Controls/AllModulesControl.xaml.cs": "ShowCommand/Controls/AllModulesControl.xaml.cs", - "monad/src/graphicalhost/ShowCommand/Controls/CmdletControl.xaml.cs": "ShowCommand/Controls/CmdletControl.xaml.cs", - "monad/src/graphicalhost/ShowCommand/Controls/ImageButton/ImageButton.xaml.cs": "ShowCommand/Controls/ImageButton/ImageButton.xaml.cs", - "monad/src/graphicalhost/ShowCommand/Controls/ImageButton/ImageButtonBase.cs": "ShowCommand/Controls/ImageButton/ImageButtonBase.cs", - "monad/src/graphicalhost/ShowCommand/Controls/ImageButton/ImageButtonTooltipConverter.cs": "ShowCommand/Controls/ImageButton/ImageButtonTooltipConverter.cs", - "monad/src/graphicalhost/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml.cs": "ShowCommand/Controls/ImageButton/ImageToggleButton.xaml.cs", - "monad/src/graphicalhost/ShowCommand/Controls/NotImportedCmdletControl.xaml.cs": "ShowCommand/Controls/NotImportedCmdletControl.xaml.cs", - "monad/src/graphicalhost/ShowCommand/Controls/MultipleSelectionControl.xaml.cs": "ShowCommand/Controls/MultipleSelectionControl.xaml.cs", - "monad/src/graphicalhost/ShowCommand/Controls/ParameterSetControl.xaml.cs": "ShowCommand/Controls/ParameterSetControl.xaml.cs", - "monad/src/graphicalhost/commandHelpers/HelpWindowHelper.cs": "commandHelpers/HelpWindowHelper.cs", - "monad/src/graphicalhost/commandHelpers/ShowCommandHelper.cs": "commandHelpers/ShowCommandHelper.cs", - "monad/src/graphicalhost/ShowCommand/Controls/ShowModuleControl.xaml.cs": "ShowCommand/Controls/ShowModuleControl.xaml.cs", - "monad/src/graphicalhost/ShowCommand/ViewModel/AllModulesViewModel.cs": "ShowCommand/ViewModel/AllModulesViewModel.cs", - "monad/src/graphicalhost/ShowCommand/ViewModel/CommandEventArgs.cs": "ShowCommand/ViewModel/CommandEventArgs.cs", - "monad/src/graphicalhost/ShowCommand/ViewModel/CommandViewModel.cs": "ShowCommand/ViewModel/CommandViewModel.cs", - "monad/src/graphicalhost/ShowCommand/ViewModel/HelpNeededEventArgs.cs": "ShowCommand/ViewModel/HelpNeededEventArgs.cs", - "monad/src/graphicalhost/ShowCommand/ViewModel/ImportModuleEventArgs.cs": "ShowCommand/ViewModel/ImportModuleEventArgs.cs", - "monad/src/graphicalhost/ShowCommand/ViewModel/ModuleViewModel.cs": "ShowCommand/ViewModel/ModuleViewModel.cs", - "monad/src/graphicalhost/ShowCommand/ViewModel/ParameterSetViewModel.cs": "ShowCommand/ViewModel/ParameterSetViewModel.cs", - "monad/src/graphicalhost/ShowCommand/ViewModel/ParameterViewModel.cs": "ShowCommand/ViewModel/ParameterViewModel.cs", - "monad/src/graphicalhost/ShowCommand/Windows/ShowAllModulesWindow.xaml.cs": "ShowCommand/Windows/ShowAllModulesWindow.xaml.cs", - "monad/src/graphicalhost/ShowCommand/Windows/ShowCommandWindow.xaml.cs": "ShowCommand/Windows/ShowCommandWindow.xaml.cs", - "monad/src/graphicalhost/ShowCommand/Windows/MultipleSelectionDialog.xaml.cs": "ShowCommand/Windows/MultipleSelectionDialog.xaml.cs", - "monad/src/graphicalhost/ManagementList/ManagementList/ColumnPicker.xaml": "xamls/ColumnPicker.xaml", - "monad/src/graphicalhost/HelpWindow/HelpWindow.xaml": "xamls/HelpWindow.xaml", - "monad/src/graphicalhost/HelpWindow/SettingsDialog.xaml": "xamls/SettingsDialog.xaml", - "monad/src/graphicalhost/ShowCommand/Controls/AllModulesControl.xaml": "xamls/AllModulesControl.xaml", - "monad/src/graphicalhost/ShowCommand/Controls/CmdletControl.xaml": "xamls/CmdletControl.xaml", - "monad/src/graphicalhost/ShowCommand/Controls/ImageButton/ImageButton.xaml": "xamls/ImageButton.xaml", - "monad/src/graphicalhost/ShowCommand/Controls/ImageButton/ImageButtonCommon.xaml": "xamls/ImageButtonCommon.xaml", - "monad/src/graphicalhost/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml": "xamls/ImageToggleButton.xaml", - "monad/src/graphicalhost/ShowCommand/Controls/NotImportedCmdletControl.xaml": "xamls/NotImportedCmdletControl.xaml", - "monad/src/graphicalhost/ShowCommand/Controls/ParameterSetControl.xaml": "xamls/ParameterSetControl.xaml", - "monad/src/graphicalhost/ShowCommand/Controls/ShowModuleControl.xaml": "xamls/ShowModuleControl.xaml", - "monad/src/graphicalhost/ShowCommand/Controls/MultipleSelectionControl.xaml": "xamls/MultipleSelectionControl.xaml", - "monad/src/graphicalhost/ShowCommand/Windows/ShowAllModulesWindow.xaml": "xamls/ShowAllModulesWindow.xaml", - "monad/src/graphicalhost/ShowCommand/Windows/ShowCommandWindow.xaml": "xamls/ShowCommandWindow.xaml", - "monad/src/graphicalhost/ShowCommand/Windows/MultipleSelectionDialog.xaml": "xamls/MultipleSelectionDialog.xaml" -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.GraphicalHost/resources/GraphicalHostResources.resx b/src/Microsoft.PowerShell.GraphicalHost/resources/GraphicalHostResources.resx deleted file mode 100644 index b84f9f152d1..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/resources/GraphicalHostResources.resx +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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.PowerShell.GraphicalHost/resources/HelpWindowResources.resx b/src/Microsoft.PowerShell.GraphicalHost/resources/HelpWindowResources.resx deleted file mode 100644 index 52c62a9c132..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/resources/HelpWindowResources.resx +++ /dev/null @@ -1,230 +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 - - - 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.PowerShell.GraphicalHost/resources/InvariantResources.resx b/src/Microsoft.PowerShell.GraphicalHost/resources/InvariantResources.resx deleted file mode 100644 index cd03eb26a26..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/resources/InvariantResources.resx +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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.PowerShell.GraphicalHost/resources/ShowCommandResources.resx b/src/Microsoft.PowerShell.GraphicalHost/resources/ShowCommandResources.resx deleted file mode 100644 index f35479f98da..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/resources/ShowCommandResources.resx +++ /dev/null @@ -1,239 +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 - - - 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.PowerShell.GraphicalHost/resources/UICultureResources.resx b/src/Microsoft.PowerShell.GraphicalHost/resources/UICultureResources.resx deleted file mode 100644 index 32ae43c624c..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/resources/UICultureResources.resx +++ /dev/null @@ -1,230 +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 - - - 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.PowerShell.GraphicalHost/resources/XamlLocalizableResources.resx b/src/Microsoft.PowerShell.GraphicalHost/resources/XamlLocalizableResources.resx deleted file mode 100644 index 27b106313b7..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/resources/XamlLocalizableResources.resx +++ /dev/null @@ -1,414 +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 - - - 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.PowerShell.GraphicalHost/xamls/AllModulesControl.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/AllModulesControl.xaml deleted file mode 100644 index 905cbc4e37a..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/AllModulesControl.xaml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/CmdletControl.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/CmdletControl.xaml deleted file mode 100644 index 240e697d3f1..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/CmdletControl.xaml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/ImageButton.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/ImageButton.xaml deleted file mode 100644 index 715518522cc..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/ImageButton.xaml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/ImageButtonCommon.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/ImageButtonCommon.xaml deleted file mode 100644 index db58dbf767c..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/ImageButtonCommon.xaml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/ImageToggleButton.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/ImageToggleButton.xaml deleted file mode 100644 index da23d1fc6c5..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/ImageToggleButton.xaml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/MultipleSelectionControl.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/MultipleSelectionControl.xaml deleted file mode 100644 index 0cfa6335c42..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/MultipleSelectionControl.xaml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/MultipleSelectionDialog.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/MultipleSelectionDialog.xaml deleted file mode 100644 index fadc5e49c56..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/MultipleSelectionDialog.xaml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/NotImportedCmdletControl.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/NotImportedCmdletControl.xaml deleted file mode 100644 index 8a3d26b6f01..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/NotImportedCmdletControl.xaml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/SettingsDialog.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/SettingsDialog.xaml deleted file mode 100644 index b8cc2c5aa89..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/SettingsDialog.xaml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/ShowAllModulesWindow.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/ShowAllModulesWindow.xaml deleted file mode 100644 index 49eaeb32f0c..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/ShowAllModulesWindow.xaml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/ShowCommandWindow.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/ShowCommandWindow.xaml deleted file mode 100644 index dfc2a8e1e55..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/ShowCommandWindow.xaml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.PowerShell.GraphicalHost/xamls/ShowModuleControl.xaml b/src/Microsoft.PowerShell.GraphicalHost/xamls/ShowModuleControl.xaml deleted file mode 100644 index b7ee396e96a..00000000000 --- a/src/Microsoft.PowerShell.GraphicalHost/xamls/ShowModuleControl.xaml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - 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 609320215de..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); } } @@ -155,14 +158,13 @@ public void Dispose() { if (objectName != IntPtr.Zero) { - ClrFacade.DestroyStructure(objectName); + Marshal.DestroyStructure(objectName); Marshal.FreeHGlobal(objectName); objectName = IntPtr.Zero; } } } - // 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 @@ -179,73 +181,6 @@ struct LARGE_INTEGER #pragma warning restore 0649, 0169 #endregion Structures - /// - /// Wraps calls to Marshal functions that differ between .Net 4.5 and CoreCLR. .Net 4.5.1 types are not allowed for PowerShell. - /// - internal class ClrFacade - { - /// - /// Private constructor to prevent auto-generation of a default constructor - /// - private ClrFacade() - { - } - - /// - /// Facade for Marshal.SizeOf - /// - internal static int SizeOf() - { -#if CORECLR - // Marshal.SizeOf(Type) is obsolete in CoreCLR - return Marshal.SizeOf(); -#else - return Marshal.SizeOf(typeof(T)); -#endif - } - - /// - /// Facade for Marshal.DestroyStructure - /// - internal static void DestroyStructure(IntPtr ptr) - { -#if CORECLR - // Marshal.DestroyStructure(IntPtr, Type) is obsolete in CoreCLR - Marshal.DestroyStructure(ptr); -#else - Marshal.DestroyStructure(ptr, typeof(T)); -#endif - } - - /// - /// Facade for Marshal.PtrToStructure - /// - internal static T PtrToStructure(IntPtr ptr) - { -#if CORECLR - // Marshal.PtrToStructure(IntPtr, Type) is obsolete in CoreCLR - return Marshal.PtrToStructure(ptr); -#else - return (T)Marshal.PtrToStructure(ptr, typeof(T)); -#endif - } - - /// - /// Wraps Marshal.StructureToPtr to hide differences between the CLRs. - /// - internal static void StructureToPtr( - T structure, - IntPtr ptr, - bool deleteOld) - { -#if CORECLR - Marshal.StructureToPtr( structure, ptr, deleteOld ); -#else - Marshal.StructureToPtr(structure, ptr, deleteOld); -#endif - } - } - internal static class Win32 { #region Constants @@ -262,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; @@ -276,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; @@ -402,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; @@ -415,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, @@ -424,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)] @@ -433,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 289ee19542d..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 { @@ -13,7 +12,6 @@ namespace System.Management.Automation /// internal static class PInvokeDllNames { -#if CORECLR internal const string GetLastErrorDllName = "api-ms-win-core-errorhandling-l1-1-0.dll"; /* 1*/ internal const string LookupAccountSidDllName = "api-ms-win-security-lsalookup-l2-1-1.dll"; /* 2*/ internal const string IsValidSidDllName = "api-ms-win-security-base-l1-2-0.dll"; /* 3*/ @@ -28,21 +26,5 @@ internal static class PInvokeDllNames internal const string SetSecurityDescriptorDaclDllName = "api-ms-win-security-base-l1-2-0"; /*12*/ internal const string FormatMessageDllName = "api-ms-win-core-localization-l1-2-1"; /*13*/ internal const string GetVersionExDllName = "api-ms-win-core-sysinfo-l1-2-1.dll"; /*14*/ -#else - internal const string GetLastErrorDllName = "kernel32.dll"; /* 1*/ - internal const string LookupAccountSidDllName = "advapi32.dll"; /* 2*/ - internal const string IsValidSidDllName = "advapi32.dll"; /* 3*/ - internal const string GetLengthSidDllName = "advapi32.dll"; /* 4*/ - internal const string LsaFreeMemoryDllName = "advapi32.dll"; /* 5*/ - internal const string LsaOpenPolicyDllName = "advapi32.dll"; /* 6*/ - internal const string LsaQueryInformationPolicyDllName = "advapi32.dll"; /* 7*/ - internal const string LsaCloseDllName = "advapi32.dll"; /* 8*/ - internal const string LookupAccountNameDllName = "advapi32.dll"; /* 9*/ - internal const string GetComputerNameDllName = "kernel32.dll"; /*10*/ - internal const string GetSecurityDescriptorDaclDllName = "advapi32.dll"; /*11*/ - internal const string SetSecurityDescriptorDaclDllName = "advapi32.dll"; /*12*/ - internal const string FormatMessageDllName = "kernel32.dll"; /*13*/ - internal const string GetVersionExDllName = "kernel32.dll"; /*14*/ -#endif } } diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Properties/AssemblyInfo.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Properties/AssemblyInfo.cs deleted file mode 100644 index e4a653cb140..00000000000 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Reflection; -using System.Resources; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("LocalAccounts")] -[assembly: AssemblyDescription("PowerShell cmdlet for local accounts.")] - -[assembly: AssemblyFileVersionAttribute("3.0.0.0")] -[assembly: AssemblyVersion("3.0.0.0")] - -[assembly: AssemblyCulture("")] -[assembly: NeutralResourcesLanguage("en-US")] diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Sam.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Sam.cs index 20f582b6998..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. @@ -1053,7 +1053,7 @@ private void OpenHandles() lsaHandle = IntPtr.Zero; - domainInfo = ClrFacade.PtrToStructure(pInfo); + domainInfo = Marshal.PtrToStructure(pInfo); status = SamApi.SamConnect(ref systemName, out samHandle, SamApi.SAM_SERVER_LOOKUP_DOMAIN, ref oa); ThrowOnFailure(status); @@ -1065,7 +1065,7 @@ private void OpenHandles() // Open the "BuiltIn" domain SecurityIdentifier sid = new SecurityIdentifier("S-1-5-32"); byte[] bSid = new byte[sid.BinaryLength]; - int size = ClrFacade.SizeOf() * bSid.Length; + int size = Marshal.SizeOf() * bSid.Length; pSid = Marshal.AllocHGlobal(size); @@ -1173,7 +1173,7 @@ private static IEnumerable EnumerateUsersInDomain(IntPtr doma { SAM_RID_ENUMERATION sre; - sre = ClrFacade.PtrToStructure(buffer); + sre = Marshal.PtrToStructure(buffer); SamApi.SamFreeMemory(buffer); buffer = IntPtr.Zero; @@ -1252,7 +1252,7 @@ private LocalUser CreateUser(LocalUser userInfo, System.Security.SecureString pa out userHandle, out grantedAccess, out relativeId); - ClrFacade.DestroyStructure(buffer); + Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); buffer = IntPtr.Zero; ThrowOnFailure(status); @@ -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 @@ -1371,9 +1372,10 @@ private void RenameGroup(SecurityIdentifier sid, string newName) { if (buffer != IntPtr.Zero) { - ClrFacade.DestroyStructure(buffer); + Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); } + if (aliasHandle != IntPtr.Zero) status = SamApi.SamCloseHandle(aliasHandle); } @@ -1605,7 +1607,7 @@ private LocalUser MakeLocalUserObject(SamRidEnumeration sre, IntPtr userHandle) USER_INFORMATION_CLASS.UserAllInformation, out buffer); ThrowOnFailure(status); - allInfo = ClrFacade.PtrToStructure(buffer); + allInfo = Marshal.PtrToStructure(buffer); var userSid = RidToSid(sre.domainHandle, sre.RelativeId); LocalUser user = new LocalUser() @@ -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), @@ -1676,7 +1678,7 @@ private void EnableUser(SecurityIdentifier sid, Enabling enable) USER_INFORMATION_CLASS.UserAllInformation, out buffer); ThrowOnFailure(status); - info = ClrFacade.PtrToStructure(buffer); + info = Marshal.PtrToStructure(buffer); status = SamApi.SamFreeMemory(buffer); buffer = IntPtr.Zero; @@ -1700,7 +1702,7 @@ private void EnableUser(SecurityIdentifier sid, Enabling enable) status = SamApi.SamSetInformationUser(userHandle, USER_INFORMATION_CLASS.UserAllInformation, buffer); - ClrFacade.DestroyStructure(buffer); + Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); buffer = IntPtr.Zero; ThrowOnFailure(status); @@ -1760,9 +1762,10 @@ private void RenameUser(SecurityIdentifier sid, string newName) { if (buffer != IntPtr.Zero) { - ClrFacade.DestroyStructure(buffer); + Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); } + if (userHandle != IntPtr.Zero) status = SamApi.SamCloseHandle(userHandle); } @@ -1836,7 +1839,7 @@ private static IEnumerable EnumerateGroupsInDomain(IntPtr dom { SAM_RID_ENUMERATION sre; - sre = ClrFacade.PtrToStructure(buffer); + sre = Marshal.PtrToStructure(buffer); SamApi.SamFreeMemory(buffer); buffer = IntPtr.Zero; @@ -1903,7 +1906,7 @@ private LocalGroup CreateGroup(LocalGroup groupInfo, IntPtr domainHandle) Win32.MAXIMUM_ALLOWED, out aliasHandle, out relativeId); - ClrFacade.DestroyStructure(buffer); + Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); buffer = IntPtr.Zero; ThrowOnFailure(status); @@ -1920,7 +1923,7 @@ private LocalGroup CreateGroup(LocalGroup groupInfo, IntPtr domainHandle) ALIAS_INFORMATION_CLASS.AliasAdminCommentInformation, buffer); - ClrFacade.DestroyStructure(buffer); + Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); buffer = IntPtr.Zero; ThrowOnFailure(status); @@ -1995,9 +1998,10 @@ private void UpdateGroup(LocalGroup group, LocalGroup changed) { if (buffer != IntPtr.Zero) { - ClrFacade.DestroyStructure(buffer); + Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); } + if (aliasHandle != IntPtr.Zero) status = SamApi.SamCloseHandle(aliasHandle); } @@ -2061,7 +2065,7 @@ private LocalGroup MakeLocalGroupObject(SamRidEnumeration sre, IntPtr aliasHandl ALIAS_INFORMATION_CLASS.AliasGeneralInformation, out buffer); ThrowOnFailure(status); - generalInfo = ClrFacade.PtrToStructure(buffer); + generalInfo = Marshal.PtrToStructure(buffer); LocalGroup group = new LocalGroup() { @@ -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; @@ -2247,8 +2254,8 @@ private void SetUserData(IntPtr userHandle, if ((which & SamApi.USER_ALL_USERACCOUNTCONTROL) != 0) info.UserAccountControl = uac; - buffer = Marshal.AllocHGlobal(ClrFacade.SizeOf()); - ClrFacade.StructureToPtr(info, buffer, false); + buffer = Marshal.AllocHGlobal(Marshal.SizeOf()); + Marshal.StructureToPtr(info, buffer, false); status = SamApi.SamSetInformationUser(userHandle, USER_INFORMATION_CLASS.UserAllInformation, @@ -2268,7 +2275,7 @@ private void SetUserData(IntPtr userHandle, { if (buffer != IntPtr.Zero) { - ClrFacade.DestroyStructure(buffer); + Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); } } @@ -2296,7 +2303,7 @@ private UInt32 GetUserAccountControl(IntPtr userHandle) USER_INFORMATION_CLASS.UserLogonInformation, out buffer); ThrowOnFailure(status); - info = ClrFacade.PtrToStructure(buffer); + info = Marshal.PtrToStructure(buffer); status = SamApi.SamFreeMemory(buffer); buffer = IntPtr.Zero; @@ -2330,7 +2337,7 @@ private RawAcl GetSamDacl(IntPtr objectHandle) status = SamApi.SamQuerySecurityObject(objectHandle, Win32.DACL_SECURITY_INFORMATION, out securityObject); ThrowOnFailure(status); - SECURITY_DESCRIPTOR sd = ClrFacade.PtrToStructure(securityObject); + SECURITY_DESCRIPTOR sd = Marshal.PtrToStructure(securityObject); bool daclPresent; bool daclDefaulted; @@ -2348,7 +2355,7 @@ private RawAcl GetSamDacl(IntPtr objectHandle) if (daclPresent) { - ACL acl = ClrFacade.PtrToStructure(dacl); + ACL acl = Marshal.PtrToStructure(dacl); if (acl.AclSize != 0) { @@ -2365,6 +2372,7 @@ private RawAcl GetSamDacl(IntPtr objectHandle) if (IntPtr.Zero != securityObject) status = SamApi.SamFreeMemory(securityObject); } + return rv; } @@ -2389,11 +2397,11 @@ private void SetSamDacl(IntPtr objectHandle, RawAcl rawAcl) // create a new security descriptor var sd = new SECURITY_DESCRIPTOR() { Revision = 1 }; - ipsd = Marshal.AllocHGlobal(ClrFacade.SizeOf()); + ipsd = Marshal.AllocHGlobal(Marshal.SizeOf()); if (rawAcl != null && rawAcl.BinaryLength > 0) { - ClrFacade.StructureToPtr(sd, ipsd, false); + Marshal.StructureToPtr(sd, ipsd, false); // put the DACL into unmanaged memory var length = rawAcl.BinaryLength; @@ -2544,7 +2552,7 @@ private bool IsPasswordExpired(IntPtr userHandle) USER_INFORMATION_CLASS.UserAllInformation, out buffer); ThrowOnFailure(status); - info = ClrFacade.PtrToStructure(buffer); + info = Marshal.PtrToStructure(buffer); status = SamApi.SamFreeMemory(buffer); buffer = IntPtr.Zero; @@ -2601,7 +2609,7 @@ private void SetUserPassword(IntPtr userHandle, info.PasswordExpired = setPwExpire; buffer = Marshal.AllocHGlobal(Marshal.SizeOf(info)); - ClrFacade.StructureToPtr(info, buffer, false); + Marshal.StructureToPtr(info, buffer, false); var status = SamApi.SamSetInformationUser(userHandle, USER_INFORMATION_CLASS.UserSetPasswordInformation, @@ -2612,7 +2620,7 @@ private void SetUserPassword(IntPtr userHandle, { if (buffer != IntPtr.Zero) { - ClrFacade.DestroyStructure(buffer); + Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); } } @@ -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 ad95daa3186..a85b06d4f90 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.csproj +++ b/src/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.csproj @@ -1,39 +1,14 @@ - + + + - 6.0.0 - netcoreapp2.0 - true - true - true + PowerShell's Microsoft.PowerShell.LocalAccounts project Microsoft.PowerShell.LocalAccounts - ../signing/visualstudiopublic.snk - true - false - false - false - false - false - - $(DefineConstants);CORECLR - - - - portable - - - - $(DefineConstants);UNIX - - - - full - - diff --git a/src/Microsoft.PowerShell.LocalAccounts/map.json b/src/Microsoft.PowerShell.LocalAccounts/map.json deleted file mode 100644 index 1f5b5deb2e6..00000000000 --- a/src/Microsoft.PowerShell.LocalAccounts/map.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "monad/src/LocalAccounts/Exceptions.cs": "LocalAccounts/Exceptions.cs", - "monad/src/LocalAccounts/Extensions.cs": "LocalAccounts/Extensions.cs", - "monad/src/LocalAccounts/LocalGroup.cs": "LocalAccounts/LocalGroup.cs", - "monad/src/LocalAccounts/LocalPrincipal.cs": "LocalAccounts/LocalPrincipal.cs", - "monad/src/LocalAccounts/LocalUser.cs": "LocalAccounts/LocalUser.cs", - "monad/src/LocalAccounts/Native.cs": "LocalAccounts/Native.cs", - "monad/src/LocalAccounts/NtStatus.cs": "LocalAccounts/NtStatus.cs", - "monad/src/LocalAccounts/PInvokeDllNames.cs": "LocalAccounts/PInvokeDllNames.cs", - "monad/src/LocalAccounts/Sam.cs": "LocalAccounts/Sam.cs", - "monad/src/LocalAccounts/SamApi.cs": "LocalAccounts/SamApi.cs", - "monad/src/LocalAccounts/StringUtil.cs": "LocalAccounts/StringUtil.cs", - "monad/src/LocalAccounts/Commands/AddLocalGroupMemberCommand.cs": "LocalAccounts/Commands/AddLocalGroupMemberCommand.cs", - "monad/src/LocalAccounts/Commands/DisableLocalUserCommand.cs": "LocalAccounts/Commands/DisableLocalUserCommand.cs", - "monad/src/LocalAccounts/Commands/EnableLocalUserCommand.cs": "LocalAccounts/Commands/EnableLocalUserCommand.cs", - "monad/src/LocalAccounts/Commands/GetLocalGroupCommand.cs": "LocalAccounts/Commands/GetLocalGroupCommand.cs", - "monad/src/LocalAccounts/Commands/GetLocalGroupMemberCommand.cs": "LocalAccounts/Commands/GetLocalGroupMemberCommand.cs", - "monad/src/LocalAccounts/Commands/GetLocalUserCommand.cs": "LocalAccounts/Commands/GetLocalUserCommand.cs", - "monad/src/LocalAccounts/Commands/NewLocalGroupCommand.cs": "LocalAccounts/Commands/NewLocalGroupCommand.cs", - "monad/src/LocalAccounts/Commands/NewLocalUserCommand.cs": "LocalAccounts/Commands/NewLocalUserCommand.cs", - "monad/src/LocalAccounts/Commands/RemoveLocalGroupCommand.cs": "LocalAccounts/Commands/RemoveLocalGroupCommand.cs", - "monad/src/LocalAccounts/Commands/RemoveLocalGroupMemberCommand.cs": "LocalAccounts/Commands/RemoveLocalGroupMemberCommand.cs", - "monad/src/LocalAccounts/Commands/RemoveLocalUserCommand.cs": "LocalAccounts/Commands/RemoveLocalUserCommand.cs", - "monad/src/LocalAccounts/Commands/RenameLocalGroupCommand.cs": "LocalAccounts/Commands/RenameLocalGroupCommand.cs", - "monad/src/LocalAccounts/Commands/RenameLocalUserCommand.cs": "LocalAccounts/Commands/RenameLocalUserCommand.cs", - "monad/src/LocalAccounts/Commands/SetLocalGroupCommand.cs": "LocalAccounts/Commands/SetLocalGroupCommand.cs", - "monad/src/LocalAccounts/Commands/SetLocalUserCommand.cs": "LocalAccounts/Commands/SetLocalUserCommand.cs", - "monad/src/LocalAccounts/Properties/AssemblyInfo.cs": "LocalAccounts/Properties/AssemblyInfo.cs", - "monad/src/LocalAccounts/resources/Microsoft.PowerShell.LocalAccounts.Strings.resx": "resources/Microsoft.PowerShell.LocalAccounts.Strings.resx" -} 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 18eda65c2b7..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 ($toMatch -ne $null -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 ($tokenToChange -ne $null) - { - $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 ($alias -ne $null) - { - $resolvedCommand = $alias.ResolvedCommandName - if ($resolvedCommand -ne $null) - { - $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 ($commandAst -ne $null) - { - $commandName = $commandAst.GetCommandName() - if ($commandName -ne $null) - { - $command = $ExecutionContext.InvokeCommand.GetCommand($commandName, 'All') - if ($command -is [System.Management.Automation.AliasInfo]) - { - $commandName = $command.ResolvedCommandName - } - - if ($commandName -ne $null) - { - 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() | % { - [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 2c9cc44241f..161170355e6 100644 --- a/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj +++ b/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj @@ -1,11 +1,9 @@ - - + + - false PowerShell SDK metapackage - 6.0.0 - netcoreapp2.0 Microsoft.PowerShell.SDK + false @@ -17,17 +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/map.json b/src/Microsoft.PowerShell.ScheduledJob/map.json deleted file mode 100644 index 1837a2668a9..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/map.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "monad/src/ScheduledJob/ScheduledJob.cs" : "ScheduledJob.cs", - "monad/src/ScheduledJob/ScheduledJobDefinition.cs" : "ScheduledJobDefinition.cs", - "monad/src/ScheduledJob/ScheduledJobOptions.cs" : "ScheduledJobOptions.cs", - "monad/src/ScheduledJob/ScheduledJobSourceAdapter.cs" : "ScheduledJobSourceAdapter.cs", - "monad/src/ScheduledJob/ScheduledJobStore.cs" : "ScheduledJobStore.cs", - "monad/src/ScheduledJob/ScheduledJobTrigger.cs" : "ScheduledJobTrigger.cs", - "monad/src/ScheduledJob/ScheduledJobWTS.cs" : "ScheduledJobWTS.cs", - "monad/src/ScheduledJob/commands/NewJobTrigger.cs": "commands/NewJobTrigger.cs", - "monad/src/ScheduledJob/commands/AddJobTrigger.cs": "commands/AddJobTrigger.cs", - "monad/src/ScheduledJob/commands/SchedJobCmdletBase.cs": "commands/SchedJobCmdletBase.cs", - "monad/src/ScheduledJob/commands/RemoveJobTrigger.cs": "commands/RemoveJobTrigger.cs", - "monad/src/ScheduledJob/commands/GetJobTrigger.cs": "commands/GetJobTrigger.cs", - "monad/src/ScheduledJob/commands/SetJobTrigger.cs": "commands/SetJobTrigger.cs", - "monad/src/ScheduledJob/commands/DisableJobTrigger.cs": "commands/DisableJobTrigger.cs", - "monad/src/ScheduledJob/commands/EnableJobTrigger.cs": "commands/EnableJobTrigger.cs", - "monad/src/ScheduledJob/commands/EnableDisableCmdletBase.cs": "commands/EnableDisableCmdletBase.cs", - "monad/src/ScheduledJob/commands/ScheduledJobOptionCmdletBase.cs": "commands/ScheduledJobOptionCmdletBase.cs", - "monad/src/ScheduledJob/commands/NewScheduledJobOption.cs": "commands/NewScheduledJobOption.cs", - "monad/src/ScheduledJob/commands/SetScheduledJobOption.cs": "commands/SetScheduledJobOption.cs", - "monad/src/ScheduledJob/commands/GetScheduledJobOption.cs": "commands/GetScheduledJobOption.cs", - "monad/src/ScheduledJob/commands/RegisterJobDefinition.cs": "commands/RegisterJobDefinition.cs", - "monad/src/ScheduledJob/commands/SetJobDefinition.cs": "commands/SetJobDefinition.cs", - "monad/src/ScheduledJob/commands/GetJobDefinition.cs": "commands/GetJobDefinition.cs", - "monad/src/ScheduledJob/commands/UnregisterJobDefinition.cs": "commands/UnregisterJobDefinition.cs", - "monad/src/ScheduledJob/commands/DisableJobDefinitionBase.cs": "commands/DisableJobDefinitionBase.cs", - "monad/src/ScheduledJob/commands/DisableJobDefinition.cs": "commands/DisableJobDefinition.cs", - "monad/src/ScheduledJob/commands/EnableJobDefinition.cs": "commands/EnableJobDefinition.cs", - "monad/src/ScheduledJob/resources/ScheduledJobErrorStrings.resx": "resources/ScheduledJobErrorStrings.resx" -} \ No newline at end of file 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.Activities/AssemblyInfo.cs b/src/Microsoft.PowerShell.Security.Activities/AssemblyInfo.cs deleted file mode 100644 index 55b23b3691f..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/AssemblyInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Reflection; -using System.Security.Permissions; -using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Diagnostics.CodeAnalysis; - - -[assembly: AssemblyVersion("3.0.0.0")] -[assembly: AssemblyFileVersionAttribute("3.0.0.0")] -[assembly: System.Runtime.InteropServices.ComVisible(false)] - -[assembly: AssemblyConfiguration("")] -[assembly: ReliabilityContractAttribute(Consistency.MayCorruptAppDomain, Cer.MayFail)] -[assembly: AssemblyTitle("Microsoft.PowerShell.Security.Activities")] -[assembly: AssemblyDescription("Microsoft.PowerShell.Security.Activities")] - -[module: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant")] - diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/ConvertFromSecureStringActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/ConvertFromSecureStringActivity.cs deleted file mode 100644 index 0acec77e982..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/ConvertFromSecureStringActivity.cs +++ /dev/null @@ -1,95 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\ConvertFrom-SecureString command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class ConvertFromSecureString : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public ConvertFromSecureString() - { - this.DisplayName = "ConvertFrom-SecureString"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\ConvertFrom-SecureString"; } } - - // Arguments - - /// - /// Provides access to the SecureString parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SecureString { get; set; } - - /// - /// Provides access to the SecureKey parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SecureKey { get; set; } - - /// - /// Provides access to the Key parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Key { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (SecureString.Expression != null) - { - targetCommand.AddParameter("SecureString", SecureString.Get(context)); - } - - if (SecureKey.Expression != null) - { - targetCommand.AddParameter("SecureKey", SecureKey.Get(context)); - } - - if (Key.Expression != null) - { - targetCommand.AddParameter("Key", Key.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/ConvertToSecureStringActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/ConvertToSecureStringActivity.cs deleted file mode 100644 index 3c1899a65b4..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/ConvertToSecureStringActivity.cs +++ /dev/null @@ -1,119 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\ConvertTo-SecureString command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class ConvertToSecureString : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public ConvertToSecureString() - { - this.DisplayName = "ConvertTo-SecureString"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\ConvertTo-SecureString"; } } - - // Arguments - - /// - /// Provides access to the String parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument String { get; set; } - - /// - /// Provides access to the AsPlainText parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument AsPlainText { get; set; } - - /// - /// Provides access to the Force parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Force { get; set; } - - /// - /// Provides access to the SecureKey parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SecureKey { get; set; } - - /// - /// Provides access to the Key parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Key { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (String.Expression != null) - { - targetCommand.AddParameter("String", String.Get(context)); - } - - if (AsPlainText.Expression != null) - { - targetCommand.AddParameter("AsPlainText", AsPlainText.Get(context)); - } - - if (Force.Expression != null) - { - targetCommand.AddParameter("Force", Force.Get(context)); - } - - if (SecureKey.Expression != null) - { - targetCommand.AddParameter("SecureKey", SecureKey.Get(context)); - } - - if (Key.Expression != null) - { - targetCommand.AddParameter("Key", Key.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/GetAclActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/GetAclActivity.cs deleted file mode 100644 index 10b363f6650..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/GetAclActivity.cs +++ /dev/null @@ -1,155 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\Get-Acl command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetAcl : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetAcl() - { - this.DisplayName = "Get-Acl"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\Get-Acl"; } } - - // Arguments - - /// - /// Provides access to the Path parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Path { get; set; } - - /// - /// Provides access to the InputObject parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument InputObject { get; set; } - - /// - /// Provides access to the LiteralPath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument LiteralPath { get; set; } - - /// - /// Provides access to the Audit parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Audit { get; set; } - - /// - /// Provides access to the AllCentralAccessPolicies parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument AllCentralAccessPolicies { get; set; } - - /// - /// Provides access to the Filter parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Filter { get; set; } - - /// - /// Provides access to the Include parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Include { get; set; } - - /// - /// Provides access to the Exclude parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Exclude { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (Path.Expression != null) - { - targetCommand.AddParameter("Path", Path.Get(context)); - } - - if (InputObject.Expression != null) - { - targetCommand.AddParameter("InputObject", InputObject.Get(context)); - } - - if (LiteralPath.Expression != null) - { - targetCommand.AddParameter("LiteralPath", LiteralPath.Get(context)); - } - - if (Audit.Expression != null) - { - targetCommand.AddParameter("Audit", Audit.Get(context)); - } - - if (AllCentralAccessPolicies.Expression != null) - { - targetCommand.AddParameter("AllCentralAccessPolicies", AllCentralAccessPolicies.Get(context)); - } - - if (Filter.Expression != null) - { - targetCommand.AddParameter("Filter", Filter.Get(context)); - } - - if (Include.Expression != null) - { - targetCommand.AddParameter("Include", Include.Get(context)); - } - - if (Exclude.Expression != null) - { - targetCommand.AddParameter("Exclude", Exclude.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/GetAuthenticodeSignatureActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/GetAuthenticodeSignatureActivity.cs deleted file mode 100644 index 62913bb5cbb..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/GetAuthenticodeSignatureActivity.cs +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\Get-AuthenticodeSignature command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetAuthenticodeSignature : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetAuthenticodeSignature() - { - this.DisplayName = "Get-AuthenticodeSignature"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\Get-AuthenticodeSignature"; } } - - // Arguments - - /// - /// Provides access to the FilePath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument FilePath { get; set; } - - /// - /// Provides access to the LiteralPath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument LiteralPath { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (FilePath.Expression != null) - { - targetCommand.AddParameter("FilePath", FilePath.Get(context)); - } - - if (LiteralPath.Expression != null) - { - targetCommand.AddParameter("LiteralPath", LiteralPath.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/GetCmsMessageActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/GetCmsMessageActivity.cs deleted file mode 100644 index 029cb233521..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/GetCmsMessageActivity.cs +++ /dev/null @@ -1,95 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\Get-CmsMessage command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetCmsMessage : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetCmsMessage() - { - this.DisplayName = "Get-CmsMessage"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\Get-CmsMessage"; } } - - // Arguments - - /// - /// Provides access to the Content parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Content { get; set; } - - /// - /// Provides access to the Path parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Path { get; set; } - - /// - /// Provides access to the LiteralPath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument LiteralPath { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (Content.Expression != null) - { - targetCommand.AddParameter("Content", Content.Get(context)); - } - - if (Path.Expression != null) - { - targetCommand.AddParameter("Path", Path.Get(context)); - } - - if (LiteralPath.Expression != null) - { - targetCommand.AddParameter("LiteralPath", LiteralPath.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/GetExecutionPolicyActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/GetExecutionPolicyActivity.cs deleted file mode 100644 index 200b01278c3..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/GetExecutionPolicyActivity.cs +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\Get-ExecutionPolicy command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetExecutionPolicy : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetExecutionPolicy() - { - this.DisplayName = "Get-ExecutionPolicy"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\Get-ExecutionPolicy"; } } - - // Arguments - - /// - /// Provides access to the Scope parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Scope { get; set; } - - /// - /// Provides access to the List parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument List { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (Scope.Expression != null) - { - targetCommand.AddParameter("Scope", Scope.Get(context)); - } - - if (List.Expression != null) - { - targetCommand.AddParameter("List", List.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/GetPfxCertificateActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/GetPfxCertificateActivity.cs deleted file mode 100644 index 3da864131fd..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/GetPfxCertificateActivity.cs +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\Get-PfxCertificate command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetPfxCertificate : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetPfxCertificate() - { - this.DisplayName = "Get-PfxCertificate"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\Get-PfxCertificate"; } } - - // Arguments - - /// - /// Provides access to the FilePath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument FilePath { get; set; } - - /// - /// Provides access to the LiteralPath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument LiteralPath { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (FilePath.Expression != null) - { - targetCommand.AddParameter("FilePath", FilePath.Get(context)); - } - - if (LiteralPath.Expression != null) - { - targetCommand.AddParameter("LiteralPath", LiteralPath.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/ProtectCmsMessageActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/ProtectCmsMessageActivity.cs deleted file mode 100644 index f2e2c22da46..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/ProtectCmsMessageActivity.cs +++ /dev/null @@ -1,119 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\Protect-CmsMessage command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class ProtectCmsMessage : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public ProtectCmsMessage() - { - this.DisplayName = "Protect-CmsMessage"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\Protect-CmsMessage"; } } - - // Arguments - - /// - /// Provides access to the To parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument To { get; set; } - - /// - /// Provides access to the Content parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Content { get; set; } - - /// - /// Provides access to the Path parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Path { get; set; } - - /// - /// Provides access to the LiteralPath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument LiteralPath { get; set; } - - /// - /// Provides access to the OutFile parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OutFile { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (To.Expression != null) - { - targetCommand.AddParameter("To", To.Get(context)); - } - - if (Content.Expression != null) - { - targetCommand.AddParameter("Content", Content.Get(context)); - } - - if (Path.Expression != null) - { - targetCommand.AddParameter("Path", Path.Get(context)); - } - - if (LiteralPath.Expression != null) - { - targetCommand.AddParameter("LiteralPath", LiteralPath.Get(context)); - } - - if (OutFile.Expression != null) - { - targetCommand.AddParameter("OutFile", OutFile.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/SetAclActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/SetAclActivity.cs deleted file mode 100644 index fd065686bd5..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/SetAclActivity.cs +++ /dev/null @@ -1,191 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\Set-Acl command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class SetAcl : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public SetAcl() - { - this.DisplayName = "Set-Acl"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\Set-Acl"; } } - - // Arguments - - /// - /// Provides access to the Path parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Path { get; set; } - - /// - /// Provides access to the InputObject parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument InputObject { get; set; } - - /// - /// Provides access to the LiteralPath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument LiteralPath { get; set; } - - /// - /// Provides access to the AclObject parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument AclObject { get; set; } - - /// - /// Provides access to the SecurityDescriptor parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SecurityDescriptor { get; set; } - - /// - /// Provides access to the CentralAccessPolicy parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CentralAccessPolicy { get; set; } - - /// - /// Provides access to the ClearCentralAccessPolicy parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ClearCentralAccessPolicy { get; set; } - - /// - /// Provides access to the Passthru parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Passthru { get; set; } - - /// - /// Provides access to the Filter parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Filter { get; set; } - - /// - /// Provides access to the Include parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Include { get; set; } - - /// - /// Provides access to the Exclude parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Exclude { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (Path.Expression != null) - { - targetCommand.AddParameter("Path", Path.Get(context)); - } - - if (InputObject.Expression != null) - { - targetCommand.AddParameter("InputObject", InputObject.Get(context)); - } - - if (LiteralPath.Expression != null) - { - targetCommand.AddParameter("LiteralPath", LiteralPath.Get(context)); - } - - if (AclObject.Expression != null) - { - targetCommand.AddParameter("AclObject", AclObject.Get(context)); - } - - if (SecurityDescriptor.Expression != null) - { - targetCommand.AddParameter("SecurityDescriptor", SecurityDescriptor.Get(context)); - } - - if (CentralAccessPolicy.Expression != null) - { - targetCommand.AddParameter("CentralAccessPolicy", CentralAccessPolicy.Get(context)); - } - - if (ClearCentralAccessPolicy.Expression != null) - { - targetCommand.AddParameter("ClearCentralAccessPolicy", ClearCentralAccessPolicy.Get(context)); - } - - if (Passthru.Expression != null) - { - targetCommand.AddParameter("Passthru", Passthru.Get(context)); - } - - if (Filter.Expression != null) - { - targetCommand.AddParameter("Filter", Filter.Get(context)); - } - - if (Include.Expression != null) - { - targetCommand.AddParameter("Include", Include.Get(context)); - } - - if (Exclude.Expression != null) - { - targetCommand.AddParameter("Exclude", Exclude.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/SetAuthenticodeSignatureActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/SetAuthenticodeSignatureActivity.cs deleted file mode 100644 index 1914c08c231..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/SetAuthenticodeSignatureActivity.cs +++ /dev/null @@ -1,143 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\Set-AuthenticodeSignature command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class SetAuthenticodeSignature : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public SetAuthenticodeSignature() - { - this.DisplayName = "Set-AuthenticodeSignature"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\Set-AuthenticodeSignature"; } } - - // Arguments - - /// - /// Provides access to the Certificate parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Certificate { get; set; } - - /// - /// Provides access to the IncludeChain parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument IncludeChain { get; set; } - - /// - /// Provides access to the TimestampServer parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument TimestampServer { get; set; } - - /// - /// Provides access to the HashAlgorithm parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument HashAlgorithm { get; set; } - - /// - /// Provides access to the Force parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Force { get; set; } - - /// - /// Provides access to the FilePath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument FilePath { get; set; } - - /// - /// Provides access to the LiteralPath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument LiteralPath { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (Certificate.Expression != null) - { - targetCommand.AddParameter("Certificate", Certificate.Get(context)); - } - - if (IncludeChain.Expression != null) - { - targetCommand.AddParameter("IncludeChain", IncludeChain.Get(context)); - } - - if (TimestampServer.Expression != null) - { - targetCommand.AddParameter("TimestampServer", TimestampServer.Get(context)); - } - - if (HashAlgorithm.Expression != null) - { - targetCommand.AddParameter("HashAlgorithm", HashAlgorithm.Get(context)); - } - - if (Force.Expression != null) - { - targetCommand.AddParameter("Force", Force.Get(context)); - } - - if (FilePath.Expression != null) - { - targetCommand.AddParameter("FilePath", FilePath.Get(context)); - } - - if (LiteralPath.Expression != null) - { - targetCommand.AddParameter("LiteralPath", LiteralPath.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/SetExecutionPolicyActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/SetExecutionPolicyActivity.cs deleted file mode 100644 index e6fe83b6ef7..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/SetExecutionPolicyActivity.cs +++ /dev/null @@ -1,95 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\Set-ExecutionPolicy command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class SetExecutionPolicy : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public SetExecutionPolicy() - { - this.DisplayName = "Set-ExecutionPolicy"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\Set-ExecutionPolicy"; } } - - // Arguments - - /// - /// Provides access to the ExecutionPolicy parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ExecutionPolicy { get; set; } - - /// - /// Provides access to the Scope parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Scope { get; set; } - - /// - /// Provides access to the Force parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Force { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (ExecutionPolicy.Expression != null) - { - targetCommand.AddParameter("ExecutionPolicy", ExecutionPolicy.Get(context)); - } - - if (Scope.Expression != null) - { - targetCommand.AddParameter("Scope", Scope.Get(context)); - } - - if (Force.Expression != null) - { - targetCommand.AddParameter("Force", Force.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/Generated/UnprotectCmsMessageActivity.cs b/src/Microsoft.PowerShell.Security.Activities/Generated/UnprotectCmsMessageActivity.cs deleted file mode 100644 index 01b12c14ebc..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/Generated/UnprotectCmsMessageActivity.cs +++ /dev/null @@ -1,131 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.PowerShell.Security.Activities -{ - /// - /// Activity to invoke the Microsoft.PowerShell.Security\Unprotect-CmsMessage command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class UnprotectCmsMessage : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public UnprotectCmsMessage() - { - this.DisplayName = "Unprotect-CmsMessage"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.PowerShell.Security\\Unprotect-CmsMessage"; } } - - // Arguments - - /// - /// Provides access to the Content parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Content { get; set; } - - /// - /// Provides access to the EventLogRecord parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument EventLogRecord { get; set; } - - /// - /// Provides access to the Path parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Path { get; set; } - - /// - /// Provides access to the LiteralPath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument LiteralPath { get; set; } - - /// - /// Provides access to the IncludeContext parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument IncludeContext { get; set; } - - /// - /// Provides access to the To parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument To { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if (Content.Expression != null) - { - targetCommand.AddParameter("Content", Content.Get(context)); - } - - if (EventLogRecord.Expression != null) - { - targetCommand.AddParameter("EventLogRecord", EventLogRecord.Get(context)); - } - - if (Path.Expression != null) - { - targetCommand.AddParameter("Path", Path.Get(context)); - } - - if (LiteralPath.Expression != null) - { - targetCommand.AddParameter("LiteralPath", LiteralPath.Get(context)); - } - - if (IncludeContext.Expression != null) - { - targetCommand.AddParameter("IncludeContext", IncludeContext.Get(context)); - } - - if (To.Expression != null) - { - targetCommand.AddParameter("To", To.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.PowerShell.Security.Activities/map.json b/src/Microsoft.PowerShell.Security.Activities/map.json deleted file mode 100644 index a9db8cf6d33..00000000000 --- a/src/Microsoft.PowerShell.Security.Activities/map.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/ConvertFromSecureStringActivity.cs": "Generated/ConvertFromSecureStringActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/ConvertToSecureStringActivity.cs": "Generated/ConvertToSecureStringActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/GetAclActivity.cs": "Generated/GetAclActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/GetAuthenticodeSignatureActivity.cs": "Generated/GetAuthenticodeSignatureActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/GetExecutionPolicyActivity.cs": "Generated/GetExecutionPolicyActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/GetPfxCertificateActivity.cs": "Generated/GetPfxCertificateActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/SetAclActivity.cs": "Generated/SetAclActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/SetAuthenticodeSignatureActivity.cs": "Generated/SetAuthenticodeSignatureActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/SetExecutionPolicyActivity.cs": "Generated/SetExecutionPolicyActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/UnprotectCmsMessageActivity.cs": "Generated/UnprotectCmsMessageActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/GetCmsMessageActivity.cs": "Generated/GetCmsMessageActivity.cs", - "monad/src/Activities/Generated/Microsoft_PowerShell_Security_Activities/ProtectCmsMessageActivity.cs": "Generated/ProtectCmsMessageActivity.cs" -} diff --git a/src/Microsoft.PowerShell.Security/AssemblyInfo.cs b/src/Microsoft.PowerShell.Security/AssemblyInfo.cs deleted file mode 100644 index dd4e8f28264..00000000000 --- a/src/Microsoft.PowerShell.Security/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.Security/Microsoft.PowerShell.Security.csproj b/src/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.csproj index 9c6eed2a6cf..a6dadabfed2 100644 --- a/src/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.csproj +++ b/src/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.csproj @@ -1,46 +1,20 @@ - - + + - 6.0.0 - netcoreapp2.0 - $(NoWarn);CS1570 - true - true - true - true + PowerShell's Microsoft.PowerShell.Security project + $(NoWarn);CS1570;CA1416 Microsoft.PowerShell.Security - ../signing/visualstudiopublic.snk - true - false - false - false - - $(DefineConstants);CORECLR - - - + - - portable - - - - $(DefineConstants);UNIX - - - - full - - diff --git a/src/Microsoft.PowerShell.Security/map.json b/src/Microsoft.PowerShell.Security/map.json deleted file mode 100644 index 5a7c57668f9..00000000000 --- a/src/Microsoft.PowerShell.Security/map.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "monad/src/security/CredentialCommands.cs": "security/CredentialCommands.cs", - "monad/src/security/SignatureCommands.cs": "security/SignatureCommands.cs", - "monad/src/security/resources/UtilsStrings.resx": "resources/UtilsStrings.resx", - "monad/src/security/resources/CertificateProviderStrings.resx": "resources/CertificateProviderStrings.resx", - "monad/src/security/resources/ExecutionPolicyCommands.resx": "resources/ExecutionPolicyCommands.resx", - "monad/src/security/resources/CmsCommands.resx": "resources/CmsCommands.resx", - "monad/src/security/CertificateProvider.cs": "security/CertificateProvider.cs", - "monad/src/security/resources/CertificateCommands.resx": "resources/CertificateCommands.resx", - "monad/src/security/certificateproviderexceptions.cs": "security/certificateproviderexceptions.cs", - "monad/src/security/SecureStringCommands.cs": "security/SecureStringCommands.cs", - "monad/src/security/resources/SecureStringCommands.resx": "resources/SecureStringCommands.resx", - "monad/src/security/resources/SecurityMshSnapinResources.resx": "resources/SecurityMshSnapinResources.resx", - "monad/src/security/Utils.cs": "security/Utils.cs", - "monad/src/security/CertificateCommands.cs": "security/CertificateCommands.cs", - "monad/src/singleshell/installer/MshSecurityMshSnapin.cs": "singleshell/installer/MshSecurityMshSnapin.cs", - "monad/src/security/ExecutionPolicyCommands.cs": "security/ExecutionPolicyCommands.cs", - "monad/src/security/CmsCommands.cs": "security/CmsCommands.cs", - "monad/src/security/AclCommands.cs": "security/AclCommands.cs", - "monad/src/security/CatalogCommands.cs": "security/CatalogCommands.cs", - "monad/src/security/resources/SignatureCommands.resx": "resources/SignatureCommands.resx" -} 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 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 have an empty BaseDirectory for importing localized data. Please specify a valid BaseDirectory and run the command again. - BaseDirectory should not be localized. This is a parameter. - - diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/AssemblyInfo.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/AssemblyInfo.cs deleted file mode 100644 index 944ee1264ec..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/AssemblyInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using System.Security.Permissions; -using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Diagnostics.CodeAnalysis; - -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyInformationalVersionAttribute (@"10.0.10011.16384")] -[assembly: ReliabilityContractAttribute(Consistency.MayCorruptAppDomain, Cer.MayFail)] -[assembly: AssemblyTitle("Microsoft.PowerShell.Workflow.ServiceCore")] -[assembly: AssemblyDescription("Microsoft.PowerShell.Workflow.ServiceCore")] -[assembly:AssemblyFileVersionAttribute("3.0.0.0")] -[assembly:AssemblyVersion("3.0.0.0")] - -[module: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant")] -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="Resources.resources", MessageId="InlineScript")] -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="Resources.resources", MessageId="foreach")] -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="Resources.resources", MessageId="suspendable")] - -[assembly:InternalsVisibleTo("M3PTestAutomation,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly:InternalsVisibleTo("M3PTestCommon,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly:InternalsVisibleTo("VSTS.M3PUnitTest,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly:InternalsVisibleTo("VSTS.ActivityTests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly:InternalsVisibleTo("ActivityTests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100eb57e8072e5fa11a7783bde0003e38bec8272f81d965c161b33b2f45404c9c9177a682ee7049400723fc456ea69b638514943375f7c594027477f9d17e8c3535e6491f382308fbf1c7795413456ac88c0c9881ef49ab9789919f5eeabc9149ccb800bbef8f04103ea24b9be1fb923006b0a272c0d8d928bfdbe947a0f3df4a99")] -[assembly:InternalsVisibleTo("WorkflowCoreTest,PublicKey=0024000004800000940000000602000000240000525341310004000001000100eb57e8072e5fa11a7783bde0003e38bec8272f81d965c161b33b2f45404c9c9177a682ee7049400723fc456ea69b638514943375f7c594027477f9d17e8c3535e6491f382308fbf1c7795413456ac88c0c9881ef49ab9789919f5eeabc9149ccb800bbef8f04103ea24b9be1fb923006b0a272c0d8d928bfdbe947a0f3df4a99")] -[assembly:InternalsVisibleTo("WorkflowJobsTest,PublicKey=0024000004800000940000000602000000240000525341310004000001000100eb57e8072e5fa11a7783bde0003e38bec8272f81d965c161b33b2f45404c9c9177a682ee7049400723fc456ea69b638514943375f7c594027477f9d17e8c3535e6491f382308fbf1c7795413456ac88c0c9881ef49ab9789919f5eeabc9149ccb800bbef8f04103ea24b9be1fb923006b0a272c0d8d928bfdbe947a0f3df4a99")] - diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityCategories.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityCategories.cs deleted file mode 100644 index 5f438fae44b..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityCategories.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Activities; -using System.ComponentModel; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Collections.ObjectModel; -using System.ComponentModel.Design; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Category attribute for "Activity-Specific Parameters" - /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] - public sealed class ParameterSpecificCategoryAttribute : CategoryAttribute - { - /// - /// Creates the attribute. - /// - public ParameterSpecificCategoryAttribute() : base("") { } - - /// - /// Gets a localized version of the attribute description. - /// - /// Not used. - /// A localized version of the attribute description - protected override string GetLocalizedString(string value) - { - return Resources.ActivityParameterGroup; - } - } - - /// - /// Category attribute for "Input and Output" - /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] - public sealed class InputAndOutputCategoryAttribute : CategoryAttribute - { - /// - /// Creates the attribute. - /// - public InputAndOutputCategoryAttribute() : base("") { } - - /// - /// Gets a localized version of the attribute description. - /// - /// Not used. - /// A localized version of the attribute description - protected override string GetLocalizedString(string value) - { - return Resources.InputAndOutputGroup; - } - } - - /// - /// Category attribute for "Behavior" - /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] - public sealed class BehaviorCategoryAttribute : CategoryAttribute - { - /// - /// Creates the attribute. - /// - public BehaviorCategoryAttribute() : base("") { } - - /// - /// Gets a localized version of the attribute description. - /// - /// Not used. - /// A localized version of the attribute description - protected override string GetLocalizedString(string value) - { - return CategoryAttribute.Behavior.Category; - } - } - - /// - /// Category attribute for "Connectivity" - /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] - public sealed class ConnectivityCategoryAttribute : CategoryAttribute - { - /// - /// Creates the attribute. - /// - public ConnectivityCategoryAttribute() : base("") { } - - /// - /// Gets a localized version of the attribute description. - /// - /// Not used. - /// A localized version of the attribute description - protected override string GetLocalizedString(string value) - { - return Resources.ConnectivityGroup; - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostManager.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostManager.cs deleted file mode 100644 index 34efb708c37..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostManager.cs +++ /dev/null @@ -1,673 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Management.Automation.Runspaces; -using System.Threading; -using System.Management.Automation; -using Microsoft.PowerShell.Activities; -using System.Management.Automation.Tracing; -using System.Activities; -using System.Management.Automation.PerformanceData; -using System.Management.Automation.Remoting; - -namespace Microsoft.PowerShell.Workflow -{ - internal class ConnectionAsyncResult : IAsyncResult - { - #region Private Members - - private readonly object _state; - private readonly AsyncCallback _callback; - private bool _isCompleted; - private ManualResetEvent _completedWaitHandle; - private readonly object _syncObject = new object(); - private Exception _exception; - private readonly Guid _ownerId; - #endregion Private Members - - #region Internal - - internal ConnectionAsyncResult(object state, AsyncCallback callback, Guid ownerId) - { - _state = state; - _ownerId = ownerId; - _callback = callback; - } - - internal Object State - { - get { return _state; } - } - - internal AsyncCallback Callback - { - get { return _callback; } - } - - internal ActivityInvoker Invoker { get; set; } - - /// - /// this method is not thread safe - /// - internal void SetAsCompleted(Exception exception) - { - if (_isCompleted) return; - - lock (_syncObject) - { - if (_isCompleted) return; - - _isCompleted = true; - _exception = exception; - if (_completedWaitHandle != null) _completedWaitHandle.Set(); - } - - // invoke callback if available - if (null != _callback) - _callback(this); - } - - internal Guid OwnerId - { - get { return _ownerId; } - } - - internal void EndInvoke() - { - AsyncWaitHandle.WaitOne(); - AsyncWaitHandle.Close(); - _completedWaitHandle = null; // allow early GC - - if (null != _exception) throw _exception; - } - - #endregion Internal - - #region Overrides - - /// - /// Whether the operation represented by this method is - /// completed - /// - public bool IsCompleted - { - get { return _isCompleted; } - } - - /// - /// Wait Handle for the operation - /// - public WaitHandle AsyncWaitHandle - { - get - { - if(null == _completedWaitHandle) - { - lock(_syncObject) - { - if (null == _completedWaitHandle) - { - _completedWaitHandle = new ManualResetEvent(_isCompleted); - } - } - } - - return _completedWaitHandle; - } - } - - /// - /// Optional user specified state - /// - public object AsyncState - { - get { return _state; } - } - - /// - /// Whether this operation completed synchronously - /// - public bool CompletedSynchronously - { - get { return _completedSynchronously; } - set { _completedSynchronously = value; } - } - - private bool _completedSynchronously; - - #endregion Overrides - } - - /// - /// Activity Host Manager which will spawn a set of activity - /// host processes until all of them are used after which - /// it will start queueing requests - /// - /// Whether this class needs to remain public should be - /// evaluated - internal sealed class PSOutOfProcessActivityController : PSActivityHostController - { - #region Private Members - - /// - /// Stack of available host processes - /// - private readonly Collection _hostProcesses = new Collection(); - - /// - /// Queue of requests - /// - private readonly ConcurrentQueue _requests = - new ConcurrentQueue(); - private int _isServicing; - private const int Servicing = 1; - private const int NotServicing = 0; - private const int MinActivityHosts = 1; - private int _busyHosts; - private readonly Tracer _structuredTracer = new Tracer(); - private readonly PSWorkflowConfigurationProvider _configuration; - private static readonly PSPerfCountersMgr PerfCountersMgr = PSPerfCountersMgr.Instance; - - - /// - /// List of requests that need to be invoked again due to process crash - /// - private readonly ConcurrentQueue _failedRequests = new ConcurrentQueue(); - - #endregion Private Members - - #region Internal Methods - - internal void RunPowerShellInActivityHost(System.Management.Automation.PowerShell powershell, PSDataCollection input, - PSDataCollection output, PSActivityEnvironment policy, ConnectionAsyncResult asyncResult) - { - ActivityInvoker invoker = - new ActivityInvoker - { - Input = input, - Output = output, - Policy = policy, - PowerShell = powershell, - AsyncResult = asyncResult - }; - - _requests.Enqueue(invoker); - CheckAndStartServicingThread(); - } - - /// - /// This is a helper method which should only be called - /// from test code to reset stuff - /// - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is called from the tests")] - internal void Reset() - { - foreach (ActivityHostProcess process in _hostProcesses) - { - process.Dispose(); - } - _hostProcesses.Clear(); - - InitializeActivityHostProcesses(); - } - - #endregion Internal Methods - - #region Constructors - - internal PSOutOfProcessActivityController(PSWorkflowRuntime runtime) - : base(runtime) - { - if (runtime == null) - throw new ArgumentNullException("runtime"); - - Debug.Assert(runtime.Configuration != null, "For now only expecting PSWorkflowConfigurationProvider"); - - this._configuration = runtime.Configuration; - InitializeActivityHostProcesses(); - } - - #endregion Constructors - - #region Private Methods - - private void InitializeActivityHostProcesses() - { - // create the minimum number of hosts - for (int i = 0; i < MinActivityHosts; i++) - { - ActivityHostProcess process = CreateNewActivityHostProcess(); - _hostProcesses.Add(process); - } - } - - private ActivityHostProcess CreateNewActivityHostProcess() - { - ActivityHostProcess process = new ActivityHostProcess(_configuration.ActivityProcessIdleTimeoutSec, _configuration.LanguageMode); - process.ProcessCrashed += ProcessCrashed; - process.Finished += ProcessFinished; - process.OnProcessIdle += ProcessIdle; - - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrProcessesPoolSize); - return process; - } - - private void ProcessCrashed(object sender, ActivityHostCrashedEventArgs e) - { - // the process crashed, we need to not use it anymore - ActivityHostProcess process = sender as ActivityHostProcess; - Debug.Assert(process != null, "ActivityHostProcess did not raise event correctly"); - Debug.Assert(process.Busy, "When ProcessCrashed is raised busy should not be reset"); - process.MarkForRemoval = true; - - if (e.FailureOnSetup) - { - // the request needs to be processed again - _failedRequests.Enqueue(e.Invoker); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrFailedRequestsPerSec); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrFailedRequestsQueueLength); - } - - // Below call is added to fix the race condition: - // When OOP host process is crashed, ProcessCrashed() method sets the process as MarkForRemoval, context switch happened at this time - // and another process has finished and started the servicing thread, which checks the above process as MarkForRemoval and disposes it. - // ProcessFinished event handler is unregistered from the process and ActivityHostProcess.HandleTransportError will not be able to raise - // process finished event, resulting inconsistent _busyHost count. - // - DecrementHostCountAndStartThreads(); - } - - private void ProcessFinished(object sender, EventArgs e) - { - DecrementHostCountAndStartThreads(); - } - - private void DecrementHostCountAndStartThreads() - { - Interlocked.Decrement(ref _busyHosts); - - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrBusyProcessesCount, - -1); - - CheckAndStartServicingThread(); - } - - private void ProcessIdle(object sender, EventArgs e) - { - // Only service thread should access the hostProcess collection - // Start servicing thread if it is not running already - // - CheckAndStartServicingThread(); - } - - /// - /// Handler method which runs a specific command in the specified process - /// - /// - private static void RunPowerShellInActivityHostWorker(object state) - { - // Tuple object with HostProcess and ActivityInvoker is added to ThreadPool.QueueUserWorkItem as part of RunInProcess() - // Back reference on ActivityHostProcess in ActivityInvoker is removed - // - Tuple tupleProcessAndInvoker = state as Tuple; - tupleProcessAndInvoker.Item1.PrepareAndRun(tupleProcessAndInvoker.Item2); - } - - /// - /// Checks to see if a thread is servicing requests. If not - /// starts one - /// - private void CheckAndStartServicingThread() - { - if (Interlocked.CompareExchange(ref _isServicing, Servicing, NotServicing) == NotServicing) - { - ThreadPool.QueueUserWorkItem(ServiceRequests); - } - } - - /// - /// Method which performs the actual servicing of requests - /// - /// - private void ServiceRequests(object state) - { - bool isFailedRequest = false; - while (Interlocked.CompareExchange(ref _busyHosts, _configuration.MaxActivityProcesses, _configuration.MaxActivityProcesses) < _configuration.MaxActivityProcesses) - { - // if there are any processes marked for removal - // remove them - List toRemove = _hostProcesses.Where(process => process.MarkForRemoval).ToList(); - - foreach(var process in toRemove) - { - SafelyDisposeProcess(process); - } - - ActivityInvoker invoker; - // first service previously failed request - // and then a queued request - if (_failedRequests.Count > 0) - { - _failedRequests.TryDequeue(out invoker); - isFailedRequest = true; - } - else - { - _requests.TryDequeue(out invoker); - isFailedRequest = false; - } - - if (invoker == null) break; - - if (invoker.IsCancelled) continue; - - if (isFailedRequest) - { - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrFailedRequestsQueueLength, - -1); - } - else - { - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrPendingRequestsQueueLength, - -1); - } - - bool processed = false; - foreach (ActivityHostProcess process in _hostProcesses.Where(process => !process.Busy)) - { - // we have found the first free process available use it - processed = true; - RunInProcess(invoker, process); - break; - } - - // if there weren't enough processes, then we create one more - if (processed) continue; - - ActivityHostProcess hostProcess = CreateNewActivityHostProcess(); - _hostProcesses.Add(hostProcess); - RunInProcess(invoker, hostProcess); - } - - // we are all done, set servicing to false - Interlocked.CompareExchange(ref _isServicing, NotServicing, Servicing); - - // Try to start the servicing thread again if there are any pending requests and activity host processes are available. - // This is required to fix the race condition between CheckAndStartServicingThread() and ServiceRequests() methods. - // - if((_failedRequests.Count > 0 || _requests.Count > 0) && - Interlocked.CompareExchange(ref _busyHosts, _configuration.MaxActivityProcesses, _configuration.MaxActivityProcesses) < _configuration.MaxActivityProcesses) - { - CheckAndStartServicingThread(); - } - } - - - /// - /// Unregisters all wait handles and disposes a process - /// - /// - private void SafelyDisposeProcess(ActivityHostProcess process) - { - process.Finished -= ProcessFinished; - process.ProcessCrashed -= ProcessCrashed; - process.OnProcessIdle -= ProcessIdle; - - process.Dispose(); - - _hostProcesses.Remove(process); - - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrProcessesPoolSize, - -1); - } - - /// - /// Method called by servicing thread. This method will run the command in the - /// specified process on a separate thread - /// - /// - /// - private void RunInProcess(ActivityInvoker invoker, ActivityHostProcess process) - { - if (invoker.IsCancelled) - { - return; - } - - process.Busy = true; - Interlocked.Increment(ref _busyHosts); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrBusyProcessesCount); - - // Adding Tuple object with HostProcess and ActivityInvoker to ThreadPool.QueueUserWorkItem - // Back reference on ActivityHostProcess in ActivityInvoker is removed - // - Tuple tupleProcessAndInvoker = new Tuple(process, invoker); - ThreadPool.QueueUserWorkItem(RunPowerShellInActivityHostWorker, tupleProcessAndInvoker); - } - - #endregion Private Methods - - #region PSActivityHostManager Overrides - - /// - /// Begin invocation of command specified in activity - /// - /// pipeline of command to execute - /// input collection - /// output collection - /// policy to use for the activity - /// optional callback - /// optional caller specified state - /// IAsyncResult - internal IAsyncResult BeginInvokePowerShell(System.Management.Automation.PowerShell command, - PSDataCollection input, PSDataCollection output, PSActivityEnvironment policy, - AsyncCallback callback, object state) - { - if (command == null) - { - throw new ArgumentNullException("command"); - } - - ConnectionAsyncResult result = new ConnectionAsyncResult(state, callback, command.InstanceId); - - _structuredTracer.OutOfProcessRunspaceStarted(command.ToString()); - - ActivityInvoker invoker = - new ActivityInvoker - { - Input = input, - Output = output, - Policy = policy, - PowerShell = command, - AsyncResult = result - }; - - result.Invoker = invoker; - - _requests.Enqueue(invoker); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrIncomingRequestsPerSec); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrPendingRequestsQueueLength); - CheckAndStartServicingThread(); - return result; - } - - /// - /// Block until operation is complete on the current thread - /// - /// IAsyncResult to block on - internal void EndInvokePowerShell(IAsyncResult asyncResult) - { - ConnectionAsyncResult result = asyncResult as ConnectionAsyncResult; - - if (result == null) - { - throw new PSInvalidOperationException(Resources.AsyncResultNotValid); - } - - result.EndInvoke(); - } - - /// - /// Cancels an already started execution - /// - /// async result to cancel - internal void CancelInvokePowerShell(IAsyncResult asyncResult) - { - ConnectionAsyncResult result = asyncResult as ConnectionAsyncResult; - if (result != null) - { - result.Invoker.StopPowerShell(); - } - } - - #endregion PSActivityHostManager Overrides - } - - #region ActivityInvoker - - internal class ActivityInvoker - { - internal PSDataCollection Input { get; set; } - internal PSDataCollection Output { get; set; } - internal System.Management.Automation.PowerShell PowerShell { get; set; } - internal PSActivityEnvironment Policy { get; set; } - internal ConnectionAsyncResult AsyncResult { get; set; } - private readonly object _syncObject = new object(); - private bool _invoked; - private bool _cancelled; - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - internal void InvokePowerShell(Runspace runspace) - { - IAsyncResult result = null; - - if (_cancelled) - return; - - Exception invokeException = null; - - try - { - // Acquiring sync object to fix a race condition between BeginInvoke() and Stop() operations on powershell object. - // Invoked flag is set only when begin invoke is succeeded, so that if this activity is cancelled, powershell needs to be stopped in StopPowerShell(). - // - lock (_syncObject) - { - if (_cancelled) - return; - - _tracer.WriteMessage("State of runspace passed to invoker ", runspace.RunspaceStateInfo.State.ToString()); - - // at this point we assume we have a clean runspace to - // run the command - // if the runspace is broken then the invocation will - // result in an error either ways - PowerShell.Runspace = runspace; - - // Temporary fix: - // If we are about to run this out of proc, we need to complete any open - // input. Otherwise, the out-of-proc PowerShell will hang expecting input. - // However, this has a bug that it will break a third command trying to - // use the same input collection. Nana to resolve with final fix. - if ((Input != null) && Input.EnumeratorNeverBlocks && Input.IsOpen) - { - Input.Complete(); - } - - _tracer.WriteMessage("BEGIN invocation of command out of proc"); - result = Output == null - ? PowerShell.BeginInvoke(Input) - : PowerShell.BeginInvoke(Input, Output); - - // _invoked should bet set here as powershell object can be disposed before runspace assignment due to cancelling an activity - // - _invoked = true; - } // end of lock - - // wait until PowerShell Execution Completes - PowerShell.EndInvoke(result); - _tracer.WriteMessage("END invocation of command out of proc"); - } - catch (Exception exception) - { - // Since the callback is internally handled there should - // not be any exceptions. Fix the same - // ignore any exceptions caused in the callback invocation - _tracer.WriteMessage("Running powershell in activity host threw exception"); - _tracer.TraceException(exception); - invokeException = exception; - - // transport errors are centrally handled for queue and process management - if (exception is PSRemotingTransportException) throw; - } - finally - { - AsyncResult.SetAsCompleted(invokeException); - } - } - - internal void StopPowerShell() - { - bool needToStop = false; - - lock (_syncObject) - { - _cancelled = true; - - if (_invoked) - { - needToStop = true; - } - } - - if (needToStop) - { - PowerShell.Stop(); - } - - AsyncResult.SetAsCompleted(null); - } - - internal bool IsCancelled - { - get - { - lock (_syncObject) - { - return _cancelled; - } - } - } - } - - #endregion ActivityInvoker - -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostProcess.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostProcess.cs deleted file mode 100644 index 83ab8a54339..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostProcess.cs +++ /dev/null @@ -1,564 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Remoting; -using System.Management.Automation.Tracing; -using System.Management.Automation.PerformanceData; -using System.Timers; -using Timer = System.Timers.Timer; -using System.Globalization; - -namespace Microsoft.PowerShell.Workflow -{ - internal class ActivityHostCrashedEventArgs : EventArgs - { - internal bool FailureOnSetup { get; set; } - internal ActivityInvoker Invoker { get; set; } - } - - /// - /// Encapsulates an out of process activity host - /// - /// This class is not thread safe. Caller has to - /// ensure thread safety of accessing internal properties - internal class ActivityHostProcess : IDisposable - { - #region Private Members - private static PSPerfCountersMgr _perfCountersMgr = PSPerfCountersMgr.Instance; - private Runspace _runspace; - private static readonly WSManConnectionInfo ActivityHostConnectionInfo; - private const string ActivityHostShellUri = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell.Workflow.ActivityHost"; - private static readonly string[] ActivitiesTypesFiles = new[] { - @"%windir%\system32\windowspowershell\v1.0\modules\psworkflow\PSWorkflow.types.ps1xml" - }; - private static readonly TypeTable ActivitiesTypeTable; - private const int WSManLocalPort = 47001; - private bool _busy; - private readonly object _syncObject = new object(); - private readonly PowerShellProcessInstance _processInstance; - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private ActivityInvoker _currentInvoker; - - /// - /// This the period of time for which the process will - /// remain inactive. Afterwards it will be killed - /// - private const int TimeOut = 5*60*1000; - private readonly Timer _timer; - - /// - /// Use this flag to flip between IPC process and - /// WSMan process in localhost - /// - private readonly bool _useJobIPCProcess; - - private readonly PSLanguageMode? _languageMode; - - /// - /// Creating a _PSSetVariable to set multiple variables in a single call --> PERF - /// Naming it _PSSetVariable to avoid name collision with Set-Variable cmdlet - /// - private const string SetVariableFunction = @"function _PSSetVariable - { - [CmdletBinding()] - param( - - [Parameter(Position=0)] - [string[]] - $Name, - - [Parameter(Position=1)] - [object[]] - $Value - ) - - for($i=0; $i -lt $Name.Count; $i++) - { - microsoft.powershell.utility\set-variable -name $Name[$i] -value $Value[$i] -scope global - } - - Set-StrictMode -Off - }"; - - private bool _isDisposed; - #endregion Private Members - - #region Constructors - - /// - /// Initialize a connection info object in the static constructor - /// It can be reused for all connections - /// - static ActivityHostProcess() - { - ActivityHostConnectionInfo = new WSManConnectionInfo { Port = WSManLocalPort, ShellUri = ActivityHostShellUri }; - - List typefiles = TypeTable.GetDefaultTypeFiles(); - typefiles.AddRange(ActivitiesTypesFiles.Select(Environment.ExpandEnvironmentVariables)); - ActivitiesTypeTable = new TypeTable(typefiles); - - } - - internal ActivityHostProcess(int activityHostTimeoutSec, PSLanguageMode? languageMode) - { - _languageMode = languageMode; - _useJobIPCProcess = true; - _tracer.WriteMessage("BEGIN Creating new PowerShell process instance"); - _processInstance = new PowerShellProcessInstance(); - _tracer.WriteMessage("END Creating new PowerShell process instance "); - _runspace = CreateRunspace(); - _tracer.WriteMessage("New runspace created ", _runspace.InstanceId.ToString()); - _timer = new Timer {AutoReset = false, Interval = TimeOut}; - _timer.Elapsed += TimerElapsed; - _timer.Interval = activityHostTimeoutSec > 0 ? activityHostTimeoutSec*1000 : TimeOut; - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrCreatedProcessesCount); - } - - #endregion Constructors - - #region Private Methods - - /// - /// Get the runspace corresponding to this process - /// - /// indicates if a new runspace - /// needs to be created - /// runspace object - private Runspace GetRunspace(bool createNew) - { - if (_runspace.RunspaceStateInfo.State == RunspaceState.BeforeOpen) - { - // if the runspace is not opened, open it and return - OpenRunspace(_runspace); - } - else - { - if (_useJobIPCProcess) - { - // dispose the existing runspace and create a new one - CloseAndDisposeRunspace(); - - _runspace = CreateRunspace(); - _tracer.WriteMessage("New runspace created ", _runspace.InstanceId.ToString()); - OpenRunspace(_runspace); - } - } - return _runspace; - } - - private void CloseAndDisposeRunspace() - { - try - { - // Close and dispose the existing runspace - // - _runspace.Close(); - _runspace.Dispose(); - } - catch (Exception e) - { - // RemoteRunspace.Close can throw exceptions when Server process has exited or runspace is invalid. - // Ignoring all exceptions as this runspace was used for previous OOP activity execution. - // - _tracer.TraceException(e); - - // do nothing - } - } - - /// - /// Depending on the option return an Out-of-proc - /// or remoting runspace on localhost - /// - /// runspace object for use - private Runspace CreateRunspace() - { - return _useJobIPCProcess ? RunspaceFactory.CreateOutOfProcessRunspace(ActivitiesTypeTable, _processInstance) : RunspaceFactory.CreateRunspace(ActivityHostConnectionInfo, null, ActivitiesTypeTable); - } - - /// - /// Opens the specified runspace. If there are any errors in - /// opening the runspace, the method just eats them so that - /// an unhandled exception in the background thread in which this - /// method is invoked does not lead to a process crash - /// - /// runspace to open - private void OpenRunspace(Runspace runspace) - { - // a runspace open can fail for a variety of reasons - // eat the exceptions - try - { - _tracer.WriteMessage("Opening runspace " , _runspace.InstanceId.ToString()); - runspace.Open(); - _tracer.WriteMessage("Runspace opened successfully ", _runspace.InstanceId.ToString()); - - if (_languageMode != null && _languageMode.HasValue) - { - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - ps.Runspace = runspace; - string langScript = "$ExecutionContext.SessionState.LanguageMode = '" + _languageMode.Value.ToString() + "'"; - ps.AddScript(langScript); - ps.Invoke(); - } - } - } - catch (PSRemotingTransportRedirectException) - { - // we should not be getting this exception - // in the normal case - _tracer.WriteMessage("Opening runspace threw PSRemotingTransportRedirectException", _runspace.InstanceId.ToString()); - Debug.Assert(false, "We should not get a redirect exception under normal circumstances"); - } - catch (PSRemotingTransportException transportException) - { - _tracer.WriteMessage("Opening runspace threw PSRemotingTransportException", _runspace.InstanceId.ToString()); - _tracer.TraceException(transportException); - // throwing PSRemotingTransportException exception as it will be handled at single place in PrepareAndRun() method. - throw; - } - catch(PSRemotingDataStructureException) - { - // just eat the exception - _tracer.WriteMessage("Opening runspace threw PSRemotingDataStructureException", _runspace.InstanceId.ToString()); - Debug.Assert(false, "We should not get a protocol exception under normal circumstances"); - } - } - - /// - /// Import all the specified modules in the specified runspace - /// - /// runspace in which to import the modules - /// collection of modules to import - /// true if setting up of runspace from policy succeeded - private void ImportModulesFromPolicy(Runspace runspace, ICollection modules) - { - // If any modules are specified, load them into the runspace. - // In general, autoloading should be preferred to explicitly loading a module - // at startup time... - if (modules.Count <= 0) - return; - - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - ps.Runspace = runspace; - // Setting erroraction to stop for import-module since they are required modules. If not present, stop the execution - ps.AddCommand("Import-Module").AddArgument(modules).AddParameter("ErrorAction",ActionPreference.Stop).AddParameter("Force"); - ps.Invoke(); - } - } - - /// - /// Set the specified variables in the runspace - /// - /// runspace in which the variables need - /// to be set - /// collection of variables that need to be set - private void SetVariablesFromPolicy(Runspace runspace, IDictionary variables) - { - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - ps.Runspace = runspace; - ps.AddScript(SetVariableFunction); - ps.Invoke(); - - ps.Commands.Clear(); - ps.AddCommand("_PSSetVariable").AddParameter("Name", variables.Keys).AddParameter("Value", variables.Values); - ps.Invoke(); - - // Remove the temporary function _PSSetVariable after its use is done. - ps.Commands.Clear(); - ps.AddCommand("Remove-Item").AddParameter("Path", "function:\\_PSSetVariable").AddParameter("Force"); - ps.Invoke(); - - } - } - - /// - /// Set the busy flag since the process is being used. - /// Stop the idle timer - /// - private void SetBusy() - { - lock(_syncObject) - { - _busy = true; - - // stop the idle timer - _timer.Stop(); - } - } - - /// - /// Reset the busy flag and start the idle timer - /// - private void ResetBusy() - { - lock(_syncObject) - { - // Process state should not be set to false if it is already marked for removal - // Process is marked for removal in two cases: - // 1) When PSRemotingTransportException is received in PrepareAndRun, in this case Server process is already crashed/exited. - // If busy state is set to false, this process can be assigned to another activity by the servicing thread. - // Instead of adding separate boolean variable in PrepareAndRun to call ResetBusy we are checking for not marked for removal here. - // 2) When idle time of 5 min elapsed, process is marked for removal so that server process can be killed. After this, ResetBusy is never called. - // - if (_busy && !_markForRemoval) - { - _busy = false; - - // enable the idle timer - _timer.Enabled = true; - - this.RaiseProcessFinishedEvent(); - } - } - } - - /// - /// Idle timeout has occured. If the runspace is not being used - /// just close it - /// - /// sender of this event - /// unused - private void TimerElapsed(object sender, ElapsedEventArgs e) - { - lock (_syncObject) - { - if (_busy) return; - - // Mark this process for removal and set it as busy so that this process will not be assigned to any new activity. - // Marking for removal will ensure that servicing thread will remove this object from host process collection. - // - _busy = true; - _markForRemoval = true; - } - - if (OnProcessIdle != null) - OnProcessIdle(this, new EventArgs()); - } - - /// - /// Handles a transport error since it is most likely that - /// the process crash - /// - /// the transport exception - /// that was raised - /// true indicates that the crash was - /// encountered when setting up the process and not when the - /// command was run - private void HandleTransportError(PSRemotingTransportException transportException, bool onSetup) - { - _tracer.TraceException(transportException); - - if (ProcessCrashed != null) - { - ActivityHostCrashedEventArgs eventArgs = new ActivityHostCrashedEventArgs - {FailureOnSetup = onSetup, - Invoker = _currentInvoker}; - ProcessCrashed(this, eventArgs); - } - } - - internal void RaiseProcessFinishedEvent() - { - if (Finished != null) - Finished(this, new EventArgs()); - } - - #endregion Private Methods - - #region Internal Methods - - internal static String ActivityHostConfiguration - { - get { return ActivityHostShellUri; } - } - internal event EventHandler Finished; - internal event EventHandler OnProcessIdle; - - internal event EventHandler ProcessCrashed; - - /// - /// Prepare the environment based on the policy and run the powershell - /// - /// - /// It is assumed that the caller of this method will spawn appropriate - /// thread and so it is fine for us to call the callback on the same thread - internal void PrepareAndRun(ActivityInvoker invoker) - { - bool setupSucceeded = false; - try - { - // Transport (PSRemotingTransportException) error can happen during setup/prepare phase, so it is getting assigned before setup phase. - // - _currentInvoker = invoker; - Runspace runspace = null; - // Retry for 10 times if runspace is in Broken state - // - // Runspace can be broken, when a remote runspace is getting closed during the next activity execution, - // the close ack is getting timedout and that close ack is received by the newly created remote runspace for - // the next activity since stdoutput stream of server process is not cleared/discarded, - // that is why during new runspace is in broken state while it is being opened. - // - for (int i = 1; (i <= 10) && !invoker.IsCancelled; i++) - { - // prepare the runspace with the necessary - // modules and variables here - runspace = GetRunspace(true); - - if (runspace.RunspaceStateInfo.State == RunspaceState.Opened) - break; - - System.Threading.Thread.Sleep(i*200); - } - - if (invoker.IsCancelled) - { - return; - } - - Debug.Assert((runspace.RunspaceStateInfo.State == RunspaceState.Opened), "Runspace is not in Opened state for running an OOP activity"); - - if (invoker.Policy.Variables.Count > 0) - { - // set the variables in the specified runspace - _tracer.WriteMessage("BEGIN Setting up variables in runspace ", _runspace.InstanceId.ToString()); - SetVariablesFromPolicy(runspace, invoker.Policy.Variables); - _tracer.WriteMessage("END Setting up variables in runspace ", _runspace.InstanceId.ToString()); - } - - if (invoker.Policy.Modules.Count > 0) - { - // attempt loading the modules in the specified runspace - _tracer.WriteMessage("BEGIN Setting up runspace from policy ", _runspace.InstanceId.ToString()); - ImportModulesFromPolicy(runspace, invoker.Policy.Modules); - _tracer.WriteMessage("END Setting up runspace from policy ", _runspace.InstanceId.ToString()); - } - - // Prepare phase is completed without any issues. - // setupSucceeded flag is used in HandleTransportError method to enqueue the current activity for retry. - // If there is any PSRemotingTransportException during InvokePowershell current activity will not be enqueued to setup failed requests in ActivityHostManager. - // - setupSucceeded = true; - - // at this point we assume we have a clean runspace to - // run the command - // if the runspace is broken then the invocation will - // result in an error either ways - invoker.InvokePowerShell(runspace); - } - catch (PSRemotingTransportException transportException) - { - HandleTransportError(transportException, !setupSucceeded); - } - catch (Exception e) - { - // at this point there is an exception other than a - // transport exception that is caused by trying to - // setup the runspace. Release the asyncresult - // accordingly - _tracer.TraceException(e); - invoker.AsyncResult.SetAsCompleted(e); - } - finally - { - _currentInvoker = null; - ResetBusy(); - } - } - - /// - /// If the process represented by this object is busy - /// - internal bool Busy - { - get - { - lock (_syncObject) - { - return _busy; - } - } - set - { - if (value) SetBusy(); - else ResetBusy(); - } - } - - /// - /// Indicates that the associated process crashed and this object - /// needs to be removed by ActivityHostManager - /// - internal bool MarkForRemoval { - get - { - // _processInstance.HasExited can not be used to tell as marked for removal - // as it will cause inconsistent busy host count - // - return _markForRemoval; - } - set - { - _markForRemoval = value; - } - } - private bool _markForRemoval = false; - - #endregion Internal Methods - - #region IDisposable - - /// - /// Dispose - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// dispose of managed resources - /// - /// true if being disposed - protected void Dispose(bool disposing) - { - if (!disposing) return; - - if (_isDisposed) return; - lock (_syncObject) - { - if (_isDisposed) return; - _isDisposed = true; - } - - Debug.Assert(_runspace != null, "Runspace is already null"); - - CloseAndDisposeRunspace(); - - _processInstance.Dispose(); - - _currentInvoker = null; - _tracer.Dispose(); - - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrDisposedProcessesCount); - } - - #endregion IDisposable - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityUtils.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityUtils.cs deleted file mode 100644 index ccc5af33447..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityUtils.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Management.Automation; -using System.Management.Automation.Remoting; -using System.Management.Automation.Runspaces; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - static class ActivityUtils - { - // Extension method to add IsNullOrEmpty to arrays. - internal static bool IsNullOrEmpty(this System.Collections.ICollection c) - { - return (c == null || c.Count == 0); - } - - private static int DefaultMaximumConnectionRedirectionCount = 5; - - internal static List GetConnectionInfo(string[] PSComputerName, string[] PSConnectionUri, - string PSCertificateThumbprint, string PSConfigurationName, - bool? PSUseSsl, uint? PSPort, string PSApplicationName, - PSCredential PSCredential, AuthenticationMechanism PSAuthentication, - bool PSAllowRedirection, System.Management.Automation.Remoting.PSSessionOption options) - { - List connections = new List(); - - string[] machineList = null; - bool connectByComputerName = false; - - // Connect by computername - if ((! PSComputerName.IsNullOrEmpty()) && (PSConnectionUri.IsNullOrEmpty())) - { - machineList = PSComputerName; - connectByComputerName = true; - } - else if ((PSComputerName.IsNullOrEmpty()) && (! PSConnectionUri.IsNullOrEmpty())) - { - machineList = PSConnectionUri; - } - else - { - throw new ArgumentException(Resources.CannotSupplyUriAndComputername); - } - - // Go through each machine in the list an update its properties - foreach (string machine in machineList) - { - if (!string.IsNullOrEmpty(machine)) - { - WSManConnectionInfo connectionInfo = new WSManConnectionInfo(); - - if (PSPort.HasValue) - { - connectionInfo.Port = (int)PSPort.Value; - } - - if (PSUseSsl.HasValue && (PSUseSsl.Value)) - { - connectionInfo.Scheme = WSManConnectionInfo.HttpsScheme; - } - - if (!String.IsNullOrEmpty(PSConfigurationName)) - { - connectionInfo.ShellUri = PSConfigurationName; - } - - if (!String.IsNullOrEmpty(PSApplicationName)) - { - connectionInfo.AppName = PSApplicationName; - } - - if (connectByComputerName) - { - connectionInfo.ComputerName = machine; - } - else - { - connectionInfo.ConnectionUri = (Uri)LanguagePrimitives.ConvertTo(machine, typeof(Uri), System.Globalization.CultureInfo.InvariantCulture); - } - - if (PSCredential != null) - { - connectionInfo.Credential = PSCredential; - } - - if (!String.IsNullOrEmpty(PSCertificateThumbprint)) - { - connectionInfo.CertificateThumbprint = PSCertificateThumbprint; - } - - if (PSAuthentication != AuthenticationMechanism.Default) - { - connectionInfo.AuthenticationMechanism = PSAuthentication; - } - - connectionInfo.MaximumConnectionRedirectionCount = PSAllowRedirection ? DefaultMaximumConnectionRedirectionCount : 0; - - if (options != null) - { - connectionInfo.SetSessionOptions(options); - } - - connections.Add(connectionInfo); - } - else - { - // add a null connection to account for "" or $null in PSComputerName parameter - connections.Add(null); - } - } - - return connections; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ConnectionManager.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ConnectionManager.cs deleted file mode 100644 index 93404b4c62e..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ConnectionManager.cs +++ /dev/null @@ -1,2372 +0,0 @@ -/* - * Copyright (c) 2010 Microsoft Corporation. All rights reserved - */ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Threading; -using System.Timers; -using Timer = System.Timers.Timer; -using Microsoft.PowerShell.Activities; -using System.Management.Automation.Tracing; -using System.Management.Automation.PerformanceData; - -namespace Microsoft.PowerShell.Workflow -{ - #region Connection - - /// - /// class the contains a remote connection (runspace) and associated - /// data for managing the same - /// - /// the only reason this class has an internal scope is for - /// test purposes. Else this can be a private class inside - /// connection manager - internal class Connection - { - private Runspace _runspace; - private bool _busy; - private bool _idle; - private readonly object _syncObject = new object(); - private static readonly EventArgs EventArgs = new EventArgs(); - private readonly Guid _instanceId = Guid.NewGuid(); - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private readonly Tracer _structuredTracer = new Tracer(); - private static readonly PSPerfCountersMgr _perfCountersMgr = PSPerfCountersMgr.Instance; - private bool _readyForDisconnect = false; - private bool _readyForReconnect = false; - private ConnectionManager _manager; - - /// - /// retry interval cannot be 0, so initial value is 1 - /// - private uint _retryInterval = 1; - - internal uint RetryAttempt { get; set; } - internal uint RetryCount { get; set; } - internal GetRunspaceAsyncResult AsyncResult { get; set; } - internal WSManConnectionInfo ConnectionInfo { get; set; } - - internal event EventHandler CloseCompleted; - internal event EventHandler OpenCompleted; - internal event EventHandler DisconnectCompleted; - internal event EventHandler ReconnectCompleted; - - private const int Open = 1; - private const int Close = 2; - private const int Disconnect = 3; - private const int Reconnect = 4; - - internal Connection(ConnectionManager manager) - { - _manager = manager; - _tracer.WriteMessage("PSW Conn: Creating new connection"); - } - - internal bool DisconnectedIntentionally { get; set; } - - internal Runspace Runspace - { - get { return _runspace; } - } - - internal Guid InstanceId - { - get { return _instanceId; } - } - - internal bool Idle - { - get - { - lock (_syncObject) - { - return _idle; - } - } - set - { - lock (_syncObject) - { - _idle = value; - } - } - } - internal bool ReadyForDisconnect - { - get - { - lock (_syncObject) - { - return _readyForDisconnect; - } - } - - set - { - lock (_syncObject) - { - _readyForDisconnect = value; - - if (_readyForDisconnect) - _readyForReconnect = false; - } - } - } - - internal bool ReadyForReconnect - { - get - { - lock (_syncObject) - { - return _readyForReconnect; - } - } - set - { - lock (_syncObject) - { - _readyForReconnect = value; - - if (_readyForReconnect) - _readyForDisconnect = false; - } - } - } - - internal bool Busy - { - get - { - lock (_syncObject) - { - return _busy; - } - } - set - { - lock (_syncObject) - { - _busy = value; - - if (_busy) - { - _idle = false; - } - - // connection is set to busy by the connection manager - // when it is assigning the connection to a new activity - // then the connection should be marked as not ready for - // disconnect since the activity has to signal the same - // when it is not busy we do not want to disconnect idle - // connections, hence it needs to be marked as not ready - // for disconnect - _readyForDisconnect = false; - _readyForReconnect = false; - } - } - } - - internal uint RetryInterval - { - get { return _retryInterval; } - set - { - _retryInterval = value; - if (_retryInterval == 0) _retryInterval = 1; - } - } - - private bool CheckAndReconnectAfterCrashOrShutdown(WSManConnectionInfo connectionInfo) - { - bool connectAsyncSucceeded = false; - - RunCommandsArguments args = this.AsyncResult.AsyncState as RunCommandsArguments; - if (args != null) - { - Guid runspaceId = args.ImplementationContext.DisconnectedRunspaceInstanceId; - - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell currentPowerShellInstance = implementationContext.PowerShellInstance; - PSActivityContext psActivityContext = args.PSActivityContext; - - // Non-empty guid represents that args.ImplementationContext.EnableRemotingActivityAutoResume is true - if (!runspaceId.Equals(Guid.Empty)) - { - Runspace[] runspaces = null; - - try - { - // Queries the server for disconnected runspaces. - // Each runspace object in the returned array is in the Disconnected state and can be - // connected to the server by calling the Connect() or ConnectAsync() methods on the runspace. - runspaces = Runspace.GetRunspaces(connectionInfo); - } - catch (System.Management.Automation.RuntimeException) - { - _tracer.WriteMessage("PSW Conn: Disconnected Remote Runspace is not available. Runspace Instance Id: " + runspaceId.ToString()); - } - - if (runspaces != null) - { - // In local machine case, remote runspace with the specific id will be not be available after machine shutdown. - // Remoterunspace on local machine may be available if only current process is crashed or suspended forcefully. - // OpenAsync() will takes care of create new remote runspace if disconnected runspace is not available. - - Runspace foundRunspace = runspaces.FirstOrDefault(currentRunspace => currentRunspace.InstanceId.Equals(runspaceId)); - - if (foundRunspace != null && foundRunspace.RunspaceStateInfo.State == RunspaceState.Disconnected) - { - System.Management.Automation.PowerShell disconnectedPowerShell = null; - - try - { - // When disconnectedPowerShell is not available PSInvalidOperationException will be thrown and finally block takes care of releasing the runspace. - // And OpenAsync() will create a new remote runspace if disconnected runspace is not available. - disconnectedPowerShell = foundRunspace.CreateDisconnectedPowerShell(); - - // Add stream handler and connect to disconnected remote runspace - PSActivity.AddHandlersToStreams(disconnectedPowerShell, args); - disconnectedPowerShell.ConnectAsync(args.Output, PSActivity.PowerShellInvocationCallback, args); - - - // Assign the disconnected powershell to ImplementationContext and dispose the existing powershell instance. - implementationContext.PowerShellInstance = disconnectedPowerShell; - - // Since we are disposing the current command, remove this command from the list - // of running commands. - lock (psActivityContext.runningCommands) - { - RetryCount retryCount = psActivityContext.runningCommands[currentPowerShellInstance]; - psActivityContext.runningCommands.Remove(currentPowerShellInstance); - psActivityContext.runningCommands[disconnectedPowerShell] = retryCount; - } - currentPowerShellInstance.Dispose(); - - _runspace = foundRunspace; - _runspace.StateChanged += RunspaceStateChanged; - - connectAsyncSucceeded = true; - } - catch (PSInvalidOperationException) - { - _tracer.WriteMessage("PSW Conn: Disconnected PowerShell is not available. Runspace Instance Id: " + foundRunspace.InstanceId.ToString()); - } - finally - { - // release the runspace if reconnect did not happen. - if (connectAsyncSucceeded == false && foundRunspace != null) - { - try - { - // Remove stream handlers on disconnectedPowerShell - // Non null disconnectedPowerShell represents that handlers were added on disconnectedPowerShell and an exception happened during ConnectAsync. - if (disconnectedPowerShell != null) - PSActivity.RemoveHandlersFromStreams(disconnectedPowerShell, args); - - foundRunspace.Close(); - foundRunspace.Dispose(); - } - catch (Exception) - { - // Runspace.Close can throw exceptions when Server process has exited or runspace is invalid. - // Ignoring all exceptions. - // - _tracer.WriteMessage("PSW Conn: Disconnected PowerShell is not available. Runspace Instance Id: " + foundRunspace.InstanceId.ToString()); - } - } - } - } - } - } - } - - return connectAsyncSucceeded; - } - - internal void OpenAsync() - { - // Always create runspace with WSManConnectionInfo.EnableNetworkAccess set to true. - WSManConnectionInfo copyConnectionInfo = ConnectionInfo.Copy(); - copyConnectionInfo.EnableNetworkAccess = true; - - // If the remote runspace is not available after crash/shutdown, - // activity task resume will fall back to reexecuting in a new runspace is assigned for the connection - if (!CheckAndReconnectAfterCrashOrShutdown(copyConnectionInfo)) - { - _runspace = RunspaceFactory.CreateRunspace(copyConnectionInfo, null, LocalRunspaceProvider.SharedTypeTable); - _runspace.StateChanged += RunspaceStateChanged; - _tracer.WriteMessage("PSW Conn: Calling OpenAsync on runspace"); - _runspace.OpenAsync(); - } - } - - internal void CloseAsync() - { - _tracer.WriteMessage("PSW Conn: Calling CloseAsync on runspace"); - - // at this point the runspace can be in a closed or broken state - // if so then close has to simply raise the event - // if it was not in a terminal state when checking but happens - // later but before CloseAsync() is called then the other - // call which caused the state change will raise an event - if (_runspace.RunspaceStateInfo.State == RunspaceState.Broken || - _runspace.RunspaceStateInfo.State == RunspaceState.Closed) - { - RaiseEvents(Close); - } - else - _runspace.CloseAsync(); - } - - internal void DisconnectAsync() - { - bool disconnect = false; - - if (_readyForDisconnect) - { - lock (_syncObject) - { - if (_readyForDisconnect) - { - _readyForDisconnect=false; - _readyForReconnect = false; - disconnect = true; - } - } - if (disconnect) - { - _tracer.WriteMessage("PSW Conn: Calling Disconnect Async"); - _manager.DisconnectCalled(); - _runspace.DisconnectAsync(); - return; - } - } - RaiseEvents(Disconnect); - } - - internal void ReconnectAsync() - { - bool reconnect = false; - - if (_readyForReconnect) - { - lock (_syncObject) - { - if (_readyForReconnect) - { - _readyForReconnect = false; - _readyForDisconnect = false; - reconnect = true; - } - } - - if (reconnect) - { - _tracer.WriteMessage("PSW Conn: Calling reconnect async"); - _manager.ReconnectCalled(); - _runspace.ConnectAsync(); - return; - } - } - RaiseEvents(Reconnect); - } - - private void RunspaceStateChanged(object sender, RunspaceStateEventArgs e) - { - _structuredTracer.RunspaceStateChanged(this._instanceId.ToString(), e.RunspaceStateInfo.State.ToString(), string.Empty); - _tracer.WriteMessage("PSW Conn: runspace state" + e.RunspaceStateInfo.State.ToString()); - switch (e.RunspaceStateInfo.State) - { - case RunspaceState.Opening: - return; - - case RunspaceState.Closing: - return; - - case RunspaceState.Disconnecting: - { - ReadyForDisconnect = false; - } - return; - - case RunspaceState.Opened: - { - // runspace opened successfully, assign it to the asyncresult - // reset retry counter - RetryAttempt = 0; - ReadyForReconnect = false; - AsyncResult.Connection = this; - // this SetAsCompleted will result in the callback to the - // activity happening in the WinRM thread - AsyncResult.SetAsCompleted(null); - RaiseEvents(Open); - RaiseEvents(Reconnect); - } - break; - - case RunspaceState.Broken: - { - // Dispose the broken runspace before retry - DisposeRunspace(); - - // connection attempt failed, retry - if (RetryCount > 0 && RetryAttempt < RetryCount) - { - RetryAttempt++; - - LogRetryAttempt(RetryAttempt, RetryCount); - - Timer timer = new Timer { AutoReset = false, Enabled = false, Interval = _retryInterval * 1000 }; - timer.Elapsed += RetryTimerElapsed; - timer.Start(); - } - else - { - // all retries have failed, end the asyncresult with an exception - // message - Busy = false; - - lock (_syncObject) - { - if (AsyncResult != null) - { - AsyncResult.Connection = null; - AsyncResult.SetAsCompleted(e.RunspaceStateInfo.Reason); - } - } - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "Disposing broken connection to {0}", _runspace.ConnectionInfo.ComputerName)); - - RaiseEvents(Open); - RaiseEvents(Disconnect); - RaiseEvents(Close); - RaiseEvents(Reconnect); - } - } - break; - - case RunspaceState.Disconnected: - { - ReadyForReconnect = true; - RaiseEvents(Disconnect); - } - break; - - case RunspaceState.Closed: - { - DisposeRunspace(); - RaiseEvents(Close); - } - break; - } - } - - private void LogRetryAttempt(uint retryAttempt, uint retryTotal) - { - RunCommandsArguments args = this.AsyncResult.AsyncState as RunCommandsArguments; - if (args != null) - { - PSActivityContext psActivityContext = args.PSActivityContext; - - // Write / Log that an activity retry was required. - if (psActivityContext.progress != null) - { - string progressActivity = ((System.Activities.Activity)psActivityContext.ActivityObject).DisplayName; - - if (string.IsNullOrEmpty(progressActivity)) - progressActivity = psActivityContext.ActivityType.Name; - - string retryMessage = String.Format(CultureInfo.CurrentCulture, Resources.RetryingConnection, retryAttempt, retryTotal); - - ProgressRecord progressRecord = new ProgressRecord(0, progressActivity, retryMessage); - lock (psActivityContext.progress) - { - psActivityContext.progress.Add(progressRecord); - } - } - } - } - - private void RaiseEvents(int eventType) - { - switch (eventType) - { - case Open: - { - if (OpenCompleted != null) OpenCompleted(this, EventArgs); - } - break; - case Close: - { - if (CloseCompleted != null) CloseCompleted(this, EventArgs); - } - break; - case Disconnect: - { - if (DisconnectCompleted != null) DisconnectCompleted(this, EventArgs); - } - break; - case Reconnect: - { - if (ReconnectCompleted != null) ReconnectCompleted(this, EventArgs); - } - break; - } - } - - private void DisposeRunspace() - { - _runspace.StateChanged -= RunspaceStateChanged; - _tracer.WriteMessage("PSW Conn: disposing runspace"); - _runspace.Dispose(); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingConnectionsDisposedCount); - } - - private void RetryTimerElapsed(object sender, ElapsedEventArgs elapsedEventArgs) - { - // specified retry interval timeout has elapsed, try again - // dispose the timer - Timer timer = sender as Timer; - if (timer != null) timer.Dispose(); - - // retry the connection again - OpenAsync(); - } - } - - #endregion Connection - - #region GetRunspaceAsyncResult - - /// - /// AsyncResult object returned by ConnectionManager - /// - /// the only reason this class has an internal scope is for - /// test purposes. Else this can be a private class inside - /// connection manager - internal class GetRunspaceAsyncResult : ConnectionAsyncResult - { - internal GetRunspaceAsyncResult(object state, AsyncCallback callback, Guid ownerId) : - base(state, callback, ownerId) - { - } - - internal Connection Connection { get; set; } - } - - #endregion GetRunspaceAsyncResult - - #region ThrottleOperation - - internal abstract class ThrottleOperation - { - internal event EventHandler OperationComplete; - private static readonly EventArgs EventArgs = new EventArgs(); - - internal void RaiseOperationComplete() - { - if (OperationComplete != null) OperationComplete(this, EventArgs); - } - - internal virtual void DoOperation() - { - throw new NotImplementedException(); - } - } - - internal class OpenOperation : ThrottleOperation - { - private readonly Connection _connection; - - internal OpenOperation(Connection connection) - { - _connection = connection; - _connection.OpenCompleted += HandleOpenCompleted; - } - - private void HandleOpenCompleted(object sender, EventArgs eventArgs) - { - _connection.OpenCompleted -= HandleOpenCompleted; - RaiseOperationComplete(); - } - - internal override void DoOperation() - { - _connection.OpenAsync(); - } - } - - internal class CloseOperation : ThrottleOperation - { - private readonly Connection _connection; - internal Connection Connection - { - get { return _connection; } - } - - internal CloseOperation(Connection connection) - { - _connection = connection; - _connection.CloseCompleted += HandleCloseCompleted; - } - - private void HandleCloseCompleted(object sender, EventArgs eventArgs) - { - _connection.CloseCompleted -= HandleCloseCompleted; - RaiseOperationComplete(); - } - - internal override void DoOperation() - { - _connection.CloseAsync(); - } - } - - internal class CloseOneAndOpenAnotherOperation : ThrottleOperation - { - private readonly Connection _connectionToClose; - private readonly Connection _connectionToOpen; - - internal CloseOneAndOpenAnotherOperation(Connection toClose, Connection toOpen) - { - _connectionToClose = toClose; - _connectionToOpen = toOpen; - - _connectionToClose.CloseCompleted += HandleCloseCompleted; - _connectionToOpen.OpenCompleted += HandleOpenCompleted; - } - - private void HandleOpenCompleted(object sender, EventArgs eventArgs) - { - _connectionToOpen.OpenCompleted -= HandleOpenCompleted; - RaiseOperationComplete(); - } - - private void HandleCloseCompleted(object sender, EventArgs eventArgs) - { - _connectionToClose.CloseCompleted -= HandleCloseCompleted; - _connectionToOpen.OpenAsync(); - } - - internal override void DoOperation() - { - _connectionToClose.CloseAsync(); - } - } - - internal class DisconnectOperation : ThrottleOperation - { - private readonly Connection _connection; - - internal DisconnectOperation(Connection connection) - { - _connection = connection; - _connection.DisconnectCompleted += HandleDisconnectCompleted; - } - - private void HandleDisconnectCompleted(object sender, EventArgs eventArgs) - { - _connection.DisconnectCompleted -= HandleDisconnectCompleted; - RaiseOperationComplete(); - } - - internal override void DoOperation() - { - _connection.DisconnectAsync(); - } - } - - internal class ReconnectOperation : ThrottleOperation - { - private readonly Connection _connection; - - internal ReconnectOperation(Connection connection) - { - _connection = connection; - _connection.ReconnectCompleted += HandleReconnectCompleted; - } - - private void HandleReconnectCompleted(object sender, EventArgs eventArgs) - { - _connection.ReconnectCompleted -= HandleReconnectCompleted; - RaiseOperationComplete(); - } - - internal override void DoOperation() - { - if (_connection.Runspace.RunspaceStateInfo.State != RunspaceState.Disconnected) - { - EventHandler HandleRunspaceStateChanged = null; - HandleRunspaceStateChanged = - delegate(object sender, RunspaceStateEventArgs e) - { - if (e.RunspaceStateInfo.State == RunspaceState.Disconnected) - { - _connection.ReconnectAsync(); - _connection.Runspace.StateChanged -= HandleRunspaceStateChanged; - } - }; - - _connection.Runspace.StateChanged += HandleRunspaceStateChanged; - - if (_connection.Runspace.RunspaceStateInfo.State == RunspaceState.Disconnected) - { - _connection.ReconnectAsync(); - _connection.Runspace.StateChanged -= HandleRunspaceStateChanged; - } - - return; - } - - _connection.ReconnectAsync(); - } - } - - #endregion ThrottleOperation - - internal class ConnectionManager : RunspaceProvider, IDisposable - { - #region RequestInfo - - private class RequestInfo - { - private WSManConnectionInfo _connectionInfo; - private uint _retryCount; - private GetRunspaceAsyncResult _asyncResult; - private uint _retryInterval; - - internal WSManConnectionInfo ConnectionInfo - { - get { return _connectionInfo; } - set { _connectionInfo = value; } - } - - internal uint RetryCount - { - get { return _retryCount; } - set { _retryCount = value; } - } - - internal GetRunspaceAsyncResult AsyncResult - { - get { return _asyncResult; } - set { _asyncResult = value; } - } - - internal uint RetryInterval - { - get { return _retryInterval; } - set { _retryInterval = value; } - } - } - - #endregion RequestInfo - - #region Private members - - // this is the connection pool indexed based on a given machine - // and a given session configuration - private readonly ConcurrentDictionary>> _connectionPool = - new ConcurrentDictionary>>(); - - // this is the list of computers to which a cleanup is requested - private readonly ConcurrentDictionary>> _cleanupComputers = - new ConcurrentDictionary>>(); - - private readonly int _idleTimeout; - private readonly int _maxOutBoundConnections; - private readonly int _maxConnectedSessions; - private readonly int _maxDisconnectedSessions; - - private int _isServicing; - private int _isServicingCallbacks; - private int _isServicingCleanups; - private const int Servicing = 1; - private const int NotServicing = 0; - private readonly Timer _timer; - private readonly ConcurrentQueue _inComingRequests = new ConcurrentQueue(); - - private readonly ConcurrentQueue _callbacks = - new ConcurrentQueue(); - - private int _timerFired; - private const int TimerFired = 1; - private const int TimerReset = 0; - private bool _isDisposed; - private bool _isDisposing; - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private readonly object _syncObject = new object(); - private static readonly PSPerfCountersMgr _perfCountersMgr = PSPerfCountersMgr.Instance; - - - /// - /// The servicing thread will release control of processing and the timer - /// thread will take control and process based on this wait handle - /// - private readonly ManualResetEvent _servicingThreadRelease = new ManualResetEvent(false); - - /// - /// The timer thread will release control of processing and the servicing thread - /// will take control and process based on this wait handle - /// - private readonly ManualResetEvent _timerThreadRelease = new ManualResetEvent(true); - - /// - /// This list is assumed to accessed by only the servicing thread and - /// hence is not designed to be thread safe - /// - private readonly List _pendingRequests = new List(); - - /// - /// number of sessions in the connected state - /// - /// this is made static in the interest of time - /// should not be static - //internal static int ConnectedSessionCount = 0; - private int _connectedSessionCount = 0; - - /// - /// number of sessions in the disconnected state - /// - /// this is made static in the interest of time - /// should not be static - //internal static int _disconnectedSessionCount = 0; - private int _disconnectedSessionCount = 0; - - private int _createdConnections = 0; - - /// - /// if we need to check whether runspaces need to be - /// disconnected - /// - private int _checkForDisconnect = 0; - private const int CheckForDisconnect = 1; - private const int DoNotCheckForDisconnect = 0; - - /// - /// Map of timers for each machine - /// - private readonly ConcurrentDictionary _timerMap = new ConcurrentDictionary(); - - #endregion Private Members - - #region Constructors - - internal ConnectionManager(int idleTimeout, int maxOutBoundConnections, int throttleLimit, int maxConnectedSessions, int maxDisconnectedSessions) - { - _idleTimeout = idleTimeout; - _maxOutBoundConnections = maxOutBoundConnections; - _throttleLimit = throttleLimit; - _maxConnectedSessions = maxConnectedSessions; - _maxDisconnectedSessions = maxDisconnectedSessions; - - _timer = new Timer {AutoReset = true, Interval = _idleTimeout, Enabled=false}; - _timer.Elapsed += HandleTimerElapsed; - _timer.Start(); - } - - #endregion Constructors - - #region Interface Methods - - /// - /// Get runspace for the specified connection info to be - /// used for running commands - /// - /// connection info to use - /// retry count - /// retry interval in ms - /// remote runspace to use - public override Runspace GetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval) - { - IAsyncResult asyncResult = BeginGetRunspace(connectionInfo, retryCount, retryInterval, null, null); - - return EndGetRunspace(asyncResult); - } - - /// - /// Begin for obtaining a runspace for the specified ConnectionInfo - /// - /// connection info to be used for remote connections - /// number of times to retry - /// optional user defined callback - /// optional user specified state - /// time in milliseconds before the next retry has to be attempted - /// async result - public override IAsyncResult BeginGetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval, AsyncCallback callback, object state) - { - GetRunspaceAsyncResult result = new GetRunspaceAsyncResult(state, callback, Guid.Empty); - - // Create a Request Object and queue the same - RequestInfo requestInfo = new RequestInfo - { - ConnectionInfo = connectionInfo, - RetryCount = retryCount, - AsyncResult = result, - RetryInterval = retryInterval - }; - - _tracer.WriteMessage("PSW ConnMgr: New incoming request for runspace queued"); - _inComingRequests.Enqueue(requestInfo); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingPendingRequestsQueueLength); - - // start servicing thread if required - CheckAndStartRequiredThreads(); - - return result; - } - - /// - /// End for obtaining a runspace for the specified connection info - /// - /// async result to end on - /// remote runspace to invoke commands on - public override Runspace EndGetRunspace(IAsyncResult asyncResult) - { - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); - - GetRunspaceAsyncResult result = asyncResult as GetRunspaceAsyncResult; - - if (result == null) - throw new ArgumentException(Resources.InvalidAsyncResultSpecified, "asyncResult"); - - AssertNotDisposed(); - - // this will throw an exception when a runspace is not successfully - // available - result.EndInvoke(); - - Debug.Assert(result.Connection != null, "EndInvoke() should throw an exception if connection is null"); - - _tracer.WriteMessage("PSW ConnMgr: Request serviced and runspace returned"); - Runspace runspace = result.Connection != null ? result.Connection.Runspace : null; - - return runspace; - } - - /// - /// Release the runspace once the activity is done using the same - /// - /// runspace to release - public override void ReleaseRunspace(Runspace runspace) - { - AssertNotDisposed(); - - Connection connection = GetConnectionForRunspace(runspace); - _tracer.WriteMessage("PSW ConnMgr: Runspace released"); - - // at this point connection will be not null - // since GetConnectionForRunspace() should have - // handled all cases and raised an exception - Debug.Assert(connection != null, "GetConnectionForRunspace has not handled all cases and raised an exception"); - - connection.Busy = false; - connection.AsyncResult = null; - - CheckAndStartRequiredThreads(); - } - - /// - /// Request a cleanup to the destination specified in the - /// connection info. This means no runspaces will be held - /// to the specified connection info. - /// - /// connection info to which - /// cleanup is desired - ///callback to invoke - /// caller specified state - public override void RequestCleanup(WSManConnectionInfo connectionInfo, WaitCallback callback, object state) - { - if (_isDisposed || _isDisposing) - { - if (callback != null) - { - callback(state); - } - - return; - } - - // add this computer to the list of cleanup computers - string computerName = connectionInfo.ComputerName; - - ConcurrentQueue> callbacks = - _cleanupComputers.GetOrAdd(computerName, new ConcurrentQueue>()); - - var tuple = new Tuple(callback, state); - callbacks.Enqueue(tuple); - - CheckAndStartCleanupThread(); - } - - /// - /// Checks to see if the provider intentionally disconnected a runspace - /// or it went into disconnected state due to network issues - /// - /// runspace that needs to be checked - /// true - when intentionally disconnected - /// false - disconnected due to network issues - public override bool IsDisconnectedByRunspaceProvider(Runspace runspace) - { - Connection connection = GetConnectionForRunspace(runspace); - return connection.DisconnectedIntentionally; - } - - internal void DisconnectCalled() - { - lock (_syncObject) - { - _disconnectedSessionCount++; - _connectedSessionCount--; - } - } - internal void ReconnectCalled() - { - lock (_syncObject) - { - _connectedSessionCount++; - _disconnectedSessionCount--; - } - } - - #endregion Interface Methods - - #region Private Methods - - private static void ThrowInvalidRunspaceException(Runspace runspace) - { - throw new ArgumentException(Resources.InvalidRunspaceSpecified, "runspace"); - } - - /// - /// Handle the idle timer event - /// - /// unused - /// unused - private void HandleTimerElapsed(object sender, ElapsedEventArgs e) - { - // ensure that the servicing thread is done before proceeding - if (Interlocked.CompareExchange(ref _timerFired, TimerFired, TimerReset) == TimerFired) - { - _tracer.WriteMessage("PSW ConnMgr: Another timer thread is already servicing return"); - return; - } - - if (_isDisposed || _isDisposing) - { - return; - } - - try - { - _tracer.WriteMessage("PSW ConnMgr: Timer fired"); - - _servicingThreadRelease.WaitOne(); - _timerThreadRelease.Reset(); - - _tracer.WriteMessage("PSW ConnMgr: Timer servicing started"); - - // when the timer elapses mark all unused connections to be closed - Collection toRemoveComputer = new Collection(); - - foreach (string computername in _connectionPool.Keys) - { - ConcurrentDictionary> table = _connectionPool[computername]; - - Collection toRemoveConfig = new Collection(); - foreach (string configName in table.Keys) - { - ConcurrentDictionary connections = table[configName]; - Collection toRemoveConnections = new Collection(); - - lock (_syncObject) - { - foreach (Connection connection in connections.Values) - { - // if connection has been marked idle the last - // time remove resources - if (connection.Idle) - { - toRemoveConnections.Add(connection); - } - else if (!connection.Busy) - { - connection.Idle = true; - } - } - } - - // remove all connections that need to be removed - foreach (Connection connection in toRemoveConnections) - { - Connection removeConnection; - _createdConnections--; - // remove connection from the table before attempting to - // close it. This will ensure that this connection is not - // assigned by mistake - connections.TryRemove(connection.InstanceId, out removeConnection); - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "Closing idle connection to {0}", connection.Runspace.ConnectionInfo.ComputerName)); - SubmitOperation(new CloseOperation(connection)); - } - - // if there are no more entries in connections it needs - // to be removed from the table - if (connections.Count == 0) - { - toRemoveConfig.Add(configName); - } - - } // go through all configurations for a specified computer - - foreach (string configName in toRemoveConfig) - { - ConcurrentDictionary removedConnections; - table.TryRemove(configName, out removedConnections); - } - - // if there are no more entries in table it needs to be removed - // from the connection pool - if (table.Keys.Count == 0) - { - toRemoveComputer.Add(computername); - } - - } // go through all computernames - - // remove unwanted computernames from the connectionpool - foreach (string computerName in toRemoveComputer) - { - ConcurrentDictionary> removedTable; - _connectionPool.TryRemove(computerName, out removedTable); - } - - _tracer.WriteMessage("PSW ConnMgr: Timer servicing completed"); - - Interlocked.CompareExchange(ref _timerFired, TimerReset, TimerFired); - _timerThreadRelease.Set(); - } - catch (ObjectDisposedException) - { - // Ignoring this exception - } - finally - { - CheckAndStartRequiredThreads(); - } - } - - private void TraceThreadPoolInfo(string message) - { -#if DEBUG - int maxWorkerThreads, maxCompletionPortThreads, availableWorkerThreads, availableCompletionPortThreads, - minWorkerThreads, minCompletionPortThreads; - - ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads); - ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads); - ThreadPool.GetAvailableThreads(out availableWorkerThreads, out availableCompletionPortThreads); - - _tracer.WriteMessage(string.Format(System.Globalization.CultureInfo.InvariantCulture, - "PSW ConnMgr: {0} minwt:{1} mincpt:{2} wt:{3} ct:{4} awt:{5} act:{6}", - message, minWorkerThreads, minCompletionPortThreads, maxWorkerThreads, - maxCompletionPortThreads, availableWorkerThreads, - availableCompletionPortThreads)); -#else - _tracer.WriteMessage(string.Format(System.Globalization.CultureInfo.InvariantCulture, - "PSW ConnMgr: {0}", - message)); -#endif - } - - /// - /// Checks to see if a thread is already servicing and if not starts one - /// - private void CheckAndStartConnectionServicingThread() - { - if (Interlocked.CompareExchange(ref _isServicing, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("QueueUserWorkItem Connection Servicing thread"); - ThreadPool.QueueUserWorkItem(ServiceRequests); - } - - /// - /// Checks to see if a thread is already servicing callbacks - /// and if not starts one - /// - private void CheckAndStartCallbackServicingThread() - { - if (Interlocked.CompareExchange(ref _isServicingCallbacks, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("Callback thread"); - ThreadPool.QueueUserWorkItem(ServiceCallbacks); - } - - /// - /// Method that services the callbacks for all - /// successfully assigned runspaces - /// - /// unused - private void ServiceCallbacks(object state) - { - if (_isDisposed || _isDisposing) - { - return; - } - - GetRunspaceAsyncResult result; - while (_callbacks.TryDequeue(out result)) - { - result.SetAsCompleted(null); - } - - // release servicing callbacks - Interlocked.CompareExchange(ref _isServicingCallbacks, NotServicing, Servicing); - } - - internal void AddToPendingCallback(GetRunspaceAsyncResult asyncResult) - { - _callbacks.Enqueue(asyncResult); - CheckAndStartCallbackServicingThread(); - } - - private void CheckAndStartRequiredThreads() - { - if (_isDisposed || _isDisposing) - { - return; - } - - CheckAndStartConnectionServicingThread(); - CheckAndStartDisconnectReconnectThread(); - CheckAndStartThrottleManagerThread(); - CheckAndStartCleanupThread(); - } - - /// - /// Method that contains the core servicing logic - /// - /// not used - private void ServiceRequests(object state) - { - try - { - TraceThreadPoolInfo("Starting servicing thread"); - - // ensure that idle timer thread has completed execution - // before proceeding - _timerThreadRelease.WaitOne(); - _servicingThreadRelease.Reset(); - - if (NeedToReturnFromServicing()) - { - return; - } - - Collection toRemove = new Collection(); - - // service all pending requests - foreach (RequestInfo info in _pendingRequests) - { - if (ServiceOneRequest(info)) toRemove.Add(info); - - if (NeedToReturnFromServicing()) - { - return; - } - } - - // remove serviced requests from pending queues - foreach (RequestInfo info in toRemove) - { - _pendingRequests.Remove(info); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingForcedToWaitRequestsQueueLength, - -1); - - } - - // if timer has fired, return - if (NeedToReturnFromServicing()) - { - return; - } - - // start servicing the incoming requests - RequestInfo requestInfo; - while (_inComingRequests.TryDequeue(out requestInfo)) - { - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingPendingRequestsQueueLength, - -1); - if (!ServiceOneRequest(requestInfo)) - { - // the request could not be serviced now - // add it to the pending queue - _pendingRequests.Add(requestInfo); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingForcedToWaitRequestsQueueLength); - } - - if (NeedToReturnFromServicing()) - { - return; - } - - } // service incoming requests - } - catch (ObjectDisposedException) - { - // - } - finally - { - // set servicing as complete - SafelyReturnFromServicing(); - - // Try to start the servicing thread again if there are any _inComingRequests. - // This is required to fix the race condition between CheckAndStartRequiredThreads() and ServiceRequests() methods. - // - if (_inComingRequests.Count > 0) - { - CheckAndStartConnectionServicingThread(); - } - } - } - - /// - /// Services one single request - /// - /// request to service - /// true, if successfully serviced, false otherwise - private bool ServiceOneRequest(RequestInfo requestInfo) - { - string computerName = requestInfo.ConnectionInfo.ComputerName; - string configName = requestInfo.ConnectionInfo.ShellUri; - - // if a cleanup is requested for a specified computer - // do not service the request until it is good to do so - if (_cleanupComputers.ContainsKey(computerName)) - return false; - - int existingConnections = 0; - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount); - - // check if there is a table for this computername - ConcurrentDictionary> table = - _connectionPool.GetOrAdd(computerName, - new ConcurrentDictionary>()); - - // count the number of existing connections to this computer - existingConnections = table.Keys.Sum(key => table[key].Count); - - // check if there is a connection collection for the specified configuration - ConcurrentDictionary connections = table.GetOrAdd(configName, - new ConcurrentDictionary - ()); - - bool activityResumption = false; - RunCommandsArguments args = requestInfo.AsyncResult.AsyncState as RunCommandsArguments; - - if (args != null && args.ImplementationContext != null) - activityResumption = !args.ImplementationContext.DisconnectedRunspaceInstanceId.Equals(Guid.Empty); - - // Existing connections should not be used when remote activity is resuming as they have a valid remote runspace - // Remote activity resume operation has to reconnect to the disconnected runspace - if (!activityResumption) - { - // table is available, check if there are any free runspaces - // if the collections was newly created, then this loop will - // be skipped) - foreach (Connection connection in - connections.Values.Where(connection => !connection.Busy).Where(connection => ValidateConnection(requestInfo, connection))) - { - // the assumption is connected session count and - // disconnected session count apply only to running - // pipelines. So a connection that is not busy will - // not be in the disconnected state - //Debug.Assert(connection.Runspace.RunspaceStateInfo.State != RunspaceState.Disconnected, - // "A not Busy connection should not be in the disconnected state"); - _tracer.WriteMessage("PSW ConnMgr: Assigning existing connection to request"); - AssignConnection(requestInfo, connection); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, - -1); - return true; - } - } - - if (existingConnections < _maxOutBoundConnections) - { - _tracer.WriteMessage("PSW ConnMgr: Creating new connection to service request"); - // the number of connections hasn't maxed out for this - // computer - a new connection can be created - Connection connection = CreateConnection(requestInfo, connections); - - // when the open succeeds within the specified retry attempts - // the connection will be assigned. - // submit the connection to the pending operations queue instead - // of opening it directly - SubmitOperation(new OpenOperation(connection)); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, - -1); - return true; - } - - // when the existing connections are maxed out, there - // are one of two choices - // 1. all connections are busy, in which case - // the request cannot be serviced - // 2. there are some idle connections, which - // can be closed and a new one created - - Connection potentialConnection = null; - - // find the first available idle connection - ConcurrentDictionary removeConnections=null; - - foreach (var key in table.Keys) - { - removeConnections = table[key]; - - potentialConnection = removeConnections.Values.Where(connection => !connection.Busy).FirstOrDefault(); - - if (potentialConnection != null) break; - } - - if (potentialConnection == null) - { - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, - -1); - return false; /* case 1 */ - } - - // it is possible that the potential connection in fact - // is a good match, if so return the same - // Existing connections should not be used when remote activity is resuming as they have a valid remote runspace - // Remote activity resume operation has to reconnect to the disconnected runspace - if (!activityResumption && ValidateConnection(requestInfo, potentialConnection)) - { - _tracer.WriteMessage("PSW ConnMgr: Assigning potential connection to service request"); - AssignConnection(requestInfo, potentialConnection); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, - -1); - return true; - } - - // there is a potential connection which can be closed, - // close the same and create a new one - removeConnections.TryRemove(potentialConnection.InstanceId, out potentialConnection); - - Debug.Assert(potentialConnection != null, "Trying to remove an element not in the dictionary"); - _tracer.WriteMessage("PSW ConnMgr: Closing potential connection and creating a new one to service request"); - - // Create the connection object which will be returned after - // the potential connection is closed - _createdConnections--; - Connection newConnection = CreateConnection(requestInfo, connections); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingConnectionsClosedReopenedCount); - SubmitOperation(new CloseOneAndOpenAnotherOperation(potentialConnection, newConnection)); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, - -1); - return true; - } - - private Connection CreateConnection(RequestInfo requestInfo, ConcurrentDictionary connections) - { - Connection connection = new Connection(this) - { - ConnectionInfo = requestInfo.ConnectionInfo, - RetryCount = requestInfo.RetryCount, - RetryInterval = requestInfo.RetryInterval, - RetryAttempt = 0, - AsyncResult = requestInfo.AsyncResult, - Busy = true - }; - - // the busy status should be set before adding to the - // collection so that this connection is accounted in - // the total and is not assigned to another request - connections.TryAdd(connection.InstanceId, connection); - - _createdConnections++; - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingConnectionsCreatedCount); - return connection; - } - - /// - /// Do everything required so that servicing thread can return - /// - private void SafelyReturnFromServicing() - { - if (_isDisposed || _isDisposing) - { - return; - } - - CheckAndStartRequiredThreads(); - - try - { - _tracer.WriteMessage("PSW ConnMgr: Safely returning from servicing"); - Interlocked.CompareExchange(ref _isServicing, NotServicing, Servicing); - _servicingThreadRelease.Set(); - } - catch (ObjectDisposedException) - { - // Ignoring ObjectDisposedException - } - } - - /// - /// Check if the servicing thread need to stop processing requests - /// - /// true if processing needs to stop - private bool NeedToReturnFromServicing() - { - if(_isDisposed || _isDisposing) - { - _tracer.WriteMessage("PSW ConnMgr: Returning from servicing as ConnMgr is disposed or being disposed"); - return true; - } - - // servicing thread has to return if the timer - // has been fired - if (_timerFired == TimerFired) - { - _tracer.WriteMessage("PSW ConnMgr: Returning from servicing since timer fired"); - return true; - } - - if (_createdConnections >= _maxConnectedSessions) - { - _tracer.WriteMessage("PSW ConnMgr: Setting check for runspaces disconnect flag"); - Interlocked.CompareExchange(ref _checkForDisconnect, CheckForDisconnect, DoNotCheckForDisconnect); - } - - return false; - } - - private static bool ValidateConnection(RequestInfo requestInfo, Connection connection) - { - if (connection.Runspace.RunspaceStateInfo.State != RunspaceState.Opened ) return false; - - WSManConnectionInfo connectionInfo = requestInfo.ConnectionInfo; - - // when validation is called the Connection will have a runspace populated - WSManConnectionInfo connectionInfo2 = connection.Runspace.OriginalConnectionInfo as WSManConnectionInfo; - - // Runspace.OriginalConnectionInfo is null for disconnected runspace after process crash - if(connectionInfo2 == null) - connectionInfo2 = connection.Runspace.ConnectionInfo as WSManConnectionInfo; - - if (connectionInfo2 == null) return false; - - // check URI related stuff - if (!WorkflowUtils.CompareConnectionUri(connectionInfo, connectionInfo2)) - { - return false; - } - - // compare shell URI - if (!WorkflowUtils.CompareShellUri(connectionInfo.ShellUri, connectionInfo2.ShellUri)) - { - return false; - } - - // check authentication - if (!WorkflowUtils.CompareAuthentication(connectionInfo.AuthenticationMechanism, connectionInfo2.AuthenticationMechanism)) - { - return false; - } - - // check credentials if present - if(!WorkflowUtils.CompareCredential(connectionInfo.Credential, connectionInfo2.Credential)) - { - return false; - } - - //check certificate thumbprint - if (!WorkflowUtils.CompareCertificateThumbprint(connectionInfo.CertificateThumbprint, connectionInfo2.CertificateThumbprint)) - { - return false; - } - - //check proxy settings - if (!WorkflowUtils.CompareProxySettings(connectionInfo, connectionInfo2)) - { - return false; - } - - //check rest of wsman settings - if (!WorkflowUtils.CompareOtherWSManSettings(connectionInfo, connectionInfo2)) - { - return false; - } - - // check open timeout - if (connectionInfo2.IdleTimeout < connectionInfo.IdleTimeout) - return false; - - return true; - } - - /// - /// Service the request using the available connection - /// - /// request to service - /// connection to use for servicing - private void AssignConnection(RequestInfo requestInfo, Connection connection) - { - IAsyncResult asyncResult = requestInfo.AsyncResult; - GetRunspaceAsyncResult result = asyncResult as GetRunspaceAsyncResult; - Debug.Assert(result != null, "IAsyncResult should be GetRunspaceAsyncResult"); - - connection.Busy = true; - - connection.AsyncResult = result; - result.Connection = connection; - - AddToPendingCallback(result); - } - - /// - /// Find the connection object given a runspace - /// - /// runspace whose connection - /// needs to be found - /// Connection if found, null otherwise - private Connection GetConnectionForRunspace(Runspace runspace) - { - if (runspace == null) - throw new ArgumentNullException("runspace"); - - // OriginalConnectionInfo will be null for reconnected remoterunspace after crash or shutdown - WSManConnectionInfo connectionInfo = runspace.ConnectionInfo as WSManConnectionInfo; - - if (connectionInfo == null) - { - _tracer.WriteMessage("PSW ConnMgr: Incoming connectioninfo is null"); - // throw an exception here - ThrowInvalidRunspaceException(runspace); - } - - string computername = connectionInfo.ComputerName; - string configname = connectionInfo.ShellUri; - - ConcurrentDictionary> table; - ConcurrentDictionary connections; - - if (!_connectionPool.TryGetValue(computername, out table)) - { - // invalid runspace specified, throw an exception here - _tracer.WriteMessage("PSW ConnMgr: Cannot find table for computername " + computername); - ThrowInvalidRunspaceException(runspace); - } - - if (!table.TryGetValue(configname, out connections)) - { - // invalid runspace specified, throw an exception here - _tracer.WriteMessage("PSW ConnMgr: Cannot find list for config " + configname); - ThrowInvalidRunspaceException(runspace); - } - - foreach (Connection connection in - connections.Values.Where(connection => connection.Runspace != null).Where(connection => connection.Runspace.InstanceId == runspace.InstanceId)) - { - return connection; - } - - // if this point is reached, then there is no match - // invalid runspace specified, throw an exception - _tracer.WriteMessage("PSW ConnMgr: Cannot find the actual connection object"); - ThrowInvalidRunspaceException(runspace); - - return null; - } - - /// - /// Check and start a method which will do cleanups to - /// the specified computer - /// - private void CheckAndStartCleanupThread() - { - if (Interlocked.CompareExchange(ref _isServicingCleanups, Servicing, NotServicing) != NotServicing) - return; - - TraceThreadPoolInfo("Cleanup thread"); - - ThreadPool.QueueUserWorkItem(ServiceCleanupRequests); - } - - /// - /// Worker method for servicing cleanups to requested computers - /// - /// - private void ServiceCleanupRequests(object state) - { - - foreach (string computerName in _cleanupComputers.Keys) - { - ConcurrentDictionary> table; - _connectionPool.TryGetValue(computerName, out table); - - if (table == null) - { - RaiseCallbacksAfterCleanup(computerName); - continue; - } - - foreach (string configName in table.Keys) - { - ConcurrentDictionary connections; - table.TryGetValue(configName, out connections); - - if (connections == null) continue; - lock (_syncObject) - { - foreach (CloseOperation closeOperation in - connections.Values.Where(connection => !connection.Busy).Select(connection => new CloseOperation(connection))) - { - closeOperation.OperationComplete += HandleCloseOperationComplete; - SubmitOperation(closeOperation); - } - } - } - } - - Interlocked.CompareExchange(ref _isServicingCleanups, NotServicing, Servicing); - } - - /// - /// Handles OperationComplete of close operations i.e - /// when connections to a specified computer is closed - /// - /// the CloseOperation object - /// that initiated this event - /// event parameters - private void HandleCloseOperationComplete(object sender, EventArgs e) - { - // the specified connection was successfully closed - CloseOperation closeOperation = sender as CloseOperation; - Debug.Assert(closeOperation != null, "CloseOperation object need to be returned in OperationComplete event"); - closeOperation.OperationComplete -= HandleCloseOperationComplete; - - Connection connection = closeOperation.Connection; - WSManConnectionInfo connectionInfo = connection.Runspace.ConnectionInfo as WSManConnectionInfo; - if (connectionInfo == null) return; - - string computerName = connectionInfo.ComputerName; - string configName = connectionInfo.ShellUri; - - ConcurrentDictionary> table; - _connectionPool.TryGetValue(computerName, out table); - if (table == null) return; - - ConcurrentDictionary connections; - table.TryGetValue(configName, out connections); - if (connections == null) return; - - // remove the connection from the table for this configName - Connection removedConnection; - connections.TryRemove(connection.InstanceId, out removedConnection); - if (connections.Count != 0) return; - - // if there are no more connections to this configName - // remove the table for the configName - ConcurrentDictionary removedConnections; - table.TryRemove(configName, out removedConnections); - if (table.Count != 0) return; - - // if there are no more tables for any specified configNames - // for this computerName, remove the table for the computerName - ConcurrentDictionary> removedTable; - _connectionPool.TryRemove(computerName, out removedTable); - - if (removedTable != null) - { - // if this thread is the one that removed the table, then - // make all the callbacks in this thread - RaiseCallbacksAfterCleanup(computerName); - } - } - - /// - /// Raise all the callbacks to the specified computer - /// after the requested cleanup - /// - /// computer to which the - /// callback needs to be raised - private void RaiseCallbacksAfterCleanup(string computerName) - { - bool alreadyAdded = _timerMap.Values.Contains(computerName, StringComparer.OrdinalIgnoreCase); - - // before making the callbacks fire the timer - Timer timer = new Timer(); - _timerMap.TryAdd(timer, computerName); - - timer.Elapsed += HandleCleanupWaitTimerElapsed; - - ConcurrentQueue> waitCallbacks; - if (!_cleanupComputers.TryGetValue(computerName, out waitCallbacks)) - { - _tracer.WriteMessage("PSW ConnMgr: Cannot find specified computer in _waitCallbacks dictionary: " + computerName); - return; - } - - Tuple tuple; - Collection> callbacks = new Collection>(); - - int highest = 0; - bool calledFromActivity = false; - - while (waitCallbacks.TryDequeue(out tuple)) - { - if (tuple.Item2 != null) - { - RunCommandsArguments args = tuple.Item2 as RunCommandsArguments; - - if (args != null) - { - if (highest < args.CleanupTimeout) - { - highest = args.CleanupTimeout; - } - - calledFromActivity = true; - } - } - - callbacks.Add(tuple); - } - - if (highest != 0) - { - timer.Interval = highest * 1000; - timer.AutoReset = false; - timer.Enabled = true; - - foreach (Tuple t in callbacks) - { - if (t.Item1 != null) - t.Item1(t.Item2); - } - } - else - { - if (!alreadyAdded) - { - HandleCleanupWaitTimerElapsed(timer, null); - } - else - { - string unused; - _timerMap.TryRemove(timer, out unused); - } - } - - if (!calledFromActivity) - { - foreach(var callbackTuple in callbacks) - { - if (callbackTuple.Item1 != null) - callbackTuple.Item1(callbackTuple.Item2); - } - } - } - - /// - /// Timer elapsed handler for the specified computer. Once - /// the timer elapses, new connections to the specified - /// computer will be allowed - /// - /// timer that generated the event - /// event arguments - private void HandleCleanupWaitTimerElapsed(object sender, ElapsedEventArgs e) - { - Timer timer = sender as Timer; - Debug.Assert(timer != null, "Sender cannot be null"); - - string computerName; - _timerMap.TryGetValue(timer, out computerName); - if (!string.IsNullOrEmpty(computerName)) - { - ConcurrentQueue> removedCallbacks; - _cleanupComputers.TryRemove(computerName, out removedCallbacks); - } - - // remove entry from the timer map and do cleanup - _timerMap.TryRemove(timer, out computerName); - - timer.Elapsed -= HandleCleanupWaitTimerElapsed; - timer.Dispose(); - - CheckAndStartRequiredThreads(); - } - - #endregion Private Methods - - #region Test Helper Methods - - internal bool IsConnectionPoolEmpty() - { - return _connectionPool.Keys.Count > 0; - } - - internal IEnumerable GetConnectionEnumerator() - { - return new ConnectionEnumerator(_connectionPool); - } - #endregion Test Helper Methods - - #region Dispose - - /// - /// Dispose the connection manager - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose the connection manager - /// - /// - protected void Dispose(bool isDisposing) - { - if (_isDisposed || !isDisposing) - return; - - lock (_syncObject) - { - if (_isDisposed) - return; - - _isDisposing = true; - - // set these values to servicing so that a thread is - // not started - Interlocked.CompareExchange(ref _isServicing, Servicing, NotServicing); - Interlocked.CompareExchange(ref _isServicingCallbacks, Servicing, NotServicing); - Interlocked.CompareExchange(ref _isServicingCleanups, Servicing, NotServicing); - Interlocked.CompareExchange(ref _timerFired, TimerFired, TimerReset); - - // close and clear all connections in connection pool - ClearAll(); - - // This should be done after closing all connections. - // Throttling thread executes the close operations - Interlocked.CompareExchange(ref _isOperationsServiced, Servicing, NotServicing); - - _timer.Elapsed -= HandleTimerElapsed; - _timer.Dispose(); - - var objectDisposedException = new ObjectDisposedException("ConnectionManager"); - GetRunspaceAsyncResult item; - while (_callbacks.TryDequeue(out item)) - { - item.SetAsCompleted(objectDisposedException); - } - _cleanupComputers.Clear(); - - RequestInfo info; - while (_inComingRequests.Count > 0) - { - _inComingRequests.TryDequeue(out info); - info.AsyncResult.SetAsCompleted(objectDisposedException); - } - - foreach(var reqInfo in _pendingRequests) - { - reqInfo.AsyncResult.SetAsCompleted(objectDisposedException); - } - _pendingRequests.Clear(); - - ThrottleOperation operation; - while (_pendingQueue.Count > 0) - { - _pendingQueue.TryDequeue(out operation); - } - - _timerMap.Clear(); - _servicingThreadRelease.Close(); - _timerThreadRelease.Close(); - _testHelperCloseDone.Close(); - _tracer.Dispose(); - - _isDisposed = true; - } - } - - private void AssertNotDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException("ConnectionManager"); - } - } - #endregion Dispose - - #region Throttling - - /// - /// queue of operations to be used for throttling - /// - private readonly ConcurrentQueue _pendingQueue = new ConcurrentQueue(); - - /// - /// Count of operations in progress - /// - private int _inProgressCount = 0; - - /// - /// throttle limit - includes open/close/connect/disconnect operations - /// - private readonly int _throttleLimit; - - /// - /// is the queue of operations being serviced - /// - private int _isOperationsServiced; - - /// - /// submit an operation to the queue - /// - /// operation to submit - private void SubmitOperation(ThrottleOperation operation) - { - _pendingQueue.Enqueue(operation); - CheckAndStartThrottleManagerThread(); - } - - private void CheckAndStartThrottleManagerThread() - { - // if not already set, set flag that pending operations queue is being serviced - // else another thread is already servicing so return - if (Interlocked.CompareExchange(ref _isOperationsServiced, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("Queuing user workitem Running operations in throttle queue"); - - ThreadPool.QueueUserWorkItem(StartOperationsFromQueue); - } - - /// - /// Start operations upto throttle limit from the queue - /// - /// this method is thread safe. It starts all pending - /// operations in the first calling thread. The assumption here - /// is that starting a few operations isn't expensive and so - /// the calling thread is not blocked for a long period of time - private void StartOperationsFromQueue(object state) - { - if (_isDisposed) - { - return; - } - - TraceThreadPoolInfo("Running operations in throttle queue"); - - // at this point, there will be only one thread which will be - // servicing operations from the pending operations queue - ThrottleOperation operation; - - while (_inProgressCount < _throttleLimit && _pendingQueue.TryDequeue(out operation)) - { - operation.OperationComplete += HandleOperationComplete; - Interlocked.Increment(ref _inProgressCount); - operation.DoOperation(); - } - - _tracer.WriteMessage("PSW ConnMgr: Done throttling"); - // set flag that pending operations queue is not being serviced - Interlocked.CompareExchange(ref _isOperationsServiced, NotServicing, Servicing); - } - - private void HandleOperationComplete (Object sender, EventArgs e) - { - ThrottleOperation operation = sender as ThrottleOperation; - Debug.Assert(operation != null, "OperationComplete event does not pass ThrottleOperation as sender"); - operation.OperationComplete -= HandleOperationComplete; - Interlocked.Decrement(ref _inProgressCount); - CheckAndStartRequiredThreads(); - } - - #endregion Throttling - - #region Connection Enumerator - - private class ConnectionEnumerator : IEnumerator, IEnumerable - { - private Connection _currentConnection; - private ConcurrentDictionary _currentConnections; - private ConcurrentDictionary> _currentTable; - private readonly IEnumerator _tableEnumerator; - private IEnumerator _configEnumerator; - private IEnumerator _connectionEnumerator; - - private readonly ConcurrentDictionary>> - _connectionPool; - - internal ConnectionEnumerator(ConcurrentDictionary>> connectionPool) - { - Debug.Assert(connectionPool != null, "ConnectionPool cannot be null"); - _connectionPool = connectionPool; - _tableEnumerator = _connectionPool.Keys.GetEnumerator(); - } - - /// - /// - /// - /// - public bool MoveNext() - { - while (true) - { - if (_connectionEnumerator != null && _connectionEnumerator.MoveNext()) - { - Guid key = (Guid) _connectionEnumerator.Current; - _currentConnection = null; - _currentConnections.TryGetValue(key, out _currentConnection); - if (_currentConnection != null) - return true; - else - continue; - } - - if (_configEnumerator != null && _configEnumerator.MoveNext()) - { - string configName = (string)_configEnumerator.Current; - _currentTable.TryGetValue(configName, out _currentConnections); - _connectionEnumerator = _currentConnections.Keys.GetEnumerator(); - - Debug.Assert(_connectionEnumerator != null, "Enumerator should not be null"); - - continue; - } - - if (_tableEnumerator.MoveNext()) - { - string tableName = (string)_tableEnumerator.Current; - _connectionPool.TryGetValue(tableName, out _currentTable); - _configEnumerator = _currentTable.Keys.GetEnumerator(); - Debug.Assert(_configEnumerator != null, "Enumerator should not be null"); - continue; - } - - break; - } - - return false; - } - - /// - /// - /// - public void Reset() - { - _tableEnumerator.Reset(); - _currentTable = (ConcurrentDictionary>)_tableEnumerator.Current; - _configEnumerator = _currentTable.Keys.GetEnumerator(); - _currentConnections = (ConcurrentDictionary)_configEnumerator.Current; - _connectionEnumerator = _currentConnections.Keys.GetEnumerator(); - Guid key = (Guid)_connectionEnumerator.Current; - _currentConnections.TryGetValue(key, out _currentConnection); - } - - /// - /// - /// - public object Current - { - get { return _currentConnection; } - } - - /// - /// - /// - /// - public IEnumerator GetEnumerator() - { - return this; - } - } - - #endregion Connection Enumerator - - #region Disconnect/Reconnect - - private int _isReconnectServicing; - private const long NotMarked = 0; - private const long Marked = 1; - private long _newConnectionMarked = NotMarked; - - private void CheckAndStartDisconnectReconnectThread() - { - if (_checkForDisconnect != CheckForDisconnect) return; - - if (Interlocked.CompareExchange(ref _isReconnectServicing, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("Queuing user workitem disconnect/reconnect worker"); - ThreadPool.QueueUserWorkItem(DisconnectReconnectWorker); - } - - /// - /// Disconnect and reconnect to different runspaces as necessary - /// so as to help the connection manager scale and run a large - /// number of commands on a large number of remote nodes - /// - /// unused - private void DisconnectReconnectWorker(object state) - { - if (_isDisposed || _isDisposing) - { - return; - } - - TraceThreadPoolInfo("Running disconnect/reconnect worker"); - - Interlocked.CompareExchange(ref _newConnectionMarked, NotMarked, Marked); - - while (Interlocked.CompareExchange(ref _newConnectionMarked, Marked, NotMarked) == Marked) - { - foreach (Connection connection in - (new ConnectionEnumerator(_connectionPool))) - { - if (_disconnectedSessionCount > _maxDisconnectedSessions) break; - - if (!connection.Busy) continue; - if (!connection.ReadyForDisconnect) continue; - - connection.DisconnectedIntentionally = true; - // add a disconnect operation to the queue of operations - SubmitOperation(new DisconnectOperation(connection)); - } - - // at this point all relevant connections have been disconnected - // now connect to every remote connection for a specified period of time - - // only connections which were initially marked for disconnected - // would have been disconnected - foreach (Connection connection in - (new ConnectionEnumerator(_connectionPool))) - { - if (_connectedSessionCount > _maxConnectedSessions) break; - - if (!connection.ReadyForReconnect) continue; - - connection.DisconnectedIntentionally = false; - SubmitOperation(new ReconnectOperation(connection)); - } - } - - _tracer.WriteMessage("PSW ConnMgr: Exiting disconnect reconnect worker"); - // reset flag that disconnect/reconnect thread is running - Interlocked.CompareExchange(ref _isReconnectServicing, NotServicing, Servicing); - } - - /// - /// Callback to indicate that this runspace been initiated with - /// a pipeline and can be disconnected - /// - /// runspace that needs to be marked as - /// ready for disconnect - public override void ReadyForDisconnect(Runspace runspace) - { - Connection connection = GetConnectionForRunspace(runspace); - _tracer.WriteMessage("PSW ConnMgr: Runspace marked as ready for disconnect"); - - // at this point connection will be not null - // since GetConnectionForRunspace() should have - // handled all cases and raised an exception - Debug.Assert(connection != null, "GetConnectionForRunspace has not handled all cases and raised an exception"); - - if (connection.Busy) - { - // mark the connection as ready for disconnect - // the thread which is servicing disconnect and reconnect - // will take care of disconnecting and reconnecting the same - lock (_syncObject) - { - if (!connection.Busy) - return; - - connection.ReadyForDisconnect = true; - Interlocked.CompareExchange(ref _newConnectionMarked, NotMarked, Marked); - } - - CheckAndStartRequiredThreads(); - } - } - - #endregion Disconnect/Reconnect - - #region Test Helpers - - private readonly ManualResetEvent _testHelperCloseDone = new ManualResetEvent(false); - internal void ClearAll() - { - if (_connectionPool.Count > 0) - { - _testHelperCloseDone.Reset(); - - foreach (CloseOperation operation in - from Connection connection in new ConnectionEnumerator(_connectionPool) - select new CloseOperation(connection)) - { - operation.OperationComplete += OperationComplete; - SubmitOperation(operation); - } - - _testHelperCloseDone.WaitOne(); - - _connectionPool.Clear(); - } - } - - private void OperationComplete(object sender, EventArgs e) - { - CloseOperation closeOperation = sender as CloseOperation; - Debug.Assert(closeOperation != null, "CloseOperation not returned as sender"); - closeOperation.OperationComplete -= OperationComplete; - - if (_pendingQueue.Count == 0) - { - _testHelperCloseDone.Set(); - } - } - - #endregion Test Helpers - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/Constants.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/Constants.cs deleted file mode 100644 index 2d178f83bbe..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/Constants.cs +++ /dev/null @@ -1,91 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Reflection; -using System.Collections.Generic; -using System.Globalization; - -namespace Microsoft.PowerShell.Workflow -{ - internal static class Constants - { - - internal const string AsJob = "AsJob"; - - // Remoting-related constants - internal const string ComputerName = "PSComputerName"; - internal const string Credential = "PSCredential"; - internal const string Port = "PSPort"; - internal const string UseSSL = "PSUseSSL"; - internal const string ConfigurationName = "PSConfigurationName"; - internal const string ApplicationName = "PSApplicationName"; - internal const string ConnectionURI = "PSConnectionURI"; - internal const string AllowRedirection = "PSAllowRedirection"; - internal const string SessionOption = "PSSessionOption"; - internal const string Authentication = "PSAuthentication"; - internal const string AuthenticationLevel = "PSAuthenticationLevel"; - internal const string CertificateThumbprint = "PSCertificateThumbprint"; - internal const string PSParameterCollection = "PSParameterCollection"; - internal const string PSInputCollection = "PSInputCollection"; - internal const string InputObject = "InputObject"; - internal const string PSSenderInfo = "PSSenderInfo"; - internal const string PSCurrentDirectory = "PSCurrentDirectory"; - internal const string PSSuspendOnError = "PSSuspendOnError"; - - // PowerShell-common constants - internal const string Verbose = "Verbose"; - internal const string Debug = "Debug"; - internal const string ErrorAction = "ErrorAction"; - internal const string WarningAction = "WarningAction"; - internal const string InformationAction = "InformationAction"; - internal const string ErrorVariable = "ErrorVariable"; - internal const string WarningVariable = "WarningVariable"; - internal const string InformationVariable = "InformationVariable"; - internal const string OutVariable = "OutVariable"; - internal const string OutBuffer = "OutBuffer"; - internal const string PipelineVariable = "PipelineVariable"; - - // Retry policy constants - internal const string ConnectionRetryCount = "PSConnectionRetryCount"; - internal const string ActionRetryCount = "PSActionRetryCount"; - internal const string ConnectionRetryIntervalSec = "PSConnectionRetryIntervalSec"; - internal const string ActionRetryIntervalSec = "PSActionRetryIntervalSec"; - - internal const string PrivateMetadata = "PSPrivateMetadata"; - internal const string WorkflowTakesPrivateMetadata = "WorkflowTakesPrivateMetadata"; - internal const string WorkflowJobCreationContext = "WorkflowJobCreationContext"; - - // Timers - internal const string PSRunningTime = "PSRunningTimeoutSec"; - internal const string PSElapsedTime = "PSElapsedTimeoutSec"; - internal const string Int32MaxValueDivideByThousand = "2147483"; - - internal const string ModulePath = "PSWorkflowRoot"; - internal const string JobName = "JobName"; - internal const string DefaultComputerName = "localhost"; - - // Job Metadata constants - internal const string JobMetadataInstanceId = "InstanceId"; - internal const string JobMetadataSessionId = "Id"; - internal const string JobMetadataName = "Name"; - internal const string JobMetadataCommand = "Command"; - internal const string JobMetadataStateReason = "Reason"; - internal const string JobMetadataParentName = "ParentName"; - internal const string JobMetadataParentCommand = "ParentCommand"; - internal const string JobMetadataParentInstanceId = "ParentInstanceId"; - internal const string JobMetadataParentSessionId = "ParentSessionId"; - internal const string JobMetadataLocation = "Location"; - internal const string JobMetadataStatusMessage = "StatusMessage"; - internal const string JobMetadataUserName = "UserName"; - internal const string JobMetadataPid = "ProcessId"; - internal const string JobMetadataFilterState = "State"; - - internal const string Persist = "PSPersist"; - internal const string PSRequiredModules = "PSRequiredModules"; - - internal const string PSWorkflowErrorAction = "PSWorkflowErrorAction"; - internal const int MaxAllowedPersistencePathLength = 120; - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefaultWorkflowSessionConfiguration.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefaultWorkflowSessionConfiguration.cs deleted file mode 100644 index e5bbab4a0cb..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefaultWorkflowSessionConfiguration.cs +++ /dev/null @@ -1,126 +0,0 @@ -/********************************************************************++ - * Copyright (c) Microsoft Corporation. All rights reserved. - * --********************************************************************/ - -using System.Collections.ObjectModel; -using System.IO; -using System.Management.Automation.Remoting; -using System; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Threading; -using System.Management.Automation.Tracing; -using System.Globalization; -using Microsoft.PowerShell.Commands; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// Configuration for the M3P endpoint - /// - public class PSWorkflowSessionConfiguration : PSSessionConfiguration - { - /// - /// IsWorkflowTypeEndpoint - /// - internal static bool IsWorkflowTypeEndpoint; - - #region Overrides of PSSessionConfiguration - - /// - /// - /// - /// - /// - /// - public override InitialSessionState GetInitialSessionState(PSSenderInfo senderInfo) - { - throw new NotImplementedException(); - } - - /// - /// - /// - /// - /// - /// - /// - public override InitialSessionState GetInitialSessionState(PSSessionConfigurationData sessionConfigurationData, PSSenderInfo senderInfo, string configProviderId) - { - Tracer structuredTracer = new Tracer(); - - structuredTracer.Correlate(); - - if (sessionConfigurationData == null) - throw new ArgumentNullException("sessionConfigurationData"); - - if (senderInfo == null) - throw new ArgumentNullException("senderInfo"); - - if (string.IsNullOrEmpty(configProviderId)) - throw new ArgumentNullException("configProviderId"); - - if (Interlocked.CompareExchange(ref _modulesLoaded, ModulesLoaded, ModulesNotLoaded) == ModulesNotLoaded) - { - // it is sufficient if Populate() is called the first time and - // modules are loaded once - - try - { - IsWorkflowTypeEndpoint = true; - - PSWorkflowConfigurationProvider workflowConfiguration = WorkflowJobSourceAdapter.GetInstance().GetPSWorkflowRuntime().Configuration; - if (workflowConfiguration == null) - throw new InvalidOperationException("PSWorkflowConfigurationProvider is null"); - - workflowConfiguration.Populate(sessionConfigurationData.PrivateData, configProviderId, senderInfo); - - // now get all the modules in the specified path and import the same - if (sessionConfigurationData.ModulesToImport != null) - { - foreach (var module in sessionConfigurationData.ModulesToImport) - { - ModuleSpecification moduleSpec = null; - if (ModuleSpecification.TryParse(module, out moduleSpec)) - { - var modulesToImport = new Collection { moduleSpec }; - InitialSessionState.ImportPSModule(modulesToImport); - } - else - { - InitialSessionState.ImportPSModule(new[] { Environment.ExpandEnvironmentVariables(module) }); - } - } - } - - // Start the workflow job manager, if not started, to add an event handler for zero active sessions changed events - // This is required to auto shutdown the workflow type shared process when no workflow jobs have scheduled/inprogress and when no active sessions - WorkflowJobSourceAdapter.GetInstance().GetJobManager(); - } - catch(Exception) - { - // if there is an exception in either Populate() or Importing modules - // we consider that it is not loaded - Interlocked.CompareExchange(ref _modulesLoaded, ModulesNotLoaded, ModulesLoaded); - throw; - } - } - - if (configProviderId.ToLower(CultureInfo.InvariantCulture).Equals("http://schemas.microsoft.com/powershell/microsoft.windows.servermanagerworkflows")) - { - PSSessionConfigurationData.IsServerManager = true; - } - - return InitialSessionState; - } - - private static readonly InitialSessionState InitialSessionState = - InitialSessionState.CreateRestricted(SessionCapabilities.WorkflowServer | SessionCapabilities.RemoteServer | SessionCapabilities.Language); - - private static int _modulesLoaded = ModulesNotLoaded; - private const int ModulesNotLoaded = 0; - private const int ModulesLoaded = 1; - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefinitionCache.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefinitionCache.cs deleted file mode 100644 index 4bdcbc67c71..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefinitionCache.cs +++ /dev/null @@ -1,608 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.Activities; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Tracing; -using Microsoft.PowerShell.Commands; -using System.Reflection; - -namespace Microsoft.PowerShell.Workflow -{ - internal sealed class DefinitionCache - { - - #region Privates - - internal struct WorkflowDetails - { - internal Activity ActivityTree; - internal string CompiledAssemblyPath; - internal string CompiledAssemblyName; - internal bool IsWindowsActivity; - }; - - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// Cache of workflow details. This is unbounded - /// - private readonly Dictionary _workflowDetailsCache = - new Dictionary(new CompareBasedOnInstanceId()); - - /// - /// this is the cache of compiled activities which will be bounded - /// - /// this is separate since Opalis will not use our cache - private readonly ConcurrentDictionary _cachedActivities = - new ConcurrentDictionary(new CompareBasedOnInstanceId()); - - private System.Timers.Timer _cleanupTimer; - private int activitiesCleanupIntervalMSec; - - private static readonly DefinitionCache _instance = new DefinitionCache(); - - private DefinitionCache() - { - activitiesCleanupIntervalMSec = WorkflowJobSourceAdapter.GetInstance().GetPSWorkflowRuntime().Configuration.ActivitiesCacheCleanupIntervalMSec; - - _cleanupTimer = new System.Timers.Timer(activitiesCleanupIntervalMSec); - _cleanupTimer.Elapsed += HandleCleanupTimerElapsed; - _cleanupTimer.AutoReset = false; - _cleanupTimer.Start(); - } - - private void HandleCleanupTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) - { - List activitiesToRemove = new List(); - Activity activity; - - foreach (var workflowJobDefinition in _cachedActivities.Keys) - { - if ((GetRuntimeAssemblyPath(workflowJobDefinition) == null) && - (DateTime.Now - workflowJobDefinition.LastUsedDateTime > TimeSpan.FromMilliseconds(activitiesCleanupIntervalMSec))) - { - activitiesToRemove.Add(workflowJobDefinition); - } - } - - foreach (var wfJobDefinition in activitiesToRemove) - { - _cachedActivities.TryRemove(wfJobDefinition, out activity); - } - - // Schedule the next cleanup - _cleanupTimer.Start(); - } - - /// - /// Cache size - /// - private const int _cacheSize = 1000; - - /// - /// Path to Windows folder - /// - private const string WindowsPath = "%windir%\\system32"; - - #endregion Privates - - #region Internal - - internal int CacheSize - { - get { return _cacheSize; } - } - - /// - /// Return the singleton instance. - /// - internal static DefinitionCache Instance - { - get - { - return _instance; - } - } - - internal JobDefinition GetDefinition(Guid instanceId) - { - return _workflowDetailsCache.Keys.FirstOrDefault(def => def.InstanceId == instanceId); - } - - internal string GetWorkflowXaml(WorkflowJobDefinition definition) - { - return definition.Xaml; - } - - internal string GetRuntimeAssemblyPath(WorkflowJobDefinition definition) - { - WorkflowDetails workflowDetail; - - return _workflowDetailsCache.TryGetValue(definition, out workflowDetail) ? workflowDetail.CompiledAssemblyPath : null; - } - - internal string GetRuntimeAssemblyName(WorkflowJobDefinition definition) - { - WorkflowDetails workflowDetail; - - return _workflowDetailsCache.TryGetValue(definition, out workflowDetail) ? workflowDetail.CompiledAssemblyName : null; - } - - internal bool AllowExternalActivity; - - /// - /// Compiles the activity and adds it to the cache before returning it. - /// - /// WorkflowJobDefinition defined to represent a compiled activity. - /// Activity Tree used for external activities - /// - /// indicates if the specified xaml is a Windows workflow - /// Optional, once assigned, only root Workflow will be compiled - /// Activity compiled from xaml given, or retrieved from cache. - /// Null if not found. - internal Activity CompileActivityAndSaveInCache(WorkflowJobDefinition definition, Activity activityTree, Dictionary requiredAssemblies, out bool windowsWorkflow, string rootWorkflowName = null) - { - WorkflowDetails workflowDetail = new WorkflowDetails(); - Activity activity = null; - - // initialize windows workflow to false - windowsWorkflow = false; - - string resultingCompiledAssemblyPath = definition.DependentAssemblyPath; - string modulePath = definition.ModulePath; - string[] dependentWorkflows = definition.DependentWorkflows.ToArray(); - Assembly resultingCompiledAssembly = null; - string resultingCompiledAssemblyName = null; - string xaml = definition.Xaml; - - if (activityTree != null) - { - _tracer.WriteMessage(string.Format(CultureInfo.InvariantCulture, - "DefinitionCache: Caching activity for definition with instance ID: {0}. The activity Tree is passed.", - definition.InstanceId)); - workflowDetail.ActivityTree = activityTree; - activity = activityTree; - } - else if (!String.IsNullOrEmpty(xaml)) - { - // we need to read the xaml from the specified path and compile the same - _tracer.WriteMessage(string.Format(CultureInfo.InvariantCulture, - "DefinitionCache: Caching activity for definition with instance ID: {0}. Xaml is passed", - definition.InstanceId)); - - workflowDetail.ActivityTree = null; - // check if specified workflow is a windows workflow - if (!string.IsNullOrEmpty(modulePath)) - { - string resolvedPath = Environment.ExpandEnvironmentVariables(modulePath); - string resolvedWindowsPath = Environment.ExpandEnvironmentVariables(WindowsPath); - if (resolvedPath.IndexOf(resolvedWindowsPath, StringComparison.CurrentCultureIgnoreCase) != -1) - { - windowsWorkflow = true; - } - } - - if (definition.DependentAssemblyPath == null && dependentWorkflows.Length == 0) - { - activity = ImportWorkflowCommand.ConvertXamlToActivity(xaml); - } - else - { - if (rootWorkflowName == null || (definition.Command == rootWorkflowName)) - { - activity = ImportWorkflowCommand.ConvertXamlToActivity(xaml, dependentWorkflows, - requiredAssemblies, - ref resultingCompiledAssemblyPath, - ref resultingCompiledAssembly, - ref resultingCompiledAssemblyName); - } - else - { - activity = ImportWorkflowCommand.ConvertXamlToActivity(xaml); - } - } - } - - if (activity != null) - { - workflowDetail.IsWindowsActivity = windowsWorkflow; - workflowDetail.CompiledAssemblyPath = resultingCompiledAssemblyPath; - workflowDetail.CompiledAssemblyName = resultingCompiledAssemblyName; - - lock (_syncObject) - { - WorkflowJobDefinition definitionToRemove = - _workflowDetailsCache.Keys.FirstOrDefault(item => item.InstanceId == definition.InstanceId); - if (definitionToRemove != null) - _workflowDetailsCache.Remove(definition); - - _workflowDetailsCache.Add(definition, workflowDetail); - } - - // If cached activity count reaches _cacheSize, - // Removing the cached activity at index 0 and adding the new activity to the activity cache, - // Old logic was to clear 1000 cached activities and recompiling them again when needed. - // - if (_cachedActivities.Count == _cacheSize) - { - Activity removedActivity; - _cachedActivities.TryRemove(_cachedActivities.Keys.ElementAt(0), out removedActivity); - } - - _cachedActivities.TryAdd(definition, activity); - - return activity; - } - - // we should never hit this point under normal course of operations - return null; - } - - /// - /// - /// - /// - /// - /// - internal Activity GetActivityFromCache(WorkflowJobDefinition definition, out bool windowsWorkflow) - { - Activity activity = null; - WorkflowDetails workflowDetail; - - // initialize windowsWorkflow to false - windowsWorkflow = false; - if (_workflowDetailsCache.TryGetValue(definition, out workflowDetail)) - { - // if there is a workflow activity we would have already computed whether it is a - // Windows workflow, set the same - windowsWorkflow = workflowDetail.IsWindowsActivity; - - if (workflowDetail.ActivityTree != null) - { - if (!AllowExternalActivity) - { - Debug.Assert(false, "Product code should not contain passing an activity to definition cache"); - } - - activity = workflowDetail.ActivityTree; - } - - // if a cached value is available return the same - _cachedActivities.TryGetValue(definition, out activity); - if (activity == null) - { - // if activity is not available in cache recompile using info in - // definition cache - activity = CompileActivityAndSaveInCache(definition, null, null, out windowsWorkflow); - } - - definition.LastUsedDateTime = DateTime.Now; - } - - return activity; - } - - /// - /// - /// - /// - /// - /// - internal Activity GetActivityFromCache(string xaml, out WorkflowJobDefinition workflowJobDefinition) - { - workflowJobDefinition = null; - - foreach (var definition in - _workflowDetailsCache.Keys.Where(definition => string.Equals(definition.Xaml, xaml, StringComparison.OrdinalIgnoreCase))) - { - bool windowsWorkflow; - workflowJobDefinition = definition; - return GetActivityFromCache(definition, out windowsWorkflow); - } - - return null; - } - - private readonly object _syncObject = new object(); - - #endregion Internal - - #region Test Helpers - - internal Dictionary WorkflowDetailsCache - { - get { return _workflowDetailsCache; } - } - - internal ConcurrentDictionary ActivityCache - { - get { return _cachedActivities; } - } - - internal void ClearAll() - { - _workflowDetailsCache.Clear(); - _cachedActivities.Clear(); - } - - // only called from test - internal Activity GetActivity(Guid instanceId) - { - WorkflowJobDefinition workflowJobDefinition = new WorkflowJobDefinition(typeof(WorkflowJobSourceAdapter), - string.Empty, string.Empty, - string.Empty, - WorkflowJobDefinition.EmptyEnumerable, - string.Empty, null, string.Empty) { InstanceId = instanceId }; - - bool windowsWorkflow; - Activity activity = GetActivityFromCache(workflowJobDefinition, out windowsWorkflow) ?? - CompileActivityAndSaveInCache(workflowJobDefinition, null, null, out windowsWorkflow); - - return activity; - } - - //only called from test - internal Activity GetActivity(JobDefinition definition, string xaml) - { - bool windowsWorkflow; - WorkflowJobDefinition workflowJobDefinition = new WorkflowJobDefinition(definition, string.Empty, - WorkflowJobDefinition.EmptyEnumerable, - string.Empty, - xaml); - Activity activity = GetActivityFromCache(workflowJobDefinition, out windowsWorkflow) ?? - CompileActivityAndSaveInCache(workflowJobDefinition, null, null, out windowsWorkflow); - - return activity; - } - - //only called from test - internal Activity GetActivity(JobDefinition definition, string xaml, string[] dependentWorkflows) - { - bool windowsWorkflow; - WorkflowJobDefinition workflowJobDefinition = new WorkflowJobDefinition(definition, string.Empty, - dependentWorkflows ?? WorkflowJobDefinition.EmptyEnumerable, - string.Empty, - xaml); - - Activity activity = GetActivityFromCache(workflowJobDefinition, out windowsWorkflow) ?? - CompileActivityAndSaveInCache(workflowJobDefinition, null, null, out windowsWorkflow); - - return activity; - } - - internal Activity GetActivity(JobDefinition definition, out bool windowsWorkflow) - { - WorkflowJobDefinition workflowJobDefinition = new WorkflowJobDefinition(definition); - - Activity activity = GetActivityFromCache(workflowJobDefinition, out windowsWorkflow) ?? - CompileActivityAndSaveInCache(workflowJobDefinition, null, null, out windowsWorkflow); - - return activity; - } - - /// - /// Remove a cached definition. Needed when functions are removed to release resources. - /// - /// Xaml definition to remove. - /// True if succeeded. - internal bool RemoveCachedActivity(JobDefinition definition) - { - Debug.Assert(definition != null); - - WorkflowJobDefinition workflowJobDefinition = new WorkflowJobDefinition(definition); - Activity activity; - _cachedActivities.TryRemove(workflowJobDefinition, out activity); - lock (_syncObject) - { - if (_workflowDetailsCache.ContainsKey(workflowJobDefinition)) - { - return _workflowDetailsCache.Remove(workflowJobDefinition); - } - } - return false; - } - - internal bool RemoveCachedActivity(Guid instanceId) - { - var definition = new JobDefinition(null, null, null); - definition.InstanceId = instanceId; - return RemoveCachedActivity(definition); - } - - #endregion Test Helpers - } - - internal class CompareBasedOnInstanceId : IEqualityComparer - { - #region IEqualityComparer Members - - public bool Equals(WorkflowJobDefinition x, WorkflowJobDefinition y) - { - return x.InstanceId == y.InstanceId; - } - - public int GetHashCode(WorkflowJobDefinition obj) - { - return obj.InstanceId.GetHashCode(); - } - - #endregion - } - - internal class CompareBasedOnCommand : IEqualityComparer - { - #region IEqualityComparer Members - - public bool Equals(WorkflowJobDefinition x, WorkflowJobDefinition y) - { - bool returnValue = false; - - if (String.Equals(y.ModulePath, x.ModulePath, StringComparison.OrdinalIgnoreCase)) - { - if (string.Equals(y.Command, x.Command, StringComparison.OrdinalIgnoreCase)) - { - returnValue = true; - } - } - - return returnValue; - } - - public int GetHashCode(WorkflowJobDefinition obj) - { - int hashCode = 0; - string moduleQualifiedCommand = string.Empty; - - if (!string.IsNullOrEmpty(obj.ModulePath)) - moduleQualifiedCommand += obj.ModulePath; - - if (!string.IsNullOrEmpty(obj.Command)) - moduleQualifiedCommand += obj.Command; - - hashCode = moduleQualifiedCommand.GetHashCode(); - - return hashCode; - } - - private static readonly CompareBasedOnCommand Comparer = new CompareBasedOnCommand(); - internal static bool Compare(WorkflowJobDefinition x, WorkflowJobDefinition y) - { - return Comparer.Equals(x, y); - } - - #endregion - } - - internal class WorkflowJobDefinition : JobDefinition - { - /// - /// - /// - /// - /// - /// - public WorkflowJobDefinition(Type jobSourceAdapterType, string command, string name) : base(jobSourceAdapterType, command, name) - { - IsScriptWorkflow = false; - } - - internal WorkflowJobDefinition(Type jobSourceAdapterType, string command, string name, string modulePath, - IEnumerable dependentWorkflows, string dependentAssemblyPath, Dictionary requiredAssemblies, string xaml) - : this(jobSourceAdapterType, command, name) - { - _modulePath = modulePath; - _dependentAssemblyPath = dependentAssemblyPath; - _dependentWorkflows.AddRange(dependentWorkflows); - _requiredAssemblies = requiredAssemblies; - _xaml = xaml; - _lastUsedDateTime = DateTime.Now; - } - - internal WorkflowJobDefinition(JobDefinition jobDefinition, string modulePath, - IEnumerable dependentWorkflows, string dependentAssemblyPath, string xaml) - : this(jobDefinition.JobSourceAdapterType, jobDefinition.Command, jobDefinition.Name, modulePath, dependentWorkflows, dependentAssemblyPath, null, xaml) - { - InstanceId = jobDefinition.InstanceId; - } - - internal WorkflowJobDefinition(JobDefinition jobDefinition) - :this(jobDefinition, string.Empty, EmptyEnumerable, string.Empty, string.Empty) - { - - } - - private readonly string _modulePath; - internal string ModulePath - { - get { return _modulePath; } - } - - private readonly List _dependentWorkflows = new List(); - internal List DependentWorkflows - { - get { return _dependentWorkflows; } - } - - private readonly string _dependentAssemblyPath; - internal string DependentAssemblyPath - { - get { return _dependentAssemblyPath; } - } - - private readonly Dictionary _requiredAssemblies; - internal Dictionary RequiredAssemblies - { - get { return _requiredAssemblies; } - } - - private readonly string _xaml; - internal string Xaml - { - get { return _xaml; } - } - - internal bool IsScriptWorkflow { get; set; } - - internal static IEnumerable EmptyEnumerable = new Collection(); - - private DateTime _lastUsedDateTime; - internal DateTime LastUsedDateTime - { - get { return _lastUsedDateTime; } - set { _lastUsedDateTime = value; } - } - - /// - /// Returns the same object is the specified job definition is a - /// WorkflowJobDefinition. If not creates a new one and assigns - /// the same - /// - /// job definition to check - /// WorkflowJobDefinition equivalent - internal static WorkflowJobDefinition AsWorkflowJobDefinition(JobDefinition definition) - { - return definition as WorkflowJobDefinition ?? - (DefinitionCache.Instance.GetDefinition(definition.InstanceId) as WorkflowJobDefinition ?? - new WorkflowJobDefinition(definition)); - } - - /// - /// - /// - /// - /// - public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) - { - base.GetObjectData(info, context); - - throw new NotImplementedException(); - } - - /// - /// Workflow script file name or null if no associated file. - /// - internal string WorkflowScriptFile - { - get; - set; - } - - /// - /// Full workflow script source. - /// - internal string WorkflowFullScript - { - get; - set; - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/AsyncResult.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/AsyncResult.cs deleted file mode 100644 index 2c04a68721a..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/AsyncResult.cs +++ /dev/null @@ -1,174 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Diagnostics; - using System.Threading; - - /// - /// A generic base class for IAsyncResult implementations - /// that wraps a ManualResetEvent. - /// - abstract class FileStoreAsyncResult : IAsyncResult - { - AsyncCallback callback; - object state; - bool completedSynchronously; - bool endCalled; - Exception exception; - bool isCompleted; - ManualResetEvent manualResetEvent; - object thisLock; - - protected FileStoreAsyncResult(AsyncCallback callback, object state) - { - this.callback = callback; - this.state = state; - this.thisLock = new object(); - } - - public object AsyncState - { - get - { - return state; - } - } - - public WaitHandle AsyncWaitHandle - { - get - { - if (manualResetEvent != null) - { - return manualResetEvent; - } - - lock (ThisLock) - { - if (manualResetEvent == null) - { - manualResetEvent = new ManualResetEvent(isCompleted); - } - } - - return manualResetEvent; - } - } - - public bool CompletedSynchronously - { - get - { - return completedSynchronously; - } - } - - public bool IsCompleted - { - get - { - return isCompleted; - } - } - - object ThisLock - { - get - { - return this.thisLock; - } - } - - // Call this version of complete when your asynchronous operation is complete. This will update the state - // of the operation and notify the callback. - protected void Complete(bool completedSynchronously) - { - if (isCompleted) - { - // It is a bug to call Complete twice. - throw new InvalidOperationException(Resources.AsyncResultAlreadyCompleted); - } - - this.completedSynchronously = completedSynchronously; - - if (completedSynchronously) - { - // If we completedSynchronously, then there is no chance that the manualResetEvent was created so - // we do not need to worry about a race condition. - Debug.Assert(this.manualResetEvent == null, "No ManualResetEvent should be created for a synchronous AsyncResult."); - this.isCompleted = true; - } - else - { - lock (ThisLock) - { - this.isCompleted = true; - if (this.manualResetEvent != null) - { - this.manualResetEvent.Set(); - } - } - } - - // If the callback throws, there is a bug in the callback implementation - if (callback != null) - { - callback(this); - } - } - - // Call this version of complete if you raise an exception during processing. In addition to notifying - // the callback, it will capture the exception and store it to be thrown during AsyncResult.End. - protected void Complete(bool completedSynchronously, Exception exception) - { - this.exception = exception; - Complete(completedSynchronously); - } - - // End should be called when the End function for the asynchronous operation is complete. It - // ensures the asynchronous operation is complete, and does some common validation. - protected static TAsyncResult End(IAsyncResult result) - where TAsyncResult : FileStoreAsyncResult - { - if (result == null) - { - throw new ArgumentNullException("result"); - } - - TAsyncResult asyncResult = result as TAsyncResult; - - if (asyncResult == null) - { - throw new ArgumentException(Resources.InvalidAsyncResult); - } - - if (asyncResult.endCalled) - { - throw new InvalidOperationException(Resources.AsyncResultAlreadyEnded); - } - - asyncResult.endCalled = true; - - if (!asyncResult.isCompleted) - { - asyncResult.AsyncWaitHandle.WaitOne(); - } - - if (asyncResult.manualResetEvent != null) - { - asyncResult.manualResetEvent.Close(); - } - - if (asyncResult.exception != null) - { - throw asyncResult.exception; - } - - return asyncResult; - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/FileInstanceStore.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/FileInstanceStore.cs deleted file mode 100644 index 0f95d8ec8b0..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/FileInstanceStore.cs +++ /dev/null @@ -1,225 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Runtime.DurableInstancing; - using System.Activities.DurableInstancing; - using System.Collections.Generic; - using System.Xml.Linq; - using System.IO; - using System.Xml; - using System.Management.Automation.Tracing; - using System.Activities.Persistence; - - internal class FileInstanceStore : InstanceStore - { - private static readonly Tracer StructuredTracer = new Tracer(); - - private readonly PSWorkflowFileInstanceStore _stores; - internal FileInstanceStore(PSWorkflowFileInstanceStore stores) - { - _stores = stores; - } - - protected override IAsyncResult BeginTryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, object state) - { - StructuredTracer.Correlate(); - - try - { - if (command is SaveWorkflowCommand) - { - return new TypedCompletedAsyncResult(SaveWorkflow(context, (SaveWorkflowCommand)command), callback, state); - } - else if (command is LoadWorkflowCommand) - { - return new TypedCompletedAsyncResult(LoadWorkflow(context, (LoadWorkflowCommand)command), callback, state); - } - else if (command is CreateWorkflowOwnerCommand) - { - return new TypedCompletedAsyncResult(CreateWorkflowOwner(context, (CreateWorkflowOwnerCommand)command), callback, state); - } - else if (command is DeleteWorkflowOwnerCommand) - { - return new TypedCompletedAsyncResult(DeleteWorkflowOwner(context, (DeleteWorkflowOwnerCommand)command), callback, state); - } - return new TypedCompletedAsyncResult(false, callback, state); - } - catch (Exception e) - { - return new TypedCompletedAsyncResult(e, callback, state); - } - } - - protected override bool EndTryCommand(IAsyncResult result) - { - StructuredTracer.Correlate(); - - TypedCompletedAsyncResult exceptionResult = result as TypedCompletedAsyncResult; - if (exceptionResult != null) - { - throw exceptionResult.Data; - } - return TypedCompletedAsyncResult.End(result); - } - - private bool SaveWorkflow(InstancePersistenceContext context, SaveWorkflowCommand command) - { - if (context.InstanceVersion == -1) - { - context.BindAcquiredLock(0); - } - - if (command.CompleteInstance) - { - context.CompletedInstance(); - } - else - { - string instanceType = ""; - - const string InstanceTypeXName = "{urn:schemas-microsoft-com:System.Runtime.DurableInstancing/4.0/metadata}InstanceType"; - InstanceValue instanceTypeInstanceValue; - if (command.InstanceMetadataChanges.TryGetValue(InstanceTypeXName, out instanceTypeInstanceValue)) - { - instanceType = instanceTypeInstanceValue.Value.ToString(); - } - - Dictionary fullInstanceData = new Dictionary(); - fullInstanceData.Add("instanceId", context.InstanceView.InstanceId); - fullInstanceData.Add("instanceOwnerId", context.InstanceView.InstanceOwner.InstanceOwnerId); - fullInstanceData.Add("instanceData", SerializeablePropertyBag(command.InstanceData)); - fullInstanceData.Add("instanceMetadata", SerializeInstanceMetadata(context, command)); - - foreach (KeyValuePair property in command.InstanceMetadataChanges) - { - context.WroteInstanceMetadataValue(property.Key, property.Value); - } - - context.PersistedInstance(command.InstanceData); - - _stores.Save(WorkflowStoreComponents.Definition - | WorkflowStoreComponents.Metadata - | WorkflowStoreComponents.Streams - | WorkflowStoreComponents.TerminatingError - | WorkflowStoreComponents.Timer - | WorkflowStoreComponents.ActivityState - | WorkflowStoreComponents.JobState, - fullInstanceData); - - } - - return true; - } - - private bool LoadWorkflow(InstancePersistenceContext context, LoadWorkflowCommand command) - { - if (command.AcceptUninitializedInstance) - { - return false; - } - - if (context.InstanceVersion == -1) - { - context.BindAcquiredLock(0); - } - - Guid instanceId = context.InstanceView.InstanceId; - Guid instanceOwnerId = context.InstanceView.InstanceOwner.InstanceOwnerId; - - IDictionary instanceData = null; - IDictionary instanceMetadata = null; - - Dictionary fullInstanceData = _stores.LoadWorkflowContext(); - - instanceData = this.DeserializePropertyBag((Dictionary)fullInstanceData["instanceData"]); - instanceMetadata = this.DeserializePropertyBag((Dictionary)fullInstanceData["instanceMetadata"]); - - context.LoadedInstance(InstanceState.Initialized, instanceData, instanceMetadata, null, null); - - return true; - } - - private bool CreateWorkflowOwner(InstancePersistenceContext context, CreateWorkflowOwnerCommand command) - { - Guid instanceOwnerId = Guid.NewGuid(); - context.BindInstanceOwner(instanceOwnerId, instanceOwnerId); - context.BindEvent(HasRunnableWorkflowEvent.Value); - return true; - } - - private bool DeleteWorkflowOwner(InstancePersistenceContext context, DeleteWorkflowOwnerCommand command) - { - return true; - } - - private Dictionary SerializeablePropertyBag(IDictionary source) - { - Dictionary scratch = new Dictionary(); - foreach (KeyValuePair property in source) - { - bool writeOnly = (property.Value.Options & InstanceValueOptions.WriteOnly) != 0; - - if (!writeOnly && !property.Value.IsDeletedValue) - { - scratch.Add(property.Key, property.Value.Value); - } - } - - return scratch; - } - private Dictionary SerializeInstanceMetadata(InstancePersistenceContext context, SaveWorkflowCommand command) - { - Dictionary metadata = null; - - foreach (var property in command.InstanceMetadataChanges) - { - if (!property.Value.Options.HasFlag(InstanceValueOptions.WriteOnly)) - { - if (metadata == null) - { - metadata = new Dictionary(); - // copy current metadata. note that we must rid of InstanceValue as it is not properly serializeable - foreach (var m in context.InstanceView.InstanceMetadata) - { - metadata.Add(m.Key, m.Value.Value); - } - } - - if (metadata.ContainsKey(property.Key)) - { - if (property.Value.IsDeletedValue) metadata.Remove(property.Key); - else metadata[property.Key] = property.Value.Value; - } - else - { - if (!property.Value.IsDeletedValue) metadata.Add(property.Key, property.Value.Value); - } - } - } - - if (metadata == null) - metadata = new Dictionary(); - - return metadata; - } - - private IDictionary DeserializePropertyBag(Dictionary source) - { - Dictionary destination = new Dictionary(); - - foreach (KeyValuePair property in source) - { - destination.Add(property.Key, new InstanceValue(property.Value)); - } - - return destination; - } - - - } -} - \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStoreCryptography.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStoreCryptography.cs deleted file mode 100644 index 0f7bafaf63f..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStoreCryptography.cs +++ /dev/null @@ -1,68 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.IO; - using System.Security.Cryptography; - using System.Management.Automation.Tracing; - - /// - /// This class implements the encrypt and decrypt functionality. - /// - internal class InstanceStoreCryptography - { - /// - /// Tracer initialization. - /// - private static readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// The additional entry for security 'POWERSHELLWORKFLOW' - /// - private static byte[] s_additionalEntropy = { (byte)'P', (byte)'O', (byte)'W', (byte)'E', (byte)'R', (byte)'S', (byte)'H', (byte)'E', (byte)'L', (byte)'L', (byte)'W', (byte)'O', (byte)'R', (byte)'K', (byte)'F', (byte)'L', (byte)'O', (byte)'W' }; - - /// - /// Protect the data. - /// - /// The input data for encryption. - /// Returns encrypted data. - internal static byte[] Protect(byte[] data) - { - try - { - // Encrypt the data using DataProtectionScope.CurrentUser. The result can be decrypted - // only by the same current user. - return ProtectedData.Protect(data, s_additionalEntropy, DataProtectionScope.CurrentUser); - } - catch (CryptographicException e) - { - Tracer.TraceException(e); - - throw e; - } - } - - /// - /// Unprotect data the encrypted data. - /// - /// Encrypted data. - /// Returns decrypted data. - internal static byte[] Unprotect(byte[] data) - { - try - { - return ProtectedData.Unprotect(data, s_additionalEntropy, DataProtectionScope.CurrentUser); - } - catch (CryptographicException e) - { - Tracer.TraceException(e); - - throw e; - } - - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStorePermission.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStorePermission.cs deleted file mode 100644 index 0d29a001b07..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStorePermission.cs +++ /dev/null @@ -1,44 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.IO; - using System.Security.Cryptography; - using System.Management.Automation.Tracing; - using System.Security.AccessControl; - using System.Security.Principal; - - /// - /// This class implements the encrypt and decrypt functionality. - /// - internal class InstanceStorePermission - { - internal static void SetDirectoryPermissions(string folderName) - { - string account = WindowsIdentity.GetCurrent().Name; - RemoveInheritablePermissions(folderName); - AddDirectorySecurity(folderName, account, FileSystemRights.Modify, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow); - } - - private static void AddDirectorySecurity(string folderName, string account, FileSystemRights rights, InheritanceFlags inheritance, PropagationFlags propagation, AccessControlType controlType) - { - DirectoryInfo info = new DirectoryInfo(folderName); - DirectorySecurity dSecurity = info.GetAccessControl(); - dSecurity.AddAccessRule(new FileSystemAccessRule(account, rights, inheritance, propagation, controlType)); - info.SetAccessControl(dSecurity); - } - - private static void RemoveInheritablePermissions(string folderName) - { - DirectoryInfo info = new DirectoryInfo(folderName); - DirectorySecurity dSecurity = info.GetAccessControl(); - const bool IsProtected = true; - const bool PreserveInheritance = false; - dSecurity.SetAccessRuleProtection(IsProtected, PreserveInheritance); - info.SetAccessControl(dSecurity); - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedAsyncResult.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedAsyncResult.cs deleted file mode 100644 index dd3a3d2e1e0..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedAsyncResult.cs +++ /dev/null @@ -1,41 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Diagnostics; - using System.Threading; - - /// - /// A strongly typed AsyncResult. - /// - /// - abstract class TypedAsyncResult : FileStoreAsyncResult - { - T data; - - protected TypedAsyncResult(AsyncCallback callback, object state) - : base(callback, state) - { - } - - public T Data - { - get { return data; } - } - - protected void Complete(T data, bool completedSynchronously) - { - this.data = data; - Complete(completedSynchronously); - } - - public static T End(IAsyncResult result) - { - TypedAsyncResult typedResult = FileStoreAsyncResult.End>(result); - return typedResult.Data; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedCompletedAsyncResult.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedCompletedAsyncResult.cs deleted file mode 100644 index 92ce3cde662..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedCompletedAsyncResult.cs +++ /dev/null @@ -1,35 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Diagnostics; - using System.Threading; - - /// - /// A strongly typed AsyncResult that completes as soon as it is instantiated. - /// - /// - class TypedCompletedAsyncResult : TypedAsyncResult - { - public TypedCompletedAsyncResult(T data, AsyncCallback callback, object state) - : base(callback, state) - { - Complete(data, true); - } - - public new static T End(IAsyncResult result) - { - TypedCompletedAsyncResult completedResult = result as TypedCompletedAsyncResult; - - if (completedResult == null) - { - throw new ArgumentException(Resources.InvalidAsyncResult); - } - - return TypedAsyncResult.End(completedResult); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/WorkflowAdditionalStores.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/WorkflowAdditionalStores.cs deleted file mode 100644 index 70b065a4b67..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/WorkflowAdditionalStores.cs +++ /dev/null @@ -1,2039 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Diagnostics; -using System.IO.Compression; -using System.Management.Automation.Remoting; - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.IO; - using System.Management.Automation; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Runtime.Serialization; - using System.Xml.Linq; - using System.Runtime.DurableInstancing; - using System.Globalization; - using System.Management.Automation.Tracing; - using System.Activities.DurableInstancing; - using System.Activities.Persistence; - using System.Reflection; - using Microsoft.PowerShell.Commands; - using System.Text; - - internal class PersistenceVersion - { - private readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private bool saved = false; - private object syncLock = new object(); - - internal Version StoreVersion { get; set; } - internal Version CLRVersion { get; set; } - internal bool EnableEncryption { get; set; } - internal bool EnableCompression { get; set; } - - internal PersistenceVersion(bool enableEncryption, bool enableCompression) - { - StoreVersion = new Version(1, 0); - CLRVersion = Environment.Version; - EnableEncryption = enableEncryption; - EnableCompression = enableCompression; - } - - internal void save(string versionFileName) - { - if (saved) return; - - lock (syncLock) - { - - if (saved) return; - saved = true; - - if (File.Exists(versionFileName)) - return; - - XElement versionXml = new XElement("PersistenceVersion", - new XElement("StoreVersion", StoreVersion), - new XElement("CLRVersion", CLRVersion), - new XElement("EnableEncryption", EnableEncryption), - new XElement("EnableCompression", EnableCompression)); - - versionXml.Save(versionFileName); - } - } - - internal void load(string versionFileName) - { - try - { - if (!File.Exists(versionFileName)) - return; // use the value provided in the constructor - - XElement versionXml = XElement.Load(versionFileName); - - if (versionXml.Name.LocalName.Equals("PersistenceVersion", StringComparison.OrdinalIgnoreCase)) - { - foreach (XElement x in versionXml.Elements()) - { - if (x.Name.LocalName.Equals("StoreVersion", StringComparison.OrdinalIgnoreCase)) - { - this.StoreVersion = new Version(x.Value); - } - else if (x.Name.LocalName.Equals("CLRVersion", StringComparison.OrdinalIgnoreCase)) - { - this.CLRVersion = new Version(x.Value); - } - else if (x.Name.LocalName.Equals("EnableEncryption", StringComparison.OrdinalIgnoreCase)) - { - this.EnableEncryption = Convert.ToBoolean(x.Value, CultureInfo.InvariantCulture); - } - else if (x.Name.LocalName.Equals("EnableCompression", StringComparison.OrdinalIgnoreCase)) - { - this.EnableCompression = Convert.ToBoolean(x.Value, CultureInfo.InvariantCulture); - } - } - } - } - catch (Exception e) - { - //default values will be used - Tracer.WriteMessage(string.Format(CultureInfo.InvariantCulture, "Exception while reading or parsing the version file: {0}", versionFileName)); - Tracer.TraceException(e); - } - } - } - - - - internal enum InternalStoreComponents - { - Streams = 0, - Metadata = 1, - Definition = 2, - Timer = 3, - JobState = 4, - TerminatingError = 5, - Context = 6, - ActivityState = 7, - } - - /// - /// This class implements the functionality for storing the streams data on to the disk. - /// - public class PSWorkflowFileInstanceStore : PSWorkflowInstanceStore - { - # region private variables - - private readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static readonly Tracer etwTracer = new Tracer(); - - private readonly string Streams = "Str"; - private readonly string Error = "Err"; - private readonly string Metadatas = "Meta"; - private readonly string Definition = "Def"; - private readonly string WorkflowState = "Stat"; - - private readonly string Version_xml = "V.xml"; - - private readonly string InputStream_xml = "IS.xml"; - private readonly string OutputStream_xml = "OS.xml"; - private readonly string ErrorStream_xml = "ES.xml"; - private readonly string WarningStream_xml = "WS.xml"; - private readonly string VerboseStream_xml = "VS.xml"; - private readonly string ProgressStream_xml = "PS.xml"; - private readonly string DebugStream_xml = "DS.xml"; - private readonly string InformationStream_xml = "INFS.xml"; - - private readonly string ErrorException_xml = "EE.xml"; - - private readonly string Input_xml = "I.xml"; - private readonly string PSWorkflowCommonParameters_xml = "UI.xml"; - private readonly string JobMetadata_xml = "JM.xml"; - private readonly string PrivateMetadata_xml = "PM.xml"; - private readonly string Timer_xml = "TI.xml"; - - private readonly string WorkflowInstanceState_xml = "WS.xml"; - - private readonly string WorkflowDefinition_xaml = "WD.xaml"; - private readonly string RuntimeAssembly_dll = "RA.dll"; - private readonly string RequiredAssemblies_xml = "RA.xml"; - - private readonly string State_xml = "S.xml"; - - private readonly string ActivityState_xml = "AS.xml"; - - private readonly object _syncLock = new object(); - - private bool firstTimeStoringDefinition; - - private PSWorkflowConfigurationProvider _configuration; - - private Dictionary SavedComponentLengths; - - private long writtenTotalBytes = 0; - - # endregion - - // For testing purpose ONLY - internal static bool TestMode = false; - // For testing purpose ONLY - internal static long ObjectCounter = 0; - - /// - /// Returns all Workflow guids. - /// - /// - public static IEnumerable GetAllWorkflowInstanceIds() - { - PSWorkflowConfigurationProvider _configuration = (PSWorkflowConfigurationProvider) PSWorkflowRuntime.Instance.Configuration; - DirectoryInfo dir = new DirectoryInfo(_configuration.InstanceStorePath); - if (dir.Exists) - { - foreach (DirectoryInfo childDir in dir.GetDirectories()) - { - Guid id; - if (Guid.TryParse(childDir.Name, out id)) - yield return new PSWorkflowId(id); - } - } - } - - - /// - /// PSWorkflowFileInstanceStore ctor - /// - /// - /// - public PSWorkflowFileInstanceStore(PSWorkflowConfigurationProvider configuration, PSWorkflowInstance instance) - : base(instance) - { - if (configuration == null) - throw new ArgumentNullException("configuration"); - - if (TestMode) - { - System.Threading.Interlocked.Increment(ref ObjectCounter); - } - - _configuration = configuration; - firstTimeStoringDefinition = true; - SavedComponentLengths = new Dictionary(); - - bool enableCompression = true; - _disablePersistenceLimits = true; - if (PSSessionConfigurationData.IsServerManager) - { - enableCompression = false; - _disablePersistenceLimits = false; - } - - _version = new PersistenceVersion(_configuration.PersistWithEncryption, enableCompression); - _version.load(Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Version_xml)); - } - - private void SaveVersionFile() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString()); - EnsureInstancePath(storePath); - - _version.save(Path.Combine(storePath, Version_xml)); - } - - private PersistenceVersion _version; - - /// - /// CreateInstanceStore - /// - /// - public override InstanceStore CreateInstanceStore() - { - return new FileInstanceStore(this); - } - - /// - /// CreatePersistenceIOParticipant - /// - /// - public override PersistenceIOParticipant CreatePersistenceIOParticipant() - { - return null; - } - - # region Serialization - - /// - /// To be called from test code only. - /// - /// - internal void CallDoSave(IEnumerable components) - { - DoSave(components); - } - - /// - /// DoSave - /// - /// - protected override void DoSave(IEnumerable components) - { - // no persistence is allowed if the serialization error has occured. - if (serializationErrorHasOccured) - return; - - this.SaveVersionFile(); - - if (_disablePersistenceLimits) - { - DoSave2(components); - return; - } - - lock (_syncLock) - { - long contextLengthNew = 0; - long streamLengthNew = 0; - long metadataLengthNew = 0; - long definitionLengthNew = 0; - long timerLengthNew = 0; - long jobStateLengthNew = 0; - long errorExceptionLengthNew = 0; - long activityStateLengthNew = 0; - - long contextLengthOld = 0; - long streamLengthOld = 0; - long metadataLengthOld = 0; - long definitionLengthOld = 0; - long timerLengthOld = 0; - long jobStateLengthOld = 0; - long errorExceptionLengthOld = 0; - long activityStateLengthOld = 0; - - foreach (object component in components) - { - Type componentType = component.GetType(); - - if(componentType == typeof(Dictionary)) - { - contextLengthNew = this.LoadSerializedContext(component); - contextLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Context) ? - this.SavedComponentLengths[InternalStoreComponents.Context] : - this.GetSavedContextLength(); - - this.SaveSerializedContext(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Context)) - SavedComponentLengths.Remove(InternalStoreComponents.Context); - this.SavedComponentLengths.Add(InternalStoreComponents.Context, contextLengthNew); - } - else if(componentType == typeof(PowerShellStreams)) - { - streamLengthNew = this.LoadSerializedStreamData(component); - streamLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Streams) ? - this.SavedComponentLengths[InternalStoreComponents.Streams] : - this.GetSavedStreamDataLength(); - - this.SaveSerializedStreamData(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Streams)) - SavedComponentLengths.Remove(InternalStoreComponents.Streams); - this.SavedComponentLengths.Add(InternalStoreComponents.Streams, streamLengthNew); - } - else if(componentType == typeof(PSWorkflowContext)) - { - metadataLengthNew = this.LoadSerializedMetadata(component); - metadataLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Metadata) ? - this.SavedComponentLengths[InternalStoreComponents.Metadata] : - this.GetSavedMetadataLength(); - - this.SaveSerializedMetadata(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Metadata)) - SavedComponentLengths.Remove(InternalStoreComponents.Metadata); - this.SavedComponentLengths.Add(InternalStoreComponents.Metadata, metadataLengthNew); - } - else if(componentType == typeof(PSWorkflowDefinition)) - { - if (firstTimeStoringDefinition) - { - definitionLengthNew = this.LoadSerializedDefinition(component); - definitionLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Definition) ? - this.SavedComponentLengths[InternalStoreComponents.Definition] : - this.GetSavedDefinitionLength(); - - this.SaveSerializedDefinition(component); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Definition)) - SavedComponentLengths.Remove(InternalStoreComponents.Definition); - this.SavedComponentLengths.Add(InternalStoreComponents.Definition, definitionLengthNew); - } - } - else if(componentType == typeof(PSWorkflowTimer)) - { - - timerLengthNew = this.LoadSerializedTimer(component as PSWorkflowTimer); - timerLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Timer) ? - this.SavedComponentLengths[InternalStoreComponents.Timer] : - this.GetSavedTimerLength(); - - this.SaveSerializedTimer(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Timer)) - SavedComponentLengths.Remove(InternalStoreComponents.Timer); - this.SavedComponentLengths.Add(InternalStoreComponents.Timer, timerLengthNew); - } - else if(componentType == typeof(JobState)) - { - jobStateLengthNew = this.LoadSerializedJobState(component); - jobStateLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.JobState) ? - this.SavedComponentLengths[InternalStoreComponents.JobState] : - this.GetSavedJobStateLength(); - - this.SaveSerializedJobState(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.JobState)) - SavedComponentLengths.Remove(InternalStoreComponents.JobState); - this.SavedComponentLengths.Add(InternalStoreComponents.JobState, jobStateLengthNew); - } - else if (component is Exception) - { - errorExceptionLengthNew = this.LoadSerializedErrorException(component); - errorExceptionLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.TerminatingError) ? - this.SavedComponentLengths[InternalStoreComponents.TerminatingError] : - this.GetSavedErrorExceptionLength(); - - this.SaveSerializedErrorException(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.TerminatingError)) - SavedComponentLengths.Remove(InternalStoreComponents.TerminatingError); - this.SavedComponentLengths.Add(InternalStoreComponents.TerminatingError, errorExceptionLengthNew); - } - else if (componentType == typeof(PSWorkflowRemoteActivityState)) - { - activityStateLengthNew = this.LoadSerializedActivityState(component as PSWorkflowRemoteActivityState); - activityStateLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.ActivityState) ? - this.SavedComponentLengths[InternalStoreComponents.ActivityState] : - this.GetSavedActivityStateLength(); - - this.SaveSerializedActivityState(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.ActivityState)) - SavedComponentLengths.Remove(InternalStoreComponents.ActivityState); - this.SavedComponentLengths.Add(InternalStoreComponents.ActivityState, activityStateLengthNew); - } - } - - - // Now verify if the data can be in allowed max persistence limit - long oldValue = ( - contextLengthOld + - streamLengthOld + - metadataLengthOld + - definitionLengthOld + - timerLengthOld + - jobStateLengthOld + - activityStateLengthOld + - errorExceptionLengthOld - ); - - long newValue = ( - contextLengthNew + - streamLengthNew + - metadataLengthNew + - definitionLengthNew + - timerLengthNew + - jobStateLengthNew + - activityStateLengthNew + - errorExceptionLengthNew - ); - - bool allowed = CheckMaxPersistenceSize(oldValue, newValue); - - // if not allowed then write the warning message and continue execution. - if (allowed == false) - { - this.WriteWarning(Resources.PersistenceSizeReached); - etwTracer.PersistenceStoreMaxSizeReached(); - } - } - } - - /// - /// Delete - /// - protected override void DoDelete() - { - lock (_syncLock) - { - InternalDelete(); - } - } - - private void InternalDelete() - { - string instanceFolder = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString()); - if (Directory.Exists(instanceFolder)) - { - try - { - long size = this.GetDirectoryLength(new DirectoryInfo(instanceFolder)); - Directory.Delete(instanceFolder, true); - ReducePersistenceSize(size); - } - catch (IOException e) - { - RetryDelete(e, instanceFolder); - } - catch (UnauthorizedAccessException e) - { - RetryDelete(e, instanceFolder); - } - } - } - - // RetryDelete throws exception if delete fails again, that exception causes the remove job to fail - // - private void RetryDelete(Exception e, string instanceFolder) - { - Tracer.TraceException(e); - Tracer.WriteMessage("Trying to delete one more time."); - - try - { - Directory.Delete(instanceFolder, true); - } - catch (Exception exc) - { - Tracer.TraceException(exc); - - // eating the exceptions as remove job should not throw any errors for deleting the store files. - } - } - - # region Stream - - private ArraySegment serializedInputStreamData; - private ArraySegment serializedOutputStreamData; - private ArraySegment serializedErrorStreamData; - private ArraySegment serializedWarningStreamData; - private ArraySegment serializedVerboseStreamData; - private ArraySegment serializedProgressStreamData; - private ArraySegment serializedDebugStreamData; - private ArraySegment serializedInformationStreamData; - - private long LoadSerializedStreamData(object data) - { - Debug.Assert(data.GetType() == typeof (PowerShellStreams), "The data should be of type workflow stream"); - PowerShellStreams streams = (PowerShellStreams) data; - - this.serializedInputStreamData = Encrypt(SerializeObject(streams.InputStream)); - this.serializedOutputStreamData = Encrypt(SerializeObject(streams.OutputStream)); - this.serializedErrorStreamData = Encrypt(SerializeObject(streams.ErrorStream)); - this.serializedWarningStreamData = Encrypt(SerializeObject(streams.WarningStream)); - this.serializedVerboseStreamData = Encrypt(SerializeObject(streams.VerboseStream)); - this.serializedProgressStreamData = Encrypt(SerializeObject(streams.ProgressStream)); - this.serializedDebugStreamData = Encrypt(SerializeObject(streams.DebugStream)); - this.serializedInformationStreamData = Encrypt(SerializeObject(streams.InformationStream)); - - long size = ( - this.serializedInputStreamData.Count + - this.serializedOutputStreamData.Count + - this.serializedErrorStreamData.Count + - this.serializedWarningStreamData.Count + - this.serializedVerboseStreamData.Count + - this.serializedProgressStreamData.Count + - this.serializedDebugStreamData.Count + - this.serializedInformationStreamData.Count - ); - - return size; - } - - private void EnsureInstancePath(string storePath) - { - if (Directory.Exists(storePath) == false) - { - try - { - Directory.CreateDirectory(storePath); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - InstanceStorePermission.SetDirectoryPermissions(storePath); - } - } - - private void SaveSerializedStreamData() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Streams); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedInputStreamData, Path.Combine(storePath, InputStream_xml)); - SaveToFile(this.serializedOutputStreamData, Path.Combine(storePath, OutputStream_xml)); - SaveToFile(this.serializedErrorStreamData, Path.Combine(storePath, ErrorStream_xml)); - SaveToFile(this.serializedWarningStreamData, Path.Combine(storePath, WarningStream_xml)); - SaveToFile(this.serializedVerboseStreamData, Path.Combine(storePath, VerboseStream_xml)); - SaveToFile(this.serializedProgressStreamData, Path.Combine(storePath, ProgressStream_xml)); - SaveToFile(this.serializedDebugStreamData, Path.Combine(storePath, DebugStream_xml)); - SaveToFile(this.serializedInformationStreamData, Path.Combine(storePath, InformationStream_xml)); - } - - private long GetSavedStreamDataLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Streams); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, InputStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, OutputStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, ErrorStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, WarningStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, VerboseStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, ProgressStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, DebugStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, InformationStream_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - # region PSWorkflowContext - - private ArraySegment serializedWorkflowParameters; - private ArraySegment serializedPSWorkflowCommonParameters; - private ArraySegment serializedJobMetadata; - private ArraySegment serializedPrivateMetadata; - - private long LoadSerializedMetadata(object data) - { - Debug.Assert(data.GetType() == typeof (PSWorkflowContext), "The data should be of type workflow metadata"); - PSWorkflowContext metadata = (PSWorkflowContext) data; - - this.serializedWorkflowParameters = Encrypt(SerializeObject(metadata.WorkflowParameters)); - this.serializedPSWorkflowCommonParameters = Encrypt(SerializeObject(metadata.PSWorkflowCommonParameters)); - this.serializedJobMetadata = Encrypt(SerializeObject(metadata.JobMetadata)); - this.serializedPrivateMetadata = Encrypt(SerializeObject(metadata.PrivateMetadata)); - - long size = ( - this.serializedWorkflowParameters.Count + - this.serializedPSWorkflowCommonParameters.Count + - this.serializedJobMetadata.Count + - this.serializedPrivateMetadata.Count - ); - - return size; - - } - - private void SaveSerializedMetadata() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedWorkflowParameters, Path.Combine(storePath, Input_xml)); - SaveToFile(this.serializedPSWorkflowCommonParameters,Path.Combine(storePath, PSWorkflowCommonParameters_xml)); - SaveToFile(this.serializedJobMetadata, Path.Combine(storePath, JobMetadata_xml)); - SaveToFile(this.serializedPrivateMetadata, Path.Combine(storePath, PrivateMetadata_xml)); - } - - private long GetSavedMetadataLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(),Metadatas); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, Input_xml)); - size += (info.Exists) ? info.Length : 0; - info = new FileInfo(Path.Combine(storePath, PSWorkflowCommonParameters_xml)); - size += (info.Exists) ? info.Length : 0; - info = new FileInfo(Path.Combine(storePath, JobMetadata_xml)); - size += (info.Exists) ? info.Length : 0; - info = new FileInfo(Path.Combine(storePath, PrivateMetadata_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - # region Error Exception - - private ArraySegment serializedErrorException; - - private long LoadSerializedErrorException(object data) - { - this.serializedErrorException = Encrypt(SerializeObject(data)); - - long size = this.serializedErrorException.Count; - - return size; - } - - private void SaveSerializedErrorException() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Error); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedErrorException, Path.Combine(storePath, ErrorException_xml)); - - } - - private long GetSavedErrorExceptionLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Error); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, ErrorException_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - #region Timer - - private ArraySegment serializedTimerData; - - private long LoadSerializedTimer(PSWorkflowTimer data) - { - this.serializedTimerData = Encrypt(SerializeObject(data.GetSerializedData())); - - long size = this.serializedTimerData.Count; - - return size; - } - - private void SaveSerializedTimer() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedTimerData, Path.Combine(storePath, Timer_xml)); - } - - private long GetSavedTimerLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, Timer_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - #endregion - - # region PSWorkflowDefinition - private ArraySegment serializedRequiredAssemblies; - private long LoadSerializedDefinition(object data) - { - Debug.Assert(data.GetType() == typeof (PSWorkflowDefinition), "The data should be of type workflow definition"); - PSWorkflowDefinition definition = (PSWorkflowDefinition) data; - - string WorkflowXaml = definition.WorkflowXaml; - string runtimeAssemblyPath = definition.RuntimeAssemblyPath; - - long size = WorkflowXaml == null ? 0 : WorkflowXaml.Length; - - if (!string.IsNullOrEmpty(runtimeAssemblyPath) && File.Exists(runtimeAssemblyPath)) - { - FileInfo file = new FileInfo(runtimeAssemblyPath); - size += file.Length; - } - - if(definition.RequiredAssemblies != null) - { - this.serializedRequiredAssemblies = Encrypt(SerializeObject(definition.RequiredAssemblies)); - size += this.serializedRequiredAssemblies.Count; - } - - return size; - } - - private void SaveSerializedDefinition(object data) - { - Debug.Assert(data.GetType() == typeof (PSWorkflowDefinition), "The data should be of type workflow definition"); - PSWorkflowDefinition definition = (PSWorkflowDefinition) data; - - if (firstTimeStoringDefinition) - { - firstTimeStoringDefinition = false; - - string WorkflowXaml = definition.WorkflowXaml; - string runtimeAssemblyPath = definition.RuntimeAssemblyPath; - - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Definition); - EnsureInstancePath(storePath); - - try - { - File.WriteAllText(Path.Combine(storePath, WorkflowDefinition_xaml), WorkflowXaml); - - if (!string.IsNullOrEmpty(runtimeAssemblyPath) && File.Exists(runtimeAssemblyPath)) - { - string radllPath = Path.Combine(storePath, RuntimeAssembly_dll); - - // Copy if file locations are different. - // Both paths are same when a suspended job from another console is resumed and end persistence happened. - if (!String.Equals(radllPath, runtimeAssemblyPath, StringComparison.OrdinalIgnoreCase)) - { - File.Copy(runtimeAssemblyPath, Path.Combine(storePath, RuntimeAssembly_dll)); - } - } - - if (definition.RequiredAssemblies != null) - { - if(_disablePersistenceLimits) - { - SerializeAndSaveToFile(definition.RequiredAssemblies, Path.Combine(storePath, RequiredAssemblies_xml)); - } - else - { - SaveToFile(this.serializedRequiredAssemblies, Path.Combine(storePath, RequiredAssemblies_xml)); - } - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - } - - private long GetSavedDefinitionLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Definition); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, WorkflowDefinition_xaml)); - size += (info.Exists) ? info.Length : 0; - info = new FileInfo(Path.Combine(storePath, RuntimeAssembly_dll)); - size += (info.Exists) ? info.Length : 0; - info = new FileInfo(Path.Combine(storePath, RequiredAssemblies_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - # region Job State - - private ArraySegment serializedJobState; - - private long LoadSerializedJobState(object data) - { - this.serializedJobState = Encrypt(SerializeObject(data)); - - long size = this.serializedJobState.Count; - - return size; - } - - private void SaveSerializedJobState() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - EnsureInstancePath(storePath); - - Debug.Assert(PSWorkflowInstance.PSWorkflowJob != null, - "When SerializeWorkflowJobStateToStore is called, the _job instance needs to be populated"); - SaveToFile(this.serializedJobState, Path.Combine(storePath, WorkflowInstanceState_xml)); - } - - private long GetSavedJobStateLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, WorkflowInstanceState_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - #region ActivityState - private ArraySegment serializedActivityState; - private long LoadSerializedActivityState(PSWorkflowRemoteActivityState data) - { - this.serializedActivityState = Encrypt(SerializeObject(data.GetSerializedData())); - return this.serializedActivityState.Count; - } - - private void SaveSerializedActivityState() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedActivityState, Path.Combine(storePath, ActivityState_xml)); - } - - private long GetSavedActivityStateLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, ActivityState_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - #endregion ActivityState - - - # region WorkflowContext - - private ArraySegment serializedContext; - - private long LoadSerializedContext(object data) - { - this.serializedContext = Encrypt(SerializeObject(data)); - - long size = this.serializedContext.Count; - - return size; - } - - private void SaveSerializedContext() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), WorkflowState); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedContext, Path.Combine(storePath, State_xml)); - } - - private long GetSavedContextLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), WorkflowState); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, State_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - - # endregion - - # region Deserialization - - private PowerShellStreams DeserializeWorkflowStreamsFromStore() - { - PowerShellStreams stream = new PowerShellStreams(null); - - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Streams); - - if (Directory.Exists(storePath)) - { - if (_disablePersistenceLimits) - { - stream.InputStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, InputStream_xml)); - stream.OutputStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, OutputStream_xml)); - stream.ErrorStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, ErrorStream_xml)); - stream.WarningStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, WarningStream_xml)); - stream.VerboseStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, VerboseStream_xml)); - stream.ProgressStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, ProgressStream_xml)); - stream.DebugStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, DebugStream_xml)); - stream.InformationStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, InformationStream_xml)); - } - else - { - stream.InputStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, InputStream_xml)))); - stream.OutputStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, OutputStream_xml)))); - stream.ErrorStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, ErrorStream_xml)))); - stream.WarningStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, WarningStream_xml)))); - stream.VerboseStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, VerboseStream_xml)))); - stream.ProgressStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, ProgressStream_xml)))); - stream.DebugStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, DebugStream_xml)))); - stream.InformationStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, InformationStream_xml)))); - } - } - - return stream; - } - - private Exception DeserializeWorkflowErrorExceptionFromStore() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Error); - - if (Directory.Exists(storePath) == false) - return null; - - return _disablePersistenceLimits - ? (Exception)LoadFromFileAndDeserialize(Path.Combine(storePath, ErrorException_xml)) - : (Exception) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, ErrorException_xml)))); - } - - private object DeserializeWorkflowTimerFromStore() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - if (Directory.Exists(storePath) == false) - return null; - - return _disablePersistenceLimits - ? LoadFromFileAndDeserialize(Path.Combine(storePath, Timer_xml)) - : DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, Timer_xml)))); - } - - private PSWorkflowContext DeserializeWorkflowMetadataFromStore() - { - PSWorkflowContext metadata = new PSWorkflowContext(); - - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - if (Directory.Exists(storePath) == false) - return metadata; - - if (_disablePersistenceLimits) - { - metadata.WorkflowParameters = - (Dictionary) - LoadFromFileAndDeserialize(Path.Combine(storePath, Input_xml)); - metadata.PSWorkflowCommonParameters = - (Dictionary) - LoadFromFileAndDeserialize(Path.Combine(storePath, PSWorkflowCommonParameters_xml)); - metadata.JobMetadata = - (Dictionary) - LoadFromFileAndDeserialize(Path.Combine(storePath, JobMetadata_xml)); - metadata.PrivateMetadata = - (Dictionary) - LoadFromFileAndDeserialize(Path.Combine(storePath, PrivateMetadata_xml)); - } - else - { - metadata.WorkflowParameters = - (Dictionary) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, Input_xml)))); - metadata.PSWorkflowCommonParameters = - (Dictionary) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, PSWorkflowCommonParameters_xml)))); - metadata.JobMetadata = - (Dictionary) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, JobMetadata_xml)))); - metadata.PrivateMetadata = - (Dictionary) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, PrivateMetadata_xml)))); - } - return metadata; - } - - /// - /// To be called from test code ONLY. - /// - /// - internal IEnumerable CallDoLoad(IEnumerable componentTypes) - { - return DoLoad(componentTypes); - } - - /// - /// DoLoad - /// - /// - protected override IEnumerable DoLoad(IEnumerable componentTypes) - { - Collection loadedComponents = new Collection(); - - lock (_syncLock) - { - foreach (Type componentType in componentTypes) - { - if (componentType == typeof(JobState)) - { - JobState? state = DeserializeWorkflowInstanceStateFromStore(); - if (state != null) - { - JobState jobState = (JobState)state; - if (jobState == JobState.Running || jobState == JobState.Suspended || jobState == JobState.Suspending || jobState == JobState.Stopping || jobState == JobState.NotStarted) - jobState = JobState.Suspended; - - loadedComponents.Add(jobState); - } - } - else if (componentType == typeof(Dictionary)) - { - Dictionary context = DeserializeContextFromStore(); - - if (context != null) - loadedComponents.Add(context); - } - else if (componentType == typeof(PSWorkflowDefinition)) - { - PSWorkflowDefinition workflowDefinition = DeserializeWorkflowDefinitionFromStore(); - - if (workflowDefinition != null) - loadedComponents.Add(workflowDefinition); - } - else if (componentType == typeof(Exception)) - { - Exception exception = DeserializeWorkflowErrorExceptionFromStore(); - - if (exception != null) - loadedComponents.Add(exception); - } - else if (componentType == typeof(PSWorkflowContext)) - { - loadedComponents.Add(DeserializeWorkflowMetadataFromStore()); - } - else if (componentType == typeof(PowerShellStreams)) - { - loadedComponents.Add((PowerShellStreams)DeserializeWorkflowStreamsFromStore()); - } - else if (componentType == typeof(PSWorkflowTimer)) - { - object value = DeserializeWorkflowTimerFromStore(); - loadedComponents.Add( value == null ? new PSWorkflowTimer(PSWorkflowInstance) : new PSWorkflowTimer(PSWorkflowInstance, value)); - } - else if (componentType == typeof(PSWorkflowRemoteActivityState)) - { - Dictionary>> value = DeserializeActivityStateFromStore(); - loadedComponents.Add(value == null ? new PSWorkflowRemoteActivityState(this) : new PSWorkflowRemoteActivityState(this, value)); - } - } - } - - return loadedComponents; - } - - internal JobState? DeserializeWorkflowInstanceStateFromStore() - { - string filePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas, WorkflowInstanceState_xml); - - if (File.Exists(filePath) == false) - { - return null; - } - - return _disablePersistenceLimits - ? (JobState) LoadFromFileAndDeserialize(filePath) - : (JobState) DeserializeObject(Decrypt(LoadFromFile(filePath))); - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] - private PSWorkflowDefinition DeserializeWorkflowDefinitionFromStore() - { - string WorkflowXaml = string.Empty; - string runtimeAssemblyPath = null; - Dictionary requiredAssemblies = null; - - try - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Definition); - - if (Directory.Exists(storePath) == false) - return null; - - WorkflowXaml = File.ReadAllText(Path.Combine(storePath, WorkflowDefinition_xaml)); - - if (File.Exists(Path.Combine(storePath, RuntimeAssembly_dll))) - { - runtimeAssemblyPath = Path.Combine(storePath, RuntimeAssembly_dll); - } - - if (File.Exists(Path.Combine(storePath, RequiredAssemblies_xml))) - { - requiredAssemblies = _disablePersistenceLimits - ? (Dictionary) LoadFromFileAndDeserialize(Path.Combine(storePath, RequiredAssemblies_xml)) - : (Dictionary) DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, RequiredAssemblies_xml)))); - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - var definition = new PSWorkflowDefinition(null, WorkflowXaml, runtimeAssemblyPath, requiredAssemblies); - - // Load required assemblies - if (requiredAssemblies != null && requiredAssemblies.Values.Count > 0) - { - foreach (string requiredAssembly in requiredAssemblies.Values) - { - try - { - // Try first from the GAC - Assembly.Load(requiredAssembly); - } - catch (IOException) - { - try - { - // And second by path - Assembly.LoadFrom(requiredAssembly); - } - catch (IOException) - { - // Finally, by relative path - if (System.Management.Automation.Runspaces.Runspace.DefaultRunspace != null) - { - using (System.Management.Automation.PowerShell nestedPs = - System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) - { - nestedPs.AddCommand("Get-Location"); - PathInfo result = nestedPs.Invoke()[0]; - - string currentLocation = result.ProviderPath; - - try - { - Assembly.LoadFrom(Path.Combine(currentLocation, requiredAssembly)); - } - catch (IOException) - { - } - } - } - } - } - } - } - - if (definition.Workflow == null && string.IsNullOrEmpty(definition.WorkflowXaml) == false) - { - if (string.IsNullOrEmpty(definition.RuntimeAssemblyPath)) - { - definition.Workflow = ImportWorkflowCommand.ConvertXamlToActivity(definition.WorkflowXaml); - } - else - { - Assembly assembly = Assembly.LoadFrom(definition.RuntimeAssemblyPath); - string assemblyName = assembly.GetName().Name; - string assemblyPath = definition.RuntimeAssemblyPath; - definition.Workflow = ImportWorkflowCommand.ConvertXamlToActivity(definition.WorkflowXaml, null, requiredAssemblies, ref assemblyPath, ref assembly, ref assemblyName); - } - } - - return definition; - } - - internal Dictionary DeserializeContextFromStore() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), WorkflowState); - - if (Directory.Exists(storePath) == false) - return null; - - Dictionary context = _disablePersistenceLimits - ? (Dictionary) LoadFromFileAndDeserialize(Path.Combine(storePath, State_xml)) - : (Dictionary) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, State_xml)))); - return context; - } - - - internal Dictionary>> DeserializeActivityStateFromStore() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - if (Directory.Exists(storePath) == false) - return null; - - return _disablePersistenceLimits - ? (Dictionary>>)LoadFromFileAndDeserialize(Path.Combine(storePath, ActivityState_xml)) - : (Dictionary>>)DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, ActivityState_xml)))); - } - - # endregion - - # region Helper Methods - - private static readonly byte[] NullArray = {(byte) 'N', (byte) 'U', (byte) 'L', (byte) 'L'}; - private static readonly byte[] EncryptFalse = {(byte) 'F'}; - - internal ArraySegment SerializeObject(object source) - { - if (_version.EnableCompression) - return SerializeObject2(source); - - ArraySegment scratch = new ArraySegment(NullArray); - - try - { - XmlObjectSerializer serializer = new NetDataContractSerializer(); - - using (MemoryStream outputStream = new MemoryStream()) - { - serializer.WriteObject(outputStream, source); - - scratch = new ArraySegment(outputStream.GetBuffer(), 0, (int)outputStream.Length); - } - } - catch (SerializationException e) - { - this.ThrowErrorOrWriteWarning(e); - } - catch (InvalidDataContractException e) - { - this.ThrowErrorOrWriteWarning(e); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - return scratch; - } - - private void WriteWarning(string warningMessage) - { - Tracer.WriteMessage("WorkflowAdditionalStores", "WriteWarning", this.PSWorkflowInstance.Id, warningMessage); - - if (PSWorkflowInstance.Streams.WarningStream.IsOpen) - { - PSWorkflowInstance.Streams.WarningStream.Add(new WarningRecord(warningMessage)); - } - } - - private void ThrowErrorOrWriteWarning(Exception e) - { - if (PSSessionConfigurationData.IsServerManager) - { - string warningMessage = string.Format(CultureInfo.CurrentCulture, Resources.SerializationWarning, e.Message); - this.WriteWarning(warningMessage); - } - else - { - // there is no point in hanging around partial data in the store so deleting the store. - // and no further persistence allowed after this point - this.InternalDelete(); - serializationErrorHasOccured = true; - - SerializationException exception = new SerializationException(Resources.SerializationErrorException, e); - throw exception; - } - } - private bool serializationErrorHasOccured = false; - - internal object DeserializeObject(ArraySegment source) - { - try - { - XmlObjectSerializer serializer = new NetDataContractSerializer(); - using (MemoryStream inputStream = new MemoryStream(source.Array, source.Offset, source.Count)) - { - object scratch = serializer.ReadObject(inputStream); - - return scratch; - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - - /// - /// Encrypt - /// - /// - /// - protected internal virtual ArraySegment Encrypt(ArraySegment source) - { - if (_version.EnableCompression) - return Encrypt2(source); - - if (!_version.EnableEncryption) - { - byte[] data = new byte[source.Count + 1]; - data[0] = (byte) 'F'; - Buffer.BlockCopy(source.Array, 0, data, 1, source.Count); - - ArraySegment scratch = new ArraySegment(data, 0, data.Length); - - return scratch; - } - else - { - byte[] data = new byte[source.Count]; - Buffer.BlockCopy(source.Array, 0, data, 0, source.Count); - byte[] encryptedData = InstanceStoreCryptography.Protect(data); - - byte[] finalData = new byte[encryptedData.Length + 1]; - finalData[0] = (byte) 'T'; - Buffer.BlockCopy(encryptedData, 0, finalData, 1, encryptedData.Length); - - ArraySegment scratch = new ArraySegment(finalData, 0, encryptedData.Length + 1); - - return scratch; - } - } - - /// - /// Decrypt - /// - /// - /// - protected internal virtual ArraySegment Decrypt(ArraySegment source) - { - bool localEncrypt = false; - if (source.Array[0] == 'T') - { - localEncrypt = true; - } - - if (!localEncrypt) - { - ArraySegment scratch = new ArraySegment(source.Array, 1, source.Count - 1); - - return scratch; - } - else - { - byte[] data = new byte[source.Count - 1]; - Buffer.BlockCopy(source.Array, 1, data, 0, source.Count - 1); - - byte[] decryptedData = InstanceStoreCryptography.Unprotect(data); - - ArraySegment scratch = new ArraySegment(decryptedData, 0, decryptedData.Length); - - return scratch; - } - } - - private void SaveToFile(ArraySegment source, string filePath) - { - if (_version.EnableCompression) - { - SaveToFile2(source, filePath); - return; - } - try - { - using (FileStream stream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) - { - stream.Write(source.Array, 0, source.Count); - stream.Flush(); - stream.Close(); - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - - private ArraySegment LoadFromFile(string filePath) - { - if (_version.EnableCompression) - return LoadFromFile2(filePath); - - ArraySegment tmpBuf = new ArraySegment(); - - try - { - using (FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None)) - { - if (stream.Length != 0) - { - byte[] buf = new byte[stream.Length]; - stream.Read(buf, 0, (int) stream.Length); - tmpBuf = new ArraySegment(buf, 0, (int) stream.Length); - } - - stream.Close(); - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - return tmpBuf; - } - - #endregion - - - #region HighPerf Changes - - private bool _disablePersistenceLimits = false; - - internal ArraySegment SerializeObject2(object source) - { - ArraySegment scratch = new ArraySegment(NullArray); - - try - { - XmlObjectSerializer serializer = new NetDataContractSerializer(); - - using (MemoryStream memoryStream = new MemoryStream()) - { - serializer.WriteObject(memoryStream, source); - - byte[] data = new byte[memoryStream.Length + 1]; - data[0] = (byte) 'F'; - Buffer.BlockCopy(memoryStream.GetBuffer(), 0, data, 1, (int) memoryStream.Length); - scratch = new ArraySegment(data, 0, data.Length); - memoryStream.Close(); - } - } - catch (SerializationException e) - { - this.ThrowErrorOrWriteWarning(e); - } - catch (InvalidDataContractException e) - { - this.ThrowErrorOrWriteWarning(e); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - return scratch; - } - - internal object DeserializeObject2(ArraySegment source) - { - try - { - XmlObjectSerializer serializer = new NetDataContractSerializer(); - using (MemoryStream inputStream = new MemoryStream(source.Array, source.Offset, source.Count)) - { - object scratch = serializer.ReadObject(inputStream); - - return scratch; - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - - /// - /// Serialize an object and directly write to the file - /// - /// object to serialize - /// file to write to - /// number of bytes written - internal int SerializeAndSaveToFile(object source, string filePath) - { - // if there is encryption involved we follow the old non performant - // code path - if (_version.EnableEncryption) - { - ArraySegment serializedData = Encrypt(SerializeObject(source)); - SaveToFile2(serializedData, filePath); - return serializedData.Count; - } - - FileStream stream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None); - GZipStream compressedStream = new GZipStream(stream, CompressionMode.Compress); - - bool sError = false; - Exception sException = null; - - try - { - XmlObjectSerializer serializer = new NetDataContractSerializer(); - compressedStream.Write(EncryptFalse, 0, 1); - serializer.WriteObject(compressedStream, source); - return (int) stream.Length; - } - catch (SerializationException e) - { - sError = true; - sException = e; - } - catch (InvalidDataContractException e) - { - sError = true; - sException = e; - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - finally - { - compressedStream.Close(); - stream.Close(); - compressedStream.Dispose(); - stream.Dispose(); - } - - // this is needed because we want all the streams to closed before executing this method. - if(sError == true) - this.ThrowErrorOrWriteWarning(sException); - - return 0; - } - - internal object LoadFromFileAndDeserialize(string filePath) - { - FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None); - try - { - if (stream.Length != 0) - { - GZipStream decompressedStream = new GZipStream(stream, CompressionMode.Decompress); - - try - { - byte[] marker = new byte[1]; - decompressedStream.Read(marker, 0, 1); - - if (marker[0] == 'T') - { - // if there is encryption involved we follow the old non performant - // code path - decompressedStream.Close(); - stream.Close(); - ArraySegment serializedContents = LoadFromFile2(filePath); - return DeserializeObject2(Decrypt(serializedContents)); - } - - XmlObjectSerializer serializer = new NetDataContractSerializer(); - return serializer.ReadObject(decompressedStream); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - finally - { - decompressedStream.Close(); - decompressedStream.Dispose(); - } - } - } - finally - { - stream.Close(); - stream.Dispose(); - } - return null; - } - - private void SaveToFile2(ArraySegment source, string filePath) - { - try - { - using (FileStream stream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) - { - using (GZipStream compressedStream = new GZipStream(stream, CompressionMode.Compress)) - { - compressedStream.Write(source.Array, 0, source.Count); - compressedStream.Flush(); - compressedStream.Close(); - } - stream.Close(); - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - - private ArraySegment LoadFromFile2(string filePath) - { - ArraySegment tmpBuf = new ArraySegment(); - - try - { - using (FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None)) - { - if (stream.Length != 0) - { - using (GZipStream decompressedStream = new GZipStream(stream, CompressionMode.Decompress)) - { - using (MemoryStream memoryStream = new MemoryStream()) - { - decompressedStream.CopyTo(memoryStream); - tmpBuf = new ArraySegment(memoryStream.GetBuffer(), 0, (int)memoryStream.Length); - decompressedStream.Close(); - memoryStream.Close(); - } - } - } - - stream.Close(); - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - return tmpBuf; - } - - internal void DoSave2(IEnumerable components) - { - lock (_syncLock) - { - string storePath; - long totalBytesWritten = 0; - - if (writtenTotalBytes == 0) - { - writtenTotalBytes = this.GetSavedContextLength() + - this.GetSavedStreamDataLength() + - this.GetSavedMetadataLength() + - this.GetSavedDefinitionLength() + - this.GetSavedTimerLength() + - this.GetSavedJobStateLength() + - this.GetSavedErrorExceptionLength(); - } - - foreach (object component in components) - { - Type componentType = component.GetType(); - - if (componentType == typeof(Dictionary)) - { - CreateAndEnsureInstancePath(WorkflowState, out storePath); - - totalBytesWritten += SerializeAndSaveToFile(component, Path.Combine(storePath, State_xml)); - } - else if (componentType == typeof(PowerShellStreams)) - { - CreateAndEnsureInstancePath(Streams, out storePath); - - Debug.Assert(component.GetType() == typeof(PowerShellStreams), - "The data should be of type workflow stream"); - PowerShellStreams streams = (PowerShellStreams)component; - - totalBytesWritten += SerializeAndSaveToFile(streams.InputStream, Path.Combine(storePath, InputStream_xml)); - - long bytesWritten = 0; - bytesWritten = SerializeAndSaveToFile(streams.OutputStream, - Path.Combine(storePath, OutputStream_xml)); - totalBytesWritten += bytesWritten; - - bytesWritten = SerializeAndSaveToFile(streams.ErrorStream, Path.Combine(storePath, ErrorStream_xml)); - totalBytesWritten += bytesWritten; - - - bytesWritten = SerializeAndSaveToFile(streams.WarningStream, - Path.Combine(storePath, WarningStream_xml)); - totalBytesWritten += bytesWritten; - - bytesWritten = SerializeAndSaveToFile(streams.VerboseStream, - Path.Combine(storePath, VerboseStream_xml)); - totalBytesWritten += bytesWritten; - - bytesWritten = SerializeAndSaveToFile(streams.ProgressStream, - Path.Combine(storePath, ProgressStream_xml)); - totalBytesWritten += bytesWritten; - - bytesWritten = SerializeAndSaveToFile(streams.DebugStream, Path.Combine(storePath, DebugStream_xml)); - totalBytesWritten += bytesWritten; - - bytesWritten = SerializeAndSaveToFile(streams.InformationStream, Path.Combine(storePath, InformationStream_xml)); - totalBytesWritten += bytesWritten; - } - else if (componentType == typeof(PSWorkflowContext)) - { - CreateAndEnsureInstancePath(Metadatas, out storePath); - - Debug.Assert(component.GetType() == typeof(PSWorkflowContext), - "The data should be of type workflow metadata"); - PSWorkflowContext metadata = (PSWorkflowContext)component; - - totalBytesWritten += SerializeAndSaveToFile(metadata.WorkflowParameters, Path.Combine(storePath, Input_xml)); - totalBytesWritten += SerializeAndSaveToFile(metadata.PSWorkflowCommonParameters, - Path.Combine(storePath, PSWorkflowCommonParameters_xml)); - totalBytesWritten += SerializeAndSaveToFile(metadata.JobMetadata, Path.Combine(storePath, JobMetadata_xml)); - totalBytesWritten += SerializeAndSaveToFile(metadata.PrivateMetadata, - Path.Combine(storePath, PrivateMetadata_xml)); - } - else if (componentType == typeof(PSWorkflowDefinition)) - { - if (firstTimeStoringDefinition) - { - Debug.Assert(component.GetType() == typeof(PSWorkflowDefinition), - "The data should be of type workflow definition"); - PSWorkflowDefinition definition = (PSWorkflowDefinition)component; - - // there is no serialization involved, just storing xamls and reference - // assemblies. So existing method is good - SaveSerializedDefinition(definition); - - totalBytesWritten += this.GetSavedDefinitionLength(); - } - } - else if (componentType == typeof(PSWorkflowTimer)) - { - // timer is stored in the metadatas folder - CreateAndEnsureInstancePath(Metadatas, out storePath); - - PSWorkflowTimer data = (PSWorkflowTimer)component; - totalBytesWritten += SerializeAndSaveToFile(data.GetSerializedData(), Path.Combine(storePath, Timer_xml)); - } - else if (componentType == typeof(JobState)) - { - CreateAndEnsureInstancePath(Metadatas, out storePath); - totalBytesWritten += SerializeAndSaveToFile(component, Path.Combine(storePath, WorkflowInstanceState_xml)); - } - else if (component is Exception) - { - CreateAndEnsureInstancePath(Error, out storePath); - totalBytesWritten += SerializeAndSaveToFile(component, Path.Combine(storePath, ErrorException_xml)); - } - else if (component is PSWorkflowRemoteActivityState) - { - CreateAndEnsureInstancePath(Metadatas, out storePath); - - PSWorkflowRemoteActivityState data = (PSWorkflowRemoteActivityState) component; - totalBytesWritten += SerializeAndSaveToFile(data.GetSerializedData(), Path.Combine(storePath, ActivityState_xml)); - } - } - - long oldValueTotalBytesWritten = writtenTotalBytes; - writtenTotalBytes = totalBytesWritten; - - bool allowed = CheckMaxPersistenceSize(oldValueTotalBytesWritten, writtenTotalBytes); - - // if not allowed then write the warning message and continue execution. - if (allowed == false) - { - this.WriteWarning(Resources.PersistenceSizeReached); - etwTracer.PersistenceStoreMaxSizeReached(); - } - } - } - - private string WorkflowStorePath - { - get - { - // it is fine if this segment is not thread-safe - if (String.IsNullOrEmpty(_workflowStorePath)) - { - _workflowStorePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString()); - } - return _workflowStorePath; - } - } - - private string _workflowStorePath; - - private void CreateAndEnsureInstancePath(string subPath, out string storePath) - { - storePath = Path.Combine(WorkflowStorePath, subPath); - EnsureInstancePath(storePath); - } - - private ArraySegment Encrypt2(ArraySegment source) - { - if (!_version.EnableEncryption) - { - return source; - } - byte[] data = new byte[source.Count - 1]; - Buffer.BlockCopy(source.Array, 1, data, 0, source.Count - 1); - - byte[] encryptedData = InstanceStoreCryptography.Protect(data); - byte[] finalData = new byte[encryptedData.Length + 1]; - finalData[0] = (byte)'T'; - Buffer.BlockCopy(encryptedData, 0, finalData, 1, encryptedData.Length); - - ArraySegment scratch = new ArraySegment(finalData, 0, encryptedData.Length + 1); - - return scratch; - } - - #endregion HighPerf Changes - - private static readonly object MaxPersistenceStoreSizeLock = new object(); - internal static long CurrentPersistenceStoreSize = 0; - private static bool _firstTimeCalculatingCurrentStoreSize = true; - - private bool CheckMaxPersistenceSize(long oldValue, long newValue) - { - CalculatePersistenceStoreSizeForFirstTime(); // should not be in lock - - lock (MaxPersistenceStoreSizeLock) - { - long calculatedStoreSize = CurrentPersistenceStoreSize - oldValue + newValue; - - long endpointValueInBytes = _configuration.MaxPersistenceStoreSizeGB*1024*1024*1024; - - if (calculatedStoreSize < endpointValueInBytes) - { - CurrentPersistenceStoreSize = calculatedStoreSize; - return true; - } - else - { - return false; - } - } - } - - private void ReducePersistenceSize(long value) - { - CalculatePersistenceStoreSizeForFirstTime(); // should not be in lock - - lock (MaxPersistenceStoreSizeLock) - { - CurrentPersistenceStoreSize -= value; - } - } - - internal void CalculatePersistenceStoreSizeForFirstTime() - { - // there is no need to calculate the store for the first time because - // there might be possibility of one process trying to get the length - // and other is trying to delete it. - - if (this._configuration.IsDefaultStorePath) - return; - - if (_firstTimeCalculatingCurrentStoreSize == false) - return; - - lock (MaxPersistenceStoreSizeLock) - { - if (_firstTimeCalculatingCurrentStoreSize == false) - return; - - _firstTimeCalculatingCurrentStoreSize = false; - CurrentPersistenceStoreSize = GetDirectoryLength(new DirectoryInfo(_configuration.InstanceStorePath)); - } - } - - private long GetDirectoryLength(DirectoryInfo directory) - { - long size = 0; - - try - { - if (directory.Exists) - { - DirectoryInfo[] dirs = directory.GetDirectories(); - FileInfo[] files = directory.GetFiles(); - foreach (FileInfo file in files) - { - size += file.Exists ? file.Length : 0; - } - foreach (DirectoryInfo dir in dirs) - { - size += GetDirectoryLength(dir); - } - } - } - // It is safe of absorb an exception here because there might be a possibility that the multiple - // processes are try work on the same endpoint or default end point store folder. - // There is a possibility that one process has deleted the a workflow store and the other one is trying to access it for calculating its store size. - // And we will set the flag to make sure we calculate the size next time. - catch (DirectoryNotFoundException e) - { - Tracer.TraceException(e); - _firstTimeCalculatingCurrentStoreSize = true; - } - catch (FileNotFoundException e) - { - Tracer.TraceException(e); - _firstTimeCalculatingCurrentStoreSize = true; - } - catch (UnauthorizedAccessException e) - { - Tracer.TraceException(e); - _firstTimeCalculatingCurrentStoreSize = true; - } - - return size; - } - } -} - ; diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/GlobalConfiguration.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/GlobalConfiguration.cs deleted file mode 100644 index 3f42ae8c2a0..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/GlobalConfiguration.cs +++ /dev/null @@ -1,930 +0,0 @@ -/* - * Copyright (c) 2010 Microsoft Corporation. All rights reserved - */ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Management.Automation; -using System.Xml; -using System.Diagnostics.CodeAnalysis; -using System.Security.Principal; -using Microsoft.PowerShell.Commands; -using System.Activities; -using System.Collections.Concurrent; -using Microsoft.PowerShell.Activities; -using System.Management.Automation.Remoting; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// PSWorkflowConfigurationProvider - /// - public class PSWorkflowConfigurationProvider - { - #region Public Methods - - /// - /// Default constructor - /// - public PSWorkflowConfigurationProvider() - { - _wfOptions = new PSWorkflowExecutionOption(); - LoadFromDefaults(); - } - - #endregion - - #region Private Members - - private string _configProviderId; - private String _privateData; - private readonly object _syncObject = new object(); - private bool _isPopulated; - private const string PrivateDataToken = "PrivateData"; - private const string NameToken = "Name"; - private const string ParamToken = "Param"; - private const string ValueToken = "Value"; - - internal const string PSDefaultActivities = "PSDefaultActivities"; - - private PSWorkflowExecutionOption _wfOptions; - - internal const string TokenPersistencePath = "persistencepath"; - internal static readonly string DefaultPersistencePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\Windows\PowerShell\WF\PS"); - - internal const string TokenPersistWithEncryption = "persistwithencryption"; - internal static readonly bool DefaultPersistWithEncryption = false; - - internal const string TokenMaxPersistenceStoreSizeGB = "maxpersistencestoresizegb"; - internal static readonly long DefaultMaxPersistenceStoreSizeGB = 10; // In GB - - internal const string TokenMaxRunningWorkflows = "maxrunningworkflows"; - internal static readonly int DefaultMaxRunningWorkflows = 30; - internal const int MinMaxRunningWorkflows = 1; - internal const int MaxMaxRunningWorkflows = int.MaxValue; - - internal const string TokenAllowedActivity = "allowedactivity"; - internal static readonly IEnumerable DefaultAllowedActivity = new string[] { PSDefaultActivities }; - - internal const string TokenOutOfProcessActivity = "outofprocessactivity"; - internal static readonly IEnumerable DefaultOutOfProcessActivity = new string[] { "InlineScript" }; - - internal const string TokenEnableValidation = "enablevalidation"; - internal static readonly bool DefaultEnableValidation = false; - - internal const string TokenMaxDisconnectedSessions = "maxdisconnectedsessions"; - internal static readonly int DefaultMaxDisconnectedSessions = 1000; - internal const int MinMaxDisconnectedSessions = 1; - internal const int MaxMaxDisconnectedSessions = int.MaxValue; - - internal const string TokenMaxConnectedSessions = "maxconnectedsessions"; - internal static readonly int DefaultMaxConnectedSessions = 100; - internal const int MinMaxConnectedSessions = 1; - internal const int MaxMaxConnectedSessions = int.MaxValue; - - internal const string TokenMaxSessionsPerWorkflow = "maxsessionsperworkflow"; - internal static readonly int DefaultMaxSessionsPerWorkflow = 5; - internal const int MinMaxSessionsPerWorkflow = 1; - internal const int MaxMaxSessionsPerWorkflow = int.MaxValue; - - internal const string TokenMaxSessionsPerRemoteNode = "maxsessionsperremotenode"; - internal static readonly int DefaultMaxSessionsPerRemoteNode = 5; - internal const int MinMaxSessionsPerRemoteNode = 1; - internal const int MaxMaxSessionsPerRemoteNode = int.MaxValue; - - internal const string TokenMaxActivityProcesses = "maxactivityprocesses"; - internal static readonly int DefaultMaxActivityProcesses = 5; - internal const int MinMaxActivityProcesses = 1; - internal const int MaxMaxActivityProcesses = int.MaxValue; - - internal const string TokenActivityProcessIdleTimeoutSec = "activityprocessidletimeoutsec"; - internal static readonly int DefaultActivityProcessIdleTimeoutSec = 60; - internal const int MinActivityProcessIdleTimeoutSec = 1; - internal const int MaxActivityProcessIdleTimeoutSec = int.MaxValue; - - internal const string TokenWorkflowApplicationPersistUnloadTimeoutSec = "workflowapplicationpersistunloadtimeoutsec"; - internal static readonly int DefaultWorkflowApplicationPersistUnloadTimeoutSec = 5; - internal const int MinWorkflowApplicationPersistUnloadTimeoutSec = 0; - internal const int MaxWorkflowApplicationPersistUnloadTimeoutSec = int.MaxValue; - - internal const string TokenWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = "wsmanpluginreportcompletiononzeroactivesessionswaitintervalmsec"; - internal static readonly int DefaultWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = 30 * 1000; - internal const int MinWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = 100; - internal const int MaxWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = int.MaxValue; - - internal const string TokenActivitiesCacheCleanupIntervalMSec = "activitiescachecleanupintervalmsec"; - internal static readonly int DefaultActivitiesCacheCleanupIntervalMSec = 5 * 60 * 1000; - internal const int MinActivitiesCacheCleanupIntervalMSec = 100; - internal const int MaxActivitiesCacheCleanupIntervalMSec = int.MaxValue; - - internal const string TokenRemoteNodeSessionIdleTimeoutSec = "remotenodesessionidletimeoutsec"; - internal static readonly int DefaultRemoteNodeSessionIdleTimeoutSec = 60; - internal const int MinRemoteNodeSessionIdleTimeoutSec = 30; - internal const int MaxRemoteNodeSessionIdleTimeoutSec = 30000; - private int _remoteNodeSessionIdleTimeoutSec; - - internal const string TokenSessionThrottleLimit = "sessionthrottlelimit"; - internal static readonly int DefaultSessionThrottleLimit = 100; - internal const int MinSessionThrottleLimit = 1; - internal const int MaxSessionThrottleLimit = int.MaxValue; - - internal const string TokenValidationCacheLimit = "validationcachelimit"; - internal static readonly int DefaultValidationCacheLimit = 10000; - private int _validationCacheLimit; - - internal const string TokenCompiledAssemblyCacheLimit = "compiledassemblycachelimit"; - internal static readonly int DefaultCompiledAssemblyCacheLimit = 10000; - private int _compiledAssemblyCacheLimit; - - internal const string TokenOutOfProcessActivityCacheLimit = "outofprocessactivitycachelimit"; - internal static readonly int DefaultOutOfProcessActivityCacheLimit = 10000; - private int _outOfProcessActivityCacheLimit; - - internal static readonly int DefaultPSPersistInterval = 30; // in seconds - - internal const string TokenWorkflowShutdownTimeoutMSec = "workflowshutdowntimeoutmsec"; - internal static readonly int DefaultWorkflowShutdownTimeoutMSec = 500; // 0.5 secs - internal const int MinWorkflowShutdownTimeoutMSec = 0; - internal const int MaxWorkflowShutdownTimeoutMSec = 5 * 1000; // 5 secs - - internal const string TokenMaxInProcRunspaces = "maxinprocrunspaces"; - internal static readonly int DefaultMaxInProcRunspaces = DefaultMaxRunningWorkflows * 2; - private int _maxInProcRunspaces = DefaultMaxInProcRunspaces; - - private readonly ConcurrentDictionary outOfProcessActivityCache = new ConcurrentDictionary(); - - #endregion Private Members - - #region Private Methods - - internal static PSWorkflowExecutionOption LoadConfig(string privateData, PSWorkflowConfigurationProvider configuration) - { - PSWorkflowExecutionOption target = new PSWorkflowExecutionOption(); - if (String.IsNullOrEmpty(privateData)) - { - return target; - } - - XmlReaderSettings readerSettings = new XmlReaderSettings - { - CheckCharacters = false, - IgnoreComments = true, - IgnoreProcessingInstructions = true, - MaxCharactersInDocument = 10000, - XmlResolver = null, - ConformanceLevel = ConformanceLevel.Fragment - }; - - using (XmlReader reader = XmlReader.Create(new StringReader(privateData), readerSettings)) - { - // read the header - if (reader.ReadToFollowing(PrivateDataToken)) - { - HashSet assignedParams = new HashSet(); - bool found = reader.ReadToDescendant(ParamToken); - while (found) - { - if (!reader.MoveToAttribute(NameToken)) - { - throw new PSArgumentException(Resources.NameNotSpecifiedForParam); - } - - string optionName = reader.Value; - - if (!reader.MoveToAttribute(ValueToken)) - { - throw new PSArgumentException(Resources.ValueNotSpecifiedForParam); - } - - if (assignedParams.Contains(optionName.ToLower(CultureInfo.InvariantCulture))) - { - throw new PSArgumentException(Resources.ParamSpecifiedMoreThanOnce, optionName); - } - - string optionValue = reader.Value; - Update(optionName, optionValue, target, configuration); - - assignedParams.Add(optionName.ToLower(CultureInfo.InvariantCulture)); - found = reader.ReadToFollowing(ParamToken); - } - } - } - return target; - } - - /// - /// - /// - public PSWorkflowRuntime Runtime - { - get; - internal set; - } - - private bool? _powerShellActivitiesAreAllowed; - internal bool PSDefaultActivitiesAreAllowed - { - get - { - if (_powerShellActivitiesAreAllowed == null) - { - lock (_syncObject) { - if (_powerShellActivitiesAreAllowed == null) - { - bool allowed = (AllowedActivity ?? new string[0]).Any(a => string.Equals(a, PSDefaultActivities, StringComparison.OrdinalIgnoreCase)); - _powerShellActivitiesAreAllowed = allowed; - } - } - } - return _powerShellActivitiesAreAllowed.Value; - } - } - - /// - /// Using optionName and optionValue updates the current object - /// - /// - /// - /// - /// - private static void Update(string optionName, string value, PSWorkflowExecutionOption target, PSWorkflowConfigurationProvider configuration) - { - switch (optionName.ToLower(CultureInfo.InvariantCulture)) - { - case TokenWorkflowApplicationPersistUnloadTimeoutSec: - target.WorkflowApplicationPersistUnloadTimeoutSec = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec: - target.WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = (int)LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenActivitiesCacheCleanupIntervalMSec: - target.ActivitiesCacheCleanupIntervalMSec = (int)LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenActivityProcessIdleTimeoutSec: - target.ActivityProcessIdleTimeoutSec = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenAllowedActivity: - string[] activities = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - target.AllowedActivity = activities.Select(activity => activity.Trim()).ToArray(); - break; - case TokenEnableValidation: - bool enableValidation; - if (bool.TryParse(value, out enableValidation)) - { - target.EnableValidation = enableValidation; - } - break; - case TokenPersistencePath: - target.PersistencePath = Environment.ExpandEnvironmentVariables(value); - break; - case TokenMaxPersistenceStoreSizeGB: - target.MaxPersistenceStoreSizeGB = (long)LanguagePrimitives.ConvertTo(value, typeof(long), CultureInfo.InvariantCulture); - break; - case TokenPersistWithEncryption: - bool persistWithEncryption; - if (bool.TryParse(value, out persistWithEncryption)) - { - target.PersistWithEncryption = persistWithEncryption; - } - break; - case TokenRemoteNodeSessionIdleTimeoutSec: - target.RemoteNodeSessionIdleTimeoutSec = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - if (configuration != null) - { - configuration._remoteNodeSessionIdleTimeoutSec = target.RemoteNodeSessionIdleTimeoutSec; - } - break; - case TokenMaxActivityProcesses: - target.MaxActivityProcesses = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenMaxConnectedSessions: - target.MaxConnectedSessions = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenMaxDisconnectedSessions: - target.MaxDisconnectedSessions = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenMaxRunningWorkflows: - target.MaxRunningWorkflows = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - configuration._maxInProcRunspaces = target.MaxRunningWorkflows*2; - break; - case TokenMaxSessionsPerRemoteNode: - target.MaxSessionsPerRemoteNode = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenMaxSessionsPerWorkflow: - target.MaxSessionsPerWorkflow = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenOutOfProcessActivity: - string[] outofProcActivities = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - target.OutOfProcessActivity = outofProcActivities.Select(activity => activity.Trim()).ToArray(); - break; - - case TokenSessionThrottleLimit: - { - target.SessionThrottleLimit = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - } - break; - case TokenValidationCacheLimit: - if (configuration != null) - { - configuration._validationCacheLimit = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - } - break; - case TokenCompiledAssemblyCacheLimit: - if (configuration != null) - { - configuration._compiledAssemblyCacheLimit = (int)LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - } - break; - case TokenOutOfProcessActivityCacheLimit: - if (configuration != null) - { - configuration._outOfProcessActivityCacheLimit = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - } - break; - case TokenWorkflowShutdownTimeoutMSec: - target.WorkflowShutdownTimeoutMSec = (int)LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenMaxInProcRunspaces: - { - if (configuration != null) - { - configuration._maxInProcRunspaces = - (int) LanguagePrimitives.ConvertTo(value, typeof (int), CultureInfo.InvariantCulture); - } - } - break; - } - } - - #endregion Private Methods - - #region Internal Methods - - /// - /// PSWorkflowConfigurationProvider - /// - /// - /// - public PSWorkflowConfigurationProvider(string applicationPrivateData, string configProviderId) - { - Populate(applicationPrivateData, configProviderId); - } - - private void LoadFromDefaults() - { - _wfOptions.PersistencePath = DefaultPersistencePath; - _wfOptions.MaxPersistenceStoreSizeGB = DefaultMaxPersistenceStoreSizeGB; - _wfOptions.PersistWithEncryption = DefaultPersistWithEncryption; - _wfOptions.MaxRunningWorkflows = DefaultMaxRunningWorkflows; - _wfOptions.AllowedActivity = new List(DefaultAllowedActivity).ToArray(); - _wfOptions.OutOfProcessActivity = new List(DefaultOutOfProcessActivity).ToArray(); - _wfOptions.EnableValidation = DefaultEnableValidation; - _wfOptions.MaxDisconnectedSessions = DefaultMaxDisconnectedSessions; - _wfOptions.MaxConnectedSessions = DefaultMaxConnectedSessions; - _wfOptions.MaxSessionsPerWorkflow = DefaultMaxSessionsPerWorkflow; - _wfOptions.MaxSessionsPerRemoteNode = DefaultMaxSessionsPerRemoteNode; - _wfOptions.MaxActivityProcesses = DefaultMaxActivityProcesses; - _wfOptions.ActivityProcessIdleTimeoutSec = DefaultActivityProcessIdleTimeoutSec; - _wfOptions.WorkflowApplicationPersistUnloadTimeoutSec = DefaultWorkflowApplicationPersistUnloadTimeoutSec; - _wfOptions.WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = DefaultWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec; - _wfOptions.ActivitiesCacheCleanupIntervalMSec = DefaultActivitiesCacheCleanupIntervalMSec; - _wfOptions.RemoteNodeSessionIdleTimeoutSec = DefaultRemoteNodeSessionIdleTimeoutSec; - _wfOptions.SessionThrottleLimit = DefaultSessionThrottleLimit; - _validationCacheLimit = DefaultValidationCacheLimit; - _compiledAssemblyCacheLimit = DefaultCompiledAssemblyCacheLimit; - _outOfProcessActivityCacheLimit = DefaultOutOfProcessActivityCacheLimit; - _wfOptions.WorkflowShutdownTimeoutMSec = DefaultWorkflowShutdownTimeoutMSec; - - ResetCaching(); - } - - private void ResetCaching() - { - _powerShellActivitiesAreAllowed = null; - outOfProcessActivityCache.Clear(); - } - - internal PSSenderInfo _senderInfo = null; - internal void Populate(string applicationPrivateData, string configProviderId, PSSenderInfo senderInfo) - { - _senderInfo = senderInfo; - Populate(applicationPrivateData, configProviderId); - } - - /// - /// Populate the global configuration object with - /// information from the configuration xml - /// - /// private data - /// associated with the endpoint - /// - public void Populate(string applicationPrivateData, string configProviderId) - { - if (_isPopulated) - { - return; - } - - lock (_syncObject) - { - if (!_isPopulated) - { - _privateData = applicationPrivateData; - - string[] tokens = configProviderId.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); - _configProviderId = tokens.Length > 0 ? tokens[tokens.Length - 1] : configProviderId; - _isPopulated = true; - _wfOptions = LoadConfig(_privateData, this); - ResetCaching(); - } - else - { - Debug.Assert(false, "Populate should only be called once"); - } - } - } - - #region PSWorkflowConfigurationProvider Implementation - - /// - /// CreatePSActivityHostController - /// - /// - public virtual PSActivityHostController CreatePSActivityHostController() - { - return new PSOutOfProcessActivityController(Runtime); - } - - /// - /// CreatePSWorkflowInstance - /// - /// - /// - /// - /// - /// - public virtual PSWorkflowInstance CreatePSWorkflowInstance(PSWorkflowDefinition definition, PSWorkflowContext metadata, PSDataCollection pipelineInput, PSWorkflowJob job) - { - return new PSWorkflowApplicationInstance(Runtime, definition, metadata, pipelineInput, job); - } - - /// - /// CreatePSWorkflowInstance - /// - /// - /// - public virtual PSWorkflowInstance CreatePSWorkflowInstance(PSWorkflowId instanceId) - { - return new PSWorkflowApplicationInstance(Runtime, instanceId); - } - - /// - /// CreatePSWorkflowInstanceStore - /// - /// - /// - public virtual PSWorkflowInstanceStore CreatePSWorkflowInstanceStore(PSWorkflowInstance workflowInstance) - { - return new PSWorkflowFileInstanceStore(this, workflowInstance); - } - - /// - /// CreateWorkflowExtensions - /// - /// - public virtual IEnumerable CreateWorkflowExtensions() - { - if (PSWorkflowExtensions.CustomHandler != null) - { - return PSWorkflowExtensions.CustomHandler(); - } - - return null; - } - - /// - /// CreateWorkflowExtensionCreationFunctions - /// - /// - /// - [SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the Workflow.")] - public virtual IEnumerable> CreateWorkflowExtensionCreationFunctions() - { - return null; - } - - /// - /// CreateRemoteRunspaceProvider - /// - /// - public virtual RunspaceProvider CreateRemoteRunspaceProvider() - { - return new ConnectionManager(RemoteNodeSessionIdleTimeoutSec * 1000, - MaxSessionsPerRemoteNode, - SessionThrottleLimit, - MaxConnectedSessions, - MaxDisconnectedSessions); - } - - /// - /// Local runspace provider - /// - /// - /// - public virtual RunspaceProvider CreateLocalRunspaceProvider(bool isUnbounded) - { - if(isUnbounded) - return new LocalRunspaceProvider(RemoteNodeSessionIdleTimeoutSec, LanguageMode); - else - return new LocalRunspaceProvider(RemoteNodeSessionIdleTimeoutSec, MaxInProcRunspaces, LanguageMode); - } - - #endregion - - /// - /// CurrentUserIdentity returns the current user - /// - internal WindowsIdentity CurrentUserIdentity - { - get { return _currentIdentity ?? (_currentIdentity = WindowsIdentity.GetCurrent()); } - } - private WindowsIdentity _currentIdentity; - - /// - /// To be called only by test code - /// - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is called from the tests")] - internal void ResetPopulate() - { - lock (_syncObject) - { - _isPopulated = false; - LoadFromDefaults(); - } - } - - #endregion Internal Methods - - #region Configuration Properties - - /// - /// ConfigProviderId - /// - internal string ConfigProviderId - { - get - { - return _configProviderId; - } - } - - /// - /// PersistencePath - /// - internal string PersistencePath - { - get - { - return _wfOptions.PersistencePath; - } - } - - internal bool IsDefaultStorePath; - private string _instanceStorePath; - internal string InstanceStorePath - { - get - { - if (_instanceStorePath != null) - return _instanceStorePath; - - lock (_syncObject) - { - if (_instanceStorePath != null) - return _instanceStorePath; - - if (CurrentUserIdentity.User != null) - { - string path = Path.Combine(_wfOptions.PersistencePath ?? DefaultPersistencePath, _configProviderId ?? "default", CurrentUserIdentity.User.Value); - - WindowsPrincipal principal = new WindowsPrincipal(CurrentUserIdentity); - - bool elevated = false; - bool nonInteractive = false; - bool credssp = false; - - - if (principal.IsInRole(WindowsBuiltInRole.Administrator)) - elevated = true; - - if (PSSessionConfigurationData.IsServerManager == false) // In order to avoid breaking change (SM+ might get affected) - { - if (!principal.IsInRole(new SecurityIdentifier(WellKnownSidType.InteractiveSid, null))) - nonInteractive = true; - - if (_senderInfo != null && - _senderInfo.UserInfo != null && - _senderInfo.UserInfo.Identity != null && - _senderInfo.UserInfo.Identity.AuthenticationType != null && - _senderInfo.UserInfo.Identity.AuthenticationType.Equals("credssp", StringComparison.OrdinalIgnoreCase)) - { - credssp = true; - } - } - - if (credssp) - { - path += "_CP"; // the authentication type is credSSP - } - else - { - if (elevated) - path += "_EL"; // token is elevated - - if (nonInteractive) - path += "_NI"; // token is not interactive - } - - _instanceStorePath = path; - } - IsDefaultStorePath = string.IsNullOrEmpty(_configProviderId) ? true : false; - } - return _instanceStorePath; - } - } - - /// - /// MaxPersistenceStoreSizeGB - /// - internal long MaxPersistenceStoreSizeGB - { - get - { - return _wfOptions.MaxPersistenceStoreSizeGB; - } - } - - /// - /// PersistWithEncryption - /// - internal bool PersistWithEncryption - { - get - { - return _wfOptions.PersistWithEncryption; - } - } - - /// - /// MaxRunningWorkflows - /// - public virtual int MaxRunningWorkflows - { - get - { - return _wfOptions.MaxRunningWorkflows; - } - } - - /// - /// AllowedActivity - /// - public virtual IEnumerable AllowedActivity - { - get - { - return _wfOptions.AllowedActivity; - } - } - - /// - /// OutOfProcActivity - /// - public virtual IEnumerable OutOfProcessActivity - { - get - { - return _wfOptions.OutOfProcessActivity; - } - } - - - /// - /// EnableValidation - /// - public virtual bool EnableValidation - { - get - { - return _wfOptions.EnableValidation; - } - } - - /// - /// MaxDisconnectedSessions - /// - public virtual int MaxDisconnectedSessions - { - get - { - return _wfOptions.MaxDisconnectedSessions; - } - } - - /// - /// MaxConnectedSessions - /// - public virtual int MaxConnectedSessions - { - get - { - return _wfOptions.MaxConnectedSessions; - } - } - - /// - /// MaxSessionsPerWorkflow - /// - internal int MaxSessionsPerWorkflow - { - get - { - return _wfOptions.MaxSessionsPerWorkflow; - } - } - - /// - /// MaxSessionsPerRemoteNode - /// - public virtual int MaxSessionsPerRemoteNode - { - get - { - return _wfOptions.MaxSessionsPerRemoteNode; - } - } - - /// - /// MaxActivityProcesses - /// - public virtual int MaxActivityProcesses - { - get - { - return _wfOptions.MaxActivityProcesses; - } - } - - /// - /// WorkflowApplicationPersistUnloadTimeoutSec - /// - public virtual int PSWorkflowApplicationPersistUnloadTimeoutSec - { - get - { - return _wfOptions.WorkflowApplicationPersistUnloadTimeoutSec; - } - } - - /// - /// WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec - /// - public virtual int WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec - { - get - { - return _wfOptions.WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec; - } - } - - /// - /// ActivitiesCacheCleanupIntervalMSec - /// - public virtual int ActivitiesCacheCleanupIntervalMSec - { - get - { - return _wfOptions.ActivitiesCacheCleanupIntervalMSec; - } - } - - /// - /// Local Machine Runspace Language Mode - /// - public virtual PSLanguageMode? LanguageMode - { - get - { - return null; - } - } - - /// - /// ActivityProcessIdleTimeOutSec - /// - public virtual int ActivityProcessIdleTimeoutSec - { - get - { - return _wfOptions.ActivityProcessIdleTimeoutSec; - } - } - - /// - /// RemoteNodeSessionIdleTimeOutSec - /// - public virtual int RemoteNodeSessionIdleTimeoutSec - { - get - { - return _wfOptions.RemoteNodeSessionIdleTimeoutSec; - } - } - - /// - /// SessionThrottleLimit - /// - public virtual int SessionThrottleLimit - { - get - { - return _wfOptions.SessionThrottleLimit; - } - } - - internal int ValidationCacheLimit - { - get { return _validationCacheLimit; } - } - - internal int CompiledAssemblyCacheLimit - { - get { return _compiledAssemblyCacheLimit; } - } - - internal int OutOfProcessActivityCacheLimit - { - get - { - return _outOfProcessActivityCacheLimit; - } - } - - /// - /// WorkflowShutdownTimeoutMSec - the maximum time allowed to suspend the workflows before aborting them. - /// - internal int WorkflowShutdownTimeoutMSec - { - get - { - return _wfOptions.WorkflowShutdownTimeoutMSec; - } - } - - /// - /// MaxInProcRunspaces - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public virtual int MaxInProcRunspaces - { - get { return _maxInProcRunspaces; } - } - - /// - /// Get Activity Run Mode InProcess or Out of process - /// - /// - /// - public virtual ActivityRunMode GetActivityRunMode(Activity activity) - { - if (activity == null) - throw new ArgumentNullException("activity"); - - if (outOfProcessActivityCache.Count >= OutOfProcessActivityCacheLimit) - outOfProcessActivityCache.Clear(); - - bool outOfProc = outOfProcessActivityCache.GetOrAdd(activity.GetType(), IsOutOfProcessActivity); - return outOfProc ? ActivityRunMode.OutOfProcess : ActivityRunMode.InProcess; - } - - private bool IsOutOfProcessActivity(Type activityType) - { - return (OutOfProcessActivity ?? new string[0]).Any(outOfProcessActivity => false || IsMatched(outOfProcessActivity, activityType.Name) || IsMatched(outOfProcessActivity, activityType.FullName) || IsMatched(outOfProcessActivity, activityType.Assembly.GetName().Name + "\\" + activityType.Name) || IsMatched(outOfProcessActivity, activityType.Assembly.GetName().Name + "\\" + activityType.FullName) || IsMatched(outOfProcessActivity, activityType.Assembly.GetName().FullName + "\\" + activityType.Name) || IsMatched(outOfProcessActivity, activityType.Assembly.GetName().FullName + "\\" + activityType.FullName)); - } - - private static bool IsMatched(string allowedActivity, string match) - { - return (WildcardPattern.ContainsWildcardCharacters(allowedActivity) - ? new WildcardPattern(allowedActivity, WildcardOptions.IgnoreCase).IsMatch(match) - : string.Equals(allowedActivity, match, StringComparison.OrdinalIgnoreCase)); - } - - #endregion Configuration Properties - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ImportWorkflowCommand.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ImportWorkflowCommand.cs deleted file mode 100644 index d756340887a..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ImportWorkflowCommand.cs +++ /dev/null @@ -1,1874 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.Activities; -using System.Activities.XamlIntegration; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.Management.Automation.Remoting; -using System.Management.Automation.Tracing; -using System.Reflection; -using System.Text; -using System.Xaml; -using Microsoft.PowerShell.Workflow; -using System.Text.RegularExpressions; -using System.Management.Automation.Security; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// A cmdlet to load WF Workflows, expressed as XAML and wrap them - /// in functions. - /// - [Cmdlet(VerbsData.Import, "PSWorkflow", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=210606")] - public class ImportWorkflowCommand : PSCmdlet - { - private static readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static Tracer _structuredTracer = new System.Management.Automation.Tracing.Tracer(); - - /// - /// Paths to the XAML files to load. Wild cards are supported. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] - [Alias("PSPath")] - public String[] Path - { - get - { - return _path; - } - set - { - _path = value; - } - } - private string[] _path; - - /// - /// Paths to the dependent XAML files to load. Wild cards are supported. - /// - [Parameter(Position = 1)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] - [Alias("PSDependentWorkflow")] - public String[] DependentWorkflow - { - get - { - return _dependentWorkflow; - } - set - { - _dependentWorkflow = value; - } - } - private string[] _dependentWorkflow; - - - /// - /// Paths to the dependent XAML files to load. Wild cards are supported. - /// - [Parameter(Position = 2)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] - [Alias("PSDependentAssemblies")] - public String[] DependentAssemblies - { - get - { - return _dependentAssemblies; - } - set - { - _dependentAssemblies = value; - } - } - private string[] _dependentAssemblies; - - /// - /// Flags -force operations - /// - [Parameter] - public SwitchParameter Force - { - get { return _force; } - set { _force = value; } - } - private bool _force; - - /// - /// Process all of the specified XAML files to generate corresponding functions - /// - protected override void ProcessRecord() - { - // In ConstrainedLanguage, XAML workflows are not supported (even from a trusted FullLanguage state), - // unless they are signed in-box OS binaries. - bool checkSignatures = false; - if ((SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) || - (this.SessionState.LanguageMode == PSLanguageMode.ConstrainedLanguage)) - { - // However, this static internal property can be changed by tests that can already run a script - // in full-language mode. - PropertyInfo xamlProperty = typeof(SystemPolicy).GetProperty("XamlWorkflowSupported", BindingFlags.NonPublic | BindingFlags.Static); - checkSignatures = !((bool)xamlProperty.GetValue(null, null)); - } - - string dependentWorkflowAssemblyPath = string.Empty; - List dependentWorkflowContent = new List(); - - if (_dependentWorkflow != null) - { - foreach (string dependentPath in _dependentWorkflow) - { - Collection dependentFilePaths; - try - { - // Try to resolve the pathname, including wildcard resolution - // if an error occurs, generate a non-terminating exception and continue to the next path. - ProviderInfo provider = null; - dependentFilePaths = this.SessionState.Path.GetResolvedProviderPathFromPSPath(dependentPath, /* Ignored */ out provider); - } - catch (ItemNotFoundException notFound) - { - - ErrorRecord er = new ErrorRecord(notFound, "Workflow_XamlFileNotFound", - ErrorCategory.OpenError, dependentPath); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - - - if (dependentFilePaths != null && dependentFilePaths.Count > 0) - { - if (dependentFilePaths.Count == 1 && string.Compare(System.IO.Path.GetExtension(dependentFilePaths[0]), ".dll", StringComparison.OrdinalIgnoreCase) == 0) - { - dependentWorkflowAssemblyPath = dependentFilePaths[0]; - } - else - { - // The argument may have resolved to more than one path. Loop over the resolved paths - // loading each file in turn. If the resolved path does not have a '.xaml' exception, - // generate a non-terminating exception and continue. - foreach (string resolvedPath in dependentFilePaths) - { - string fileText; - //string name; - string extension = System.IO.Path.GetExtension(resolvedPath); - if (string.Compare(extension, ".xaml", StringComparison.OrdinalIgnoreCase) != 0) - { - InvalidOperationException opException = new InvalidOperationException( - string.Format(CultureInfo.CurrentUICulture, Resources.InvalidWorkflowExtension, extension)); - ErrorRecord er = new ErrorRecord(opException, "Workflows_InvalidWorkflowFileExtension", - ErrorCategory.InvalidOperation, resolvedPath); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - - CheckFileSignatureAsNeeded(checkSignatures, resolvedPath); - - try - { - // Finally load the file. If there is an access violation, write a - // not terminating error and continue. - fileText = System.IO.File.ReadAllText(resolvedPath); - dependentWorkflowContent.Add(fileText); - } - catch (System.AccessViolationException noPerms) - { - ErrorRecord er = new ErrorRecord(noPerms, "Workflow_XAMLfileNotAccessible", - ErrorCategory.PermissionDenied, dependentPath); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - } - } - } - else - { - // If the file spec didn't resolve to a path, write an error - string message = string.Format(CultureInfo.CurrentUICulture, Resources.NoMatchingWorkflowWasFound, dependentPath); - FileNotFoundException fnf = new FileNotFoundException(message); - ErrorRecord er = new ErrorRecord(fnf, "Workflow_NoMatchingWorkflowXamlFileFound", - ErrorCategory.ResourceUnavailable, dependentPath); - WriteError(er); - Tracer.TraceErrorRecord(er); - } - } - } - - Collection filePaths; - - if (_path != null) - { - foreach (string path in _path) - { - try - { - // Try to resolve the pathname, including wildcard resolution - // if an error occurs, generate a non-terminating exception and continue to the next path. - ProviderInfo provider = null; - filePaths = this.SessionState.Path.GetResolvedProviderPathFromPSPath(path, /* Ignored */ out provider); - } - catch (ItemNotFoundException notFound) - { - - ErrorRecord er = new ErrorRecord(notFound, "Workflow_XamlFileNotFound", - ErrorCategory.OpenError, path); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - - if (filePaths != null && filePaths.Count > 0) - { - // The argument may have resolved to more than one path. Loop over the resolved paths - // loading each file in turn. If the resolved path does not have a '.xaml' exception, - // generate a non-terminating exception and continue. - foreach (string resolvedPath in filePaths) - { - string fileText = string.Empty; - string name; - string extension = System.IO.Path.GetExtension(resolvedPath); - if (string.Compare(extension, ".xaml", StringComparison.OrdinalIgnoreCase) != 0) - { - InvalidOperationException opException = new InvalidOperationException( - string.Format(CultureInfo.CurrentUICulture, Resources.InvalidWorkflowExtension, extension)); - ErrorRecord er = new ErrorRecord(opException, "Workflows_InvalidWorkflowFileExtension", - ErrorCategory.InvalidOperation, resolvedPath); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - - CheckFileSignatureAsNeeded(checkSignatures, resolvedPath); - - FunctionDetails detailsToUseForUpdate = null; - try - { - // Write a message indicating the file name it is trying to load. - string message = string.Format(CultureInfo.CurrentUICulture, Resources.ImportingWorkflowFrom, resolvedPath); - WriteVerbose(message); - - // If -Verbose has been passed to this cmdlet, then the Set operation in GenerateFunctionFromXaml() - // will also be logged the verbose stream. This is desirable as it displays the generated function... - - // Try to read the file. If there is an access violation, write a - // non-terminating error and continue with the next workflow file. - - // check if the function cache already has the entry to this file - // if the xaml was already parsed use the same function definition - // again - name = System.IO.Path.GetFileNameWithoutExtension(resolvedPath); - _structuredTracer.ImportingWorkflowFromXaml(Guid.Empty, name); - FunctionCache.TryGetValue(resolvedPath, out detailsToUseForUpdate); - - if (detailsToUseForUpdate != null && !_force) - { - UpdateFunctionFromXaml(detailsToUseForUpdate); - _structuredTracer.ImportedWorkflowFromXaml(Guid.Empty, name); - continue; - } - - fileText = System.IO.File.ReadAllText(resolvedPath); - } - catch (System.AccessViolationException noPerms) - { - ErrorRecord er = new ErrorRecord(noPerms, "Workflow_XAMLfileNotAccessible", - ErrorCategory.PermissionDenied, path); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - - Dictionary requiredAssemblies = new Dictionary(StringComparer.OrdinalIgnoreCase); - if (DependentAssemblies != null && DependentAssemblies.Length > 0) - { - foreach (string filePath in DependentAssemblies) - { - requiredAssemblies.Add(System.IO.Path.GetFileNameWithoutExtension(filePath), filePath); - } - } - - FunctionDetails details = GenerateFunctionFromXaml(name, fileText, - requiredAssemblies, - dependentWorkflowContent.ToArray(), - dependentWorkflowAssemblyPath, - resolvedPath, - this.SessionState.LanguageMode); - - // check if the function cache already has the entry to this file - // If detailsToUseForUpdate is not null it is a forced import module - if (detailsToUseForUpdate != null) - { - // Update the cached function details - // - FunctionCache.TryUpdate(resolvedPath, details, detailsToUseForUpdate); - detailsToUseForUpdate = details; - } - else - { - if (FunctionCache.Count == FunctionCacheSize) - FunctionCache.Clear(); - - detailsToUseForUpdate = FunctionCache.GetOrAdd(resolvedPath, details); - } - - UpdateFunctionFromXaml(detailsToUseForUpdate); - - _structuredTracer.ImportedWorkflowFromXaml(Guid.Empty, name); - } - } - else - { - // If the file spec didn't resolve to a path, write an error - string message = string.Format(CultureInfo.CurrentUICulture, Resources.NoMatchingWorkflowWasFound, path); - FileNotFoundException fnf = new FileNotFoundException(message); - ErrorRecord er = new ErrorRecord(fnf, "Workflow_NoMatchingWorkflowXamlFileFound", - ErrorCategory.ResourceUnavailable, path); - WriteError(er); - Tracer.TraceErrorRecord(er); - } - } - } //if - - - } //ProcessRecord - - private static void CheckFileSignatureAsNeeded(bool checkSignatures, string filePath) - { - if (checkSignatures && !System.Management.Automation.Internal.SecuritySupport.IsProductBinary(filePath)) - { - throw new NotSupportedException(Resources.XamlWorkflowsNotSupported); - } - } - - private static readonly ConcurrentDictionary FunctionCache = - new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - - private const int FunctionCacheSize = 1000; - - /// - /// Load a workflow XAML file from the specified path and generate a PowerShell - /// function from the file. The name of the generated function will be the basename - /// of the XAML file. - /// - /// The name of workflow. - /// The xaml of workflow. - /// - /// Any workflows required by this workflow. - /// Path to the dependent assembly. - /// Resolved Path of the xaml - /// Language mode of source in which workflow should run - private static FunctionDetails GenerateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - string resolvedPath, - PSLanguageMode sourceLanguageMode) - { - if (name == null) - { - ArgumentNullException argNullException = new ArgumentNullException("name"); - Tracer.TraceException(argNullException); - throw argNullException; - } - - string modulePath = System.IO.Path.GetDirectoryName(resolvedPath); - string functionDefinition = CreateFunctionFromXaml( - name, - xaml, - requiredAssemblies, - dependentWorkflows, - dependentAssemblyPath, - null, - modulePath, - false, - "[CmdletBinding()]", - null, /* scriptContent */ - null, /* fullScript */ - null, /* rootWorkflowName */ - sourceLanguageMode, - null); - - FunctionDetails details = new FunctionDetails - {Name = name, FunctionDefinition = functionDefinition, Xaml = xaml}; - - return details; - } - - /// - /// Generate a function in the current session using the specified - /// function details - /// - /// details of the function - private void UpdateFunctionFromXaml(FunctionDetails details) - { - // Bind the command into the caller's command table. Note that if - // ~/ has been passed to this cmdlet, then the Set operation will also be logged - // the verbose stream. This is desirable as it shows the generated function... - string functionName = "function:\\script:" + details.Name; - - // This script block is defined as FullLanguage mode, since it contains - // no text that can be injected by the user. - ScriptBlock xamlFunctionDefinition = null; - PSLanguageMode oldLanguageMode = this.SessionState.LanguageMode; - try - { - this.SessionState.LanguageMode = PSLanguageMode.FullLanguage; - xamlFunctionDefinition = ScriptBlock.Create(details.FunctionDefinition); - } - finally - { - this.SessionState.LanguageMode = oldLanguageMode; - } - - WorkflowInfo workflow = new WorkflowInfo(details.Name, "", xamlFunctionDefinition, details.Xaml, null); - - SessionState.InvokeProvider.Item.Set(functionName, workflow); - } - - /// - /// Executes an instance of the workflow object graph identified by the passed - /// GUID, binding parameters from the Parameters hashtable. - /// - /// The powershell command. - /// The GUID used to identify the workflow to run. - /// The parameters to pass to the workflow instance. - /// The friendly name for the job - /// True if there was a PSParameters collection - /// - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "workFlowDefinition")] - public static ContainerParentJob StartWorkflowApplication(PSCmdlet command, string jobName, string workflowGuid, bool startAsync, - bool parameterCollectionProcessed, Hashtable[] parameters) - { - return StartWorkflowApplication(command, jobName, workflowGuid, startAsync, parameterCollectionProcessed, parameters, false); - } - - /// - /// Executes an instance of the workflow object graph identified by the passed - /// GUID, binding parameters from the Parameters hashtable. - /// - /// The powershell command. - /// The GUID used to identify the workflow to run. - /// The parameters to pass to the workflow instance. - /// The friendly name for the job - /// True if there was a PSParameters collection - /// - /// True if debugger is in active state. - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "workFlowDefinition")] - public static ContainerParentJob StartWorkflowApplication(PSCmdlet command, string jobName, string workflowGuid, bool startAsync, - bool parameterCollectionProcessed, Hashtable[] parameters, bool debuggerActive) - { - return StartWorkflowApplication(command, jobName, workflowGuid, startAsync, parameterCollectionProcessed, parameters, false, null); - } - - /// - /// Executes an instance of the workflow object graph identified by the passed - /// GUID, binding parameters from the Parameters hashtable. - /// - /// The powershell command. - /// The GUID used to identify the workflow to run. - /// The parameters to pass to the workflow instance. - /// The friendly name for the job - /// True if there was a PSParameters collection - /// - /// True if debugger is in active state. - /// Language mode of source creating workflow. - public static ContainerParentJob StartWorkflowApplication(PSCmdlet command, string jobName, string workflowGuid, bool startAsync, - bool parameterCollectionProcessed, Hashtable[] parameters, bool debuggerActive, string SourceLanguageMode) - { - Guid trackingGuid = Guid.NewGuid(); - _structuredTracer.BeginStartWorkflowApplication(trackingGuid); - - if (string.IsNullOrEmpty(workflowGuid)) - { - var exception = new ArgumentNullException("workflowGuid"); - Tracer.TraceException(exception); - throw exception; - } - - if (command == null) - { - var exception = new ArgumentNullException("command"); - Tracer.TraceException(exception); - throw exception; - } - - if (parameterCollectionProcessed) - { - StringBuilder paramString = new StringBuilder(); - StringBuilder computers = new StringBuilder(); - - // Add context for this trace record... - paramString.Append("commandName ='" + command.MyInvocation.MyCommand.Name + "'\n"); - paramString.Append("jobName ='" + jobName + "'\n"); - paramString.Append("workflowGUID = " + workflowGuid + "\n"); - paramString.Append("startAsync " + startAsync.ToString() + "\n"); - - // Stringize the parameter table to look like @{ k1 = v1; k2 = v2; ... } - if (parameters != null) - { - foreach (Hashtable h in parameters) - { - paramString.Append("@{"); - bool first = true; - foreach (DictionaryEntry e in h) - { - if (e.Key != null) - { - if (!first) - { - paramString.Append("'; "); - } - else - { - first = false; - } - paramString.Append(e.Key.ToString()); - paramString.Append("='"); - if (e.Value != null) - { - if (string.Equals(e.Key.ToString(), Constants.ComputerName, StringComparison.OrdinalIgnoreCase)) - { - computers.Append(LanguagePrimitives.ConvertTo(e.Value)); - } - - paramString.Append(e.Value.ToString()); - } - } - } - paramString.Append("}\n "); - } - } - - _structuredTracer.ParameterSplattingWasPerformed(paramString.ToString(), computers.ToString()); - } - - JobDefinition workFlowDefinition = DefinitionCache.Instance.GetDefinition(new Guid(workflowGuid)); - - if (workFlowDefinition == null) - { - var invalidOpException = new InvalidOperationException( - string.Format( - CultureInfo.CurrentUICulture, - Resources.InvalidWorkflowDefinitionState, - DefinitionCache.Instance.CacheSize)); - Tracer.TraceException(invalidOpException); - throw invalidOpException; - } - - ContainerParentJob myJob = null; - - /* - * Iterate through the list of parameters hashtables. Each table may, in turn - * contain a list of computers to target. - */ - var parameterDictionaryList = new List>(); - if (parameters == null || parameters.Length == 0) - { - // There were no parameters so add a dummy parameter collection - // to ensure that at least one child job is created... - parameterDictionaryList.Add(new Dictionary()); - } - else if (parameters.Length == 1 && !parameterCollectionProcessed) - { - // If the parameter collection is just the $PSBoundParameters - // i.e. did not come through the PSParameterCollection list - // then just we can use it as is since it's already been processed through the parameter - // binder... - Hashtable h = parameters[0]; - Dictionary paramDictionary = new Dictionary(); - foreach (var key in parameters[0].Keys) - { - paramDictionary.Add((string)key, h[key]); - } - parameterDictionaryList.Add(paramDictionary); - } - else - { - // Otherwise, iterate through the list of parameter collections using - // a function to apply the parameter binder transformations on each item in the list. - // on the hashtable. - - // Convert the parameters into dictionaries. - foreach (Hashtable h in parameters) - { - if (h != null) - { - Dictionary canonicalEntry = ConvertToParameterDictionary(h); - - // Add parameter dictionary to the list... - parameterDictionaryList.Add(canonicalEntry); - } - } - } - - JobInvocationInfo specification = new JobInvocationInfo(workFlowDefinition, parameterDictionaryList); - specification.Name = jobName; - - specification.Command = command.MyInvocation.InvocationName; - _structuredTracer.BeginCreateNewJob(trackingGuid); - myJob = command.JobManager.NewJob(specification) as ContainerParentJob; - _structuredTracer.EndCreateNewJob(trackingGuid); - - // Pass the source language mode to the workflow job so that it can be - // applied during activity execution. - PSLanguageMode sourceLanguageModeValue; - PSLanguageMode? sourceLanguageMode = null; - if (!string.IsNullOrEmpty(SourceLanguageMode) && Enum.TryParse(SourceLanguageMode, out sourceLanguageModeValue)) - { - sourceLanguageMode = sourceLanguageModeValue; - } - - // Raise engine event of new WF job start for debugger, if - // debugger is active (i.e., has breakpoints set). - if (debuggerActive) - { - RaiseWFJobEvent(command, myJob, startAsync); - } - - if (startAsync) - { - foreach(PSWorkflowJob childjob in myJob.ChildJobs) - { - if (!PSSessionConfigurationData.IsServerManager) - { - childjob.EnableStreamUnloadOnPersistentState(); - } - childjob.SourceLanguageMode = sourceLanguageMode; - } - myJob.StartJobAsync(); - } - else - { - // If the job is running synchronously, the PSWorkflowJob(s) need to know - // so that errors are decorated correctly. - foreach (PSWorkflowJob childJob in myJob.ChildJobs) - { - childJob.SynchronousExecution = true; - childJob.SourceLanguageMode = sourceLanguageMode; - } - myJob.StartJob(); - } - - // write an event specifying that job creation is done - _structuredTracer.EndStartWorkflowApplication(trackingGuid); - _structuredTracer.TrackingGuidContainerParentJobCorrelation(trackingGuid, myJob.InstanceId); - - return myJob; - } - - private static void RaiseWFJobEvent(PSCmdlet command, ContainerParentJob job, bool startAsync) - { - var eventManager = command.Events; - Debug.Assert(eventManager != null, "Event Manager cannot be null."); - - foreach (PSWorkflowJob cJob in job.ChildJobs) - { - if (cJob == null) continue; - - // Raise event for each child job. - eventManager.GenerateEvent( - sourceIdentifier: PSEngineEvent.WorkflowJobStartEvent, - sender: null, - args: new object[] { new PSJobStartEventArgs(cJob, cJob.PSWorkflowDebugger, startAsync) }, - extraData: null, - processInCurrentThread: true, - waitForCompletionInCurrentThread: true); - } - } - - private static Dictionary ConvertToParameterDictionary(Hashtable h) - { - if (h == null) - return null; - - Dictionary paramDictionary = new Dictionary(); - - foreach (var key in h.Keys) - { - paramDictionary.Add((string)key, h[key]); - } - return paramDictionary; - } - - /// - /// Retrieve a localized error message saying that only a single default parameter collection can be specified - /// - public static string ParameterErrorMessage - { - get - { - return Resources.OnlyOneDefaultParameterCollectionAllowed; - } - } - - /// - /// Retrieve a localized error message saying that AsJob, JobName and PSParameterCollection cannot be specified as entries to PSParameterCollection - /// - public static string InvalidPSParameterCollectionEntryErrorMessage - { - get - { - return Resources.AsJobAndJobNameNotAllowed; - } - } - - /// - /// Retrieve a localized error message saying that the only AsJob, JobName and InputObject can be used outside of PSParameterCollection. - /// - public static string InvalidPSParameterCollectionAdditionalErrorMessage - { - get - { - return Resources.ParameterCollectionOnlyUsedWithAsJobAndJobName; - } - } - - /// - /// Retrieve a localized error message saying that starting the workflow failed... - /// - public static string UnableToStartWorkflowMessageMessage - { - get - { - return Resources.UnableToStartWorkflow; - } - } - - /// - /// Convert the Xaml based workflow into object-graph activity. - /// - /// Xaml representing workflow. - /// Activity representing the workflow. - internal static Activity ConvertXamlToActivity(string xaml) - { - Tracer.WriteMessage("Trying to convert Xaml into Activity."); - - if (string.IsNullOrEmpty(xaml)) - { - // Tracer* _M3PErrorImportingWorkflowFromXaml - ArgumentNullException exception = new ArgumentNullException("xaml", Resources.XamlNotNull); - Tracer.TraceException(exception); - throw exception; - } - - StringReader sReader = new StringReader(xaml); - Activity workflow; - - try - { - workflow = ActivityXamlServices.Load(sReader); - } - finally - { - sReader.Dispose(); - } - //_M3PImportedWorkflowFromXaml - - return workflow; - } - - internal static ConcurrentDictionary compiledAssemblyCache = new ConcurrentDictionary(); - - internal static string CompileDependentWorkflowsToAssembly(string[] dependentWorkflows, Dictionary requiredAssemblies) - { - Debug.Assert(dependentWorkflows.Length > 0, "There should be atleast one dependent workflow."); - Debug.Assert((PSWorkflowRuntime.Instance.Configuration).GetType() == typeof(PSWorkflowConfigurationProvider), "type mismatch error."); - - PSWorkflowConfigurationProvider config = (PSWorkflowConfigurationProvider)PSWorkflowRuntime.Instance.Configuration; - - // Calculating Hashcode - string hashcode = string.Empty; - - List codes = new List(); - foreach (string dependentWorkflow in dependentWorkflows) - { - codes.Add(dependentWorkflow.GetHashCode()); - } - codes.Sort(); - - foreach (int code in codes) - { - hashcode += code.ToString(CultureInfo.InvariantCulture); - } - - Debug.Assert(string.IsNullOrEmpty(hashcode) == false, "Hash code should not be null or empty"); - - WorkflowRuntimeCompilation compiler = null; - - // check if the hashcode already exist in cache - if (compiledAssemblyCache.ContainsKey(hashcode)) - { - compiledAssemblyCache.TryGetValue(hashcode, out compiler); - } - - if (compiler == null) - { - try - { - compiler = new WorkflowRuntimeCompilation(); - - compiler.Compile(new List(dependentWorkflows), requiredAssemblies); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - // sanity check to block the unbounded increase in the cache size - if (compiledAssemblyCache.Keys.Count >= config.CompiledAssemblyCacheLimit) - { - compiledAssemblyCache.Clear(); - } - - compiledAssemblyCache.TryAdd(hashcode, compiler); - } - - // Throw an error if there was a compilation problem - if (!compiler.BuildReturnedCode || !File.Exists(compiler.AssemblyPath)) - { - string message = string.Format(CultureInfo.CurrentUICulture, - Resources.CompilationErrorWhileBuildingWorkflows, - compiler.BuildLogPath); - throw new InvalidDataException(message); - } - - return compiler.AssemblyPath; - } - - /// - /// Convert the Xaml based workflow into object-graph activity with additional xamls assembly provided. - /// - /// Xaml representing workflow. - /// Any workflows required by this workflow. - /// - /// The path to the compiled assembly for any dependent workflows. - /// The compiled assembly. - /// The compiled assembly name. - /// Activity representing the workflow. - [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", - MessageId = "System.Reflection.Assembly.LoadFrom")] - internal static Activity ConvertXamlToActivity(string xaml, string[] dependentWorkflows, - Dictionary requiredAssemblies, - ref string compiledAssemblyPath, ref Assembly compiledAssembly, - ref string compiledAssemblyName) - { - _structuredTracer.ImportedWorkflowFromXaml(Guid.Empty, string.Empty); - Tracer.WriteMessage("Trying to convert Xaml into Activity and using additional xamls assembly."); - - if (string.IsNullOrEmpty(xaml)) - { - ArgumentNullException exception = new ArgumentNullException("xaml", Resources.XamlNotNull); - Tracer.TraceException(exception); - _structuredTracer.ErrorImportingWorkflowFromXaml(Guid.Empty, exception.Message); - throw exception; - } - - Activity workflow; - XamlXmlReaderSettings readerSettings = new XamlXmlReaderSettings(); - - // If they passed in an assembly path, use it - if (!String.IsNullOrEmpty(compiledAssemblyPath)) - { - if (compiledAssembly == null && String.IsNullOrEmpty(compiledAssemblyName)) - { - compiledAssembly = Assembly.LoadFrom(compiledAssemblyPath); - compiledAssemblyName = compiledAssembly.GetName().Name; - } - - readerSettings.LocalAssembly = compiledAssembly; - } - else if ((dependentWorkflows != null) && (dependentWorkflows.Length > 0)) - { - // Otherwise, it's a first-time compilation - string assemblyPath = CompileDependentWorkflowsToAssembly(dependentWorkflows, requiredAssemblies); - readerSettings.LocalAssembly = Assembly.LoadFrom(assemblyPath); - compiledAssemblyPath = assemblyPath; - compiledAssembly = Assembly.LoadFrom(compiledAssemblyPath); - compiledAssemblyName = compiledAssembly.GetName().Name; - - readerSettings.LocalAssembly = compiledAssembly; - } - - using (StringReader sReader = new StringReader(xaml)) - { - XamlXmlReader reader = new XamlXmlReader(sReader, readerSettings); - workflow = ActivityXamlServices.Load(reader); - } - - _structuredTracer.ImportedWorkflowFromXaml(Guid.Empty, string.Empty); - - return workflow; - } - - private class FunctionDetails - { - internal string Name { get; set; } - internal string FunctionDefinition { get; set; } - internal string Xaml { get; set; } - } - - #region Moved from AstToWorkflowConverter - - private const string functionNamePattern = "^[a-zA-Z0-9-_]*$"; - /// - /// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will - /// run the workflow. - /// - /// Workflow name - /// Workflow Xaml definition - /// Required assemblies - /// Dependent workflows - /// Path for dependent assemblies - /// Workflow parameters - /// Module path - /// True if this is script workflow - /// the attribute string to use for the workflow, should be '[CmdletBinding()]' - /// - public static string CreateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - Dictionary parameterValidation, - string modulePath, - bool scriptWorkflow, - string workflowAttributes - ) - { - return CreateFunctionFromXaml( - name, - xaml, - requiredAssemblies, - dependentWorkflows, - dependentAssemblyPath, - parameterValidation, - modulePath, - scriptWorkflow, - workflowAttributes, - null); - } - - /// - /// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will - /// run the workflow. - /// - /// Workflow name - /// Workflow Xaml definition - /// Required assemblies - /// Dependent workflows - /// Path for dependent assemblies - /// Workflow parameters - /// Module path - /// True if this is script workflow - /// the attribute string to use for the workflow, should be '[CmdletBinding()]' - /// File path containing script content. - /// - public static string CreateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - Dictionary parameterValidation, - string modulePath, - bool scriptWorkflow, - string workflowAttributes, - string scriptContent - ) - { - return CreateFunctionFromXaml( - name, - xaml, - requiredAssemblies, - dependentWorkflows, - dependentAssemblyPath, - parameterValidation, - modulePath, - scriptWorkflow, - workflowAttributes, - scriptContent, - null); - } - - /// - /// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will - /// run the workflow. - /// - /// Workflow name - /// Workflow Xaml definition - /// Required assemblies - /// Dependent workflows - /// Path for dependent assemblies - /// Workflow parameters - /// Module path - /// True if this is script workflow - /// the attribute string to use for the workflow, should be '[CmdletBinding()]' - /// File path containing script content. - /// Full source script. - /// - public static string CreateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - Dictionary parameterValidation, - string modulePath, - bool scriptWorkflow, - string workflowAttributes, - string scriptContent, - string fullScript - ) - { - return CreateFunctionFromXaml(name, xaml, requiredAssemblies, dependentWorkflows, dependentAssemblyPath, - parameterValidation, modulePath, scriptWorkflow, workflowAttributes, scriptContent, fullScript, null); - } - - /// - /// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will - /// run the workflow. - /// - /// Workflow name - /// Workflow Xaml definition - /// Required assemblies - /// Dependent workflows - /// Path for dependent assemblies - /// Workflow parameters - /// Module path - /// True if this is script workflow - /// the attribute string to use for the workflow, should be '[CmdletBinding()]' - /// File path containing script content. - /// Full source script. - /// Only root Workflow will be compiled - /// - public static string CreateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - Dictionary parameterValidation, - string modulePath, - bool scriptWorkflow, - string workflowAttributes, - string scriptContent, - string fullScript, - string rootWorkflowName - ) - { - return CreateFunctionFromXaml(name, xaml, requiredAssemblies, dependentWorkflows, dependentAssemblyPath, parameterValidation, modulePath, - scriptWorkflow, workflowAttributes, scriptContent, fullScript, rootWorkflowName, null, null); - } - - /// - /// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will - /// run the workflow. - /// - /// Workflow name - /// Workflow Xaml definition - /// Required assemblies - /// Dependent workflows - /// Path for dependent assemblies - /// Workflow parameters - /// Module path - /// True if this is script workflow - /// the attribute string to use for the workflow, should be '[CmdletBinding()]' - /// File path containing script content. - /// Full source script. - /// Only root Workflow will be compiled - /// Language mode of source that is creating the workflow - /// Optional collection of parameter attribute Asts - /// - public static string CreateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - Dictionary parameterValidation, - string modulePath, - bool scriptWorkflow, - string workflowAttributes, - string scriptContent, - string fullScript, - string rootWorkflowName, - PSLanguageMode? sourceLanguageMode, - ReadOnlyCollection attributeAstCollection - ) - { - // check to see if the specified name is allowed - if (!Regex.IsMatch(name, functionNamePattern)) - { - throw new PSArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.WorkflowNameInvalid, name)); - } - - WorkflowJobDefinition definition = null; - Activity workflow = null; - - if (scriptWorkflow) - { - workflow = DefinitionCache.Instance.GetActivityFromCache(xaml, out definition); - } - - if (workflow == null) - { - definition = new WorkflowJobDefinition(typeof(WorkflowJobSourceAdapter), name, null, modulePath, dependentWorkflows, dependentAssemblyPath, requiredAssemblies, xaml); - definition.IsScriptWorkflow = scriptWorkflow; - bool windowsWorkflow; - workflow = DefinitionCache.Instance.CompileActivityAndSaveInCache(definition, null, requiredAssemblies, - out windowsWorkflow, rootWorkflowName); - } - - definition.WorkflowScriptFile = scriptContent; - definition.WorkflowFullScript = fullScript; - - // this can throw exceptions if the xaml is malformed. - DynamicActivity daBody = workflow as DynamicActivity; - - StringBuilder innerParamDefinitions = new StringBuilder(); - StringBuilder outerParamDefinitions = new StringBuilder(); - string workflowGuid = definition.InstanceId.ToString(); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "Generating function for name: {0}, WFGuid: {1}", name, workflowGuid)); - - // String to hold any updates we do for parameter defaults - List parametersWithDefaults = new List(); - string defaultUpdates = ""; - - // If the workflow is a DynamicActivity, we can use the Properties - // property to retrieve parameters to the workflow and synthesize - // PowerShell parameter declarations. - if (daBody != null) - { - foreach (var p in daBody.Properties) - { - // Skip out arguments - if (typeof(System.Activities.OutArgument).IsAssignableFrom(p.Type)) - { - continue; - } - - // If the parameter name is one of the expected collisions, don't add it to the list. - if (p.Name.Equals(Constants.ComputerName, StringComparison.OrdinalIgnoreCase) || p.Name.Equals(Constants.PrivateMetadata, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (p.Name.Equals("InputObject", StringComparison.OrdinalIgnoreCase) || p.Name.Equals("AsJob", StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - string paramTypeName = (string)LanguagePrimitives.ConvertTo(p.Type.GetGenericArguments()[0], - typeof(string), CultureInfo.InvariantCulture); - - string mandatory = ""; - string parameterDefault = ""; - - // If we got specific validations to add, add those - // We only get these in script-based workflow case, but not in XAML case. - if ((parameterValidation != null) && parameterValidation.ContainsKey(p.Name)) - { - ParameterAst parameter = parameterValidation[p.Name]; - - foreach (AttributeBaseAst attribute in parameter.Attributes) - { - innerParamDefinitions.Append(attribute.ToString()); - innerParamDefinitions.Append("\n "); - - var attributeAst = attribute as AttributeAst; - if (attributeAst == null || !string.Equals(attribute.TypeName.Name, "Parameter", StringComparison.OrdinalIgnoreCase)) - { - // If we have a Credential Attribute, it has been added to the inner function, it does not need to be added to the outer definition. - // This will prevent prompting for the cred twice. - if (!string.Equals(attribute.TypeName.FullName, "System.Management.Automation.CredentialAttribute", StringComparison.OrdinalIgnoreCase)) - { - outerParamDefinitions.Append(attribute.ToString()); - outerParamDefinitions.Append("\n "); - } - - continue; - } - - string updatedAttribute = "[Parameter("; - bool first = true; - foreach (var namedAttribute in attributeAst.NamedArguments) - { - if (string.Equals(namedAttribute.ArgumentName, "Mandatory", - StringComparison.OrdinalIgnoreCase)) - continue; - if (string.Equals(namedAttribute.ArgumentName, "ValueFromPipeline", StringComparison.OrdinalIgnoreCase) - && string.Equals(namedAttribute.Argument.Extent.Text, "$true", StringComparison.OrdinalIgnoreCase)) - { - throw new PSInvalidOperationException(Resources.ValueFromPipelineNotSupported); - } - - if (!first) updatedAttribute += ","; - first = false; - updatedAttribute += namedAttribute.ToString(); - } - updatedAttribute += ")]"; - outerParamDefinitions.Append(updatedAttribute); - outerParamDefinitions.Append("\n "); - } - - if (parameter.DefaultValue != null) - { - parameterDefault = " = " + parameter.DefaultValue.ToString(); - parametersWithDefaults.Add("'" + p.Name + "'"); - } - } - // Otherwise, add our default treatment - // XAML workflows only go through this code path. - // Scriptworkflow ALSO comes through this path. - else - { - // If the parameter is an In parameter with the RequiredArgument attribute then make it mandatory - if (typeof(System.Activities.InArgument).IsAssignableFrom(p.Type)) - { - if (p.Attributes != null) - { - foreach (var attribute in p.Attributes) - { - // Check the type of the attribute - if (attribute.TypeId.GetType() == typeof(RequiredArgumentAttribute)) - { - mandatory = "[Parameter(Mandatory=$true)] "; - } - } - } - } - } - - innerParamDefinitions.Append(String.Format(CultureInfo.InvariantCulture, - "{0}[{1}] ${2}{3},\n ", - mandatory, paramTypeName, p.Name, parameterDefault)); - outerParamDefinitions.Append(String.Format(CultureInfo.InvariantCulture, - "[{0}] ${1}{2},\n ", - paramTypeName, p.Name, parameterDefault)); - } - - if (parametersWithDefaults.Count > 0) - { - defaultUpdates = @" - # Update any parameters that had default values in the workflow - $parametersWithDefaults = @(" + String.Join(", ", parametersWithDefaults.ToArray()) + ")\n"; - } - else - { - defaultUpdates = @" - # None of the workflow parameters had default values - $parametersWithDefaults = @()"; - } - - // Add code into the function to handle PSParameterCollection parameter if present. - defaultUpdates += @" - trap { break } - $parameterCollectionProcessed = $false - $PSParameterCollectionDefaultsMember = $null - $suspendOnError = $false - - if ($PSBoundParameters.ContainsKey('PSParameterCollection')) - { - # validate parameters used with PSParameterCollection - foreach ($pa in $PSBoundParameters.Keys) - { - if ($pa -eq 'JobName' -or $pa -eq 'AsJob' -or $pa -eq 'InputObject' -or $pa -eq 'PSParameterCollection') - { - continue - } - $msg = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::InvalidPSParameterCollectionAdditionalErrorMessage; - throw (New-Object System.Management.Automation.ErrorRecord $msg, StartWorkflow.InvalidArgument, InvalidArgument, $PSParameterCollection) - } - $parameterCollectionProcessed = $true - - # See if there is a defaults collection, indicated by '*' - foreach ($collection in $PSParameterCollection) - { - if ($collection['" + Constants.ComputerName + @"'] -eq '*' ) - { - if ($PSParameterCollectionDefaultsMember -ne $null) - { - $msg = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::ParameterErrorMessage; - throw ( New-Object System.Management.Automation.ErrorRecord $msg, StartWorkflow.InvalidArgument, InvalidArgument, $PSParameterCollection) - } - $PSParameterCollectionDefaultsMember = $collection; - foreach($parameter in $parametersWithDefaults) - { - if(! $collection.ContainsKey($parameter)) - { - $collection[$parameter] = (Get-Variable $parameter).Value - } - } - } - } - - $PSParameterCollection = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::MergeParameterCollection( - $PSParameterCollection, $PSParameterCollectionDefaultsMember) - - # canonicalize each collection... - $PSParameterCollection = foreach ( $c in $PSParameterCollection) { - if($c.containskey('AsJob') -or $c.containsKey('JobName') -or $c.containsKey('PSParameterCollection') -or $c.containsKey('InputObject')) - { - $msg = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::InvalidPSParameterCollectionEntryErrorMessage; - throw ( New-Object System.Management.Automation.ErrorRecord $msg, StartWorkflow.InvalidArgument, InvalidArgument, $PSParameterCollection) - } - - if ($c['" + Constants.ErrorAction + @"'] -eq ""Suspend"") - { - $suspendOnError = $true - $c['" + Constants.ErrorAction + @"'] = ""Continue"" - } - - $validated = & """ + name + @""" @c - $validated['" + Constants.PSSuspendOnError + @"'] = $suspendOnError - $validated - } - - # If there was no '*' collection, added the parameter defaults - # to each individual collection if the parameter isn't already there... - if (-not $PSParameterCollectionDefaultsMember) - { - foreach ($collection in $PSParameterCollection) - { - foreach($parameter in $parametersWithDefaults) - { - if(! $collection.ContainsKey($parameter)) - { - $collection[$parameter] = (Get-Variable $parameter).Value - } - } - } - } - } - else - { - if ($PSBoundParameters['" + Constants.ErrorAction + @"'] -eq ""Suspend"") - { - $errorActionPreference = ""Continue"" - $suspendOnError = $true - $PSBoundParameters['" + Constants.ErrorAction + @"'] = ""Continue"" - } - - $PSBoundParameters = & """ + name + @""" @PSBoundParameters - - # no PSParameterCollection so add the default values to PSBoundParameters - foreach($parameter in $parametersWithDefaults) - { - if(! $PSBoundParameters.ContainsKey($parameter)) - { - $PSBoundParameters[$parameter] = (Get-Variable $parameter).Value - } - } - } - "; - } - - // Escaping the single quotes from the module path - modulePath = CodeGeneration.EscapeSingleQuotedStringContent(modulePath); - - //Generate the PowerShell function template - string functionPrefixTemplate = AddCommonWfParameters(false, workflowAttributes); - string validationFunctionPrefixTemplate = AddCommonWfParameters(true, workflowAttributes); - - string outerParamDefinitionsString = outerParamDefinitions.ToString(); - outerParamDefinitionsString = CodeGeneration.EscapeFormatStringContent(outerParamDefinitionsString); - string functionPrefix = String.Format(CultureInfo.InvariantCulture, functionPrefixTemplate, outerParamDefinitionsString); - - // Generate the param block for the synthesized function - string innerParamDefinitionsString = innerParamDefinitions.ToString(); - innerParamDefinitionsString = CodeGeneration.EscapeFormatStringContent(innerParamDefinitionsString); - // For the parameter validation function, add an extra param definition for $PSInputCollection - string validationFunctionPrefix = String.Format(CultureInfo.InvariantCulture, validationFunctionPrefixTemplate, innerParamDefinitionsString + "$PSInputCollection"); - - StringBuilder completeFunctionDefinitionTemplate = new StringBuilder(); - completeFunctionDefinitionTemplate.AppendLine(functionPrefix); - completeFunctionDefinitionTemplate.AppendLine(" begin {{"); - completeFunctionDefinitionTemplate.AppendLine(" function " + name + " {{"); - completeFunctionDefinitionTemplate.AppendLine(validationFunctionPrefix); - completeFunctionDefinitionTemplate.AppendLine(" $PSBoundParameters"); - completeFunctionDefinitionTemplate.AppendLine(" }}"); - completeFunctionDefinitionTemplate.AppendLine(FunctionBodyTemplate); - - // Mark the function definition with sourceLanguageMode (language mode that function can run under, i.e., - // as trusted or not trusted), unless the workflow script is marked with the "SecurityCritical" attribute in - // which case the function will always be run under the current system lock down setting. - bool isSecurityCritical = ContainsSecurityCriticalAttribute(attributeAstCollection); - string sourceLanguageModeStr = (!isSecurityCritical && (sourceLanguageMode != null)) ? sourceLanguageMode.ToString() : string.Empty; - - // Combine the pieces to create the complete function - string functionDefinition = String.Format(CultureInfo.InvariantCulture, completeFunctionDefinitionTemplate.ToString(), - defaultUpdates, workflowGuid, modulePath, sourceLanguageModeStr); - -#if DEBUG - // Verify that the generated function is valid powershell. This is only an issue when changing the - // generation code so it's debug only... - Collection templateErrors = null; - System.Management.Automation.PSParser.Tokenize(functionDefinition, out templateErrors); - if (templateErrors != null && templateErrors.Count > 0) - { - StringBuilder message = new StringBuilder(); - foreach (PSParseError parseErr in templateErrors) - { - message.Append(parseErr.Token.Content).Append(':').Append(parseErr.Message).Append("\n"); - } - message.Append("`nFunction code:`n").Append(functionDefinition); - throw new InvalidOperationException(message.ToString()); - } -#endif - workflow = null; - daBody = null; - - // strip the comments in fre build -#if !DEBUG - functionDefinition = System.Text.RegularExpressions.Regex.Replace(functionDefinition, "^ *\\#.*$", "", RegexOptions.Multiline); -#endif - return functionDefinition; - } - - private static Type securityCriticalAttributeType = typeof(System.Security.SecurityCriticalAttribute); - private static bool ContainsSecurityCriticalAttribute(ReadOnlyCollection attributeAsts) - { - if (attributeAsts != null) - { - foreach (var attributeAst in attributeAsts) - { - if (attributeAst.TypeName.GetReflectionAttributeType() == securityCriticalAttributeType) - { - return true; - } - } - } - - return false; - } - - - /// - /// - /// - /// - /// - /// - public static Hashtable[] MergeParameterCollection(Hashtable[] parameterCollection, Hashtable defaultsParameterCollection) - { - if (defaultsParameterCollection == null) return parameterCollection; - - List updatedParameterCollection = new List(); - foreach (var collection in parameterCollection) - { - if (collection.ContainsKey(Constants.ComputerName)) - { - var cnobject = collection[Constants.ComputerName]; - var pso = cnobject as PSObject; - string name; - if (pso != null) - name = pso.BaseObject as string; - else - name = cnobject as string; - if (name != null && name.Equals("*")) continue; - } - foreach (var parameterName in defaultsParameterCollection.Keys) - { - if (parameterName.Equals(Constants.ComputerName)) continue; - if (collection.ContainsKey(parameterName)) continue; - collection.Add(parameterName, defaultsParameterCollection[parameterName]); - } - - updatedParameterCollection.Add(collection); - } - return updatedParameterCollection.ToArray(); - } - - /// - /// Adds common workflow parameters - /// - /// - /// - /// - internal static string AddCommonWfParameters(bool innerfunction, string workflowAttributes) - { - // If changes are made to the common workflow parameters, then Intellisense will need to be updated as well - // as the strings are hard coded, see admin\monad\src\engine\CommandCompletion\PseudoParameterBinder.cs. - string[] commonParameters = new string[] - { - "[string[]] $" + Constants.ComputerName, - "[ValidateNotNullOrEmpty()] $" + Constants.Credential, - "[uint32] $" + Constants.ConnectionRetryCount, - "[uint32] $" + Constants.ConnectionRetryIntervalSec, - "[ValidateRange(1, " + Constants.Int32MaxValueDivideByThousand + ")][uint32] $" + Constants.PSRunningTime, - "[ValidateRange(1, " + Constants.Int32MaxValueDivideByThousand + ")][uint32] $" + Constants.PSElapsedTime, - "[bool] $" + Constants.Persist, - "[ValidateNotNullOrEmpty()] [System.Management.Automation.Runspaces.AuthenticationMechanism] $" + Constants.Authentication, - "[ValidateNotNullOrEmpty()][System.Management.AuthenticationLevel] $" + Constants.AuthenticationLevel, - "[ValidateNotNullOrEmpty()] [string] $" + Constants.ApplicationName, - "[uint32] $" + Constants.Port, - "[switch] $" + Constants.UseSSL, - "[ValidateNotNullOrEmpty()] [string] $" + Constants.ConfigurationName, - "[ValidateNotNullOrEmpty()][string[]] $" + Constants.ConnectionURI, - "[switch] $" + Constants.AllowRedirection, - "[ValidateNotNullOrEmpty()][System.Management.Automation.Remoting.PSSessionOption] $" + Constants.SessionOption, - "[ValidateNotNullOrEmpty()] [string] $" + Constants.CertificateThumbprint, - "[hashtable] $" + Constants.PrivateMetadata, - "[switch] $" + Constants.AsJob, - "[string] $" + Constants.JobName, - "$InputObject" - }; - - string functionPrefix = ""; - foreach (string workflowAttribute in workflowAttributes.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)) - { - functionPrefix += - @" - " + CodeGeneration.EscapeFormatStringContent(workflowAttribute); - } - - functionPrefix += - @" - param ( - {0}"; - - // For inner function, don't add -PSParameterCollection param. AsJob and JobName are needed in the inner function for - // parameter validation in non-PSParameterCollection cases. For PSParameterCollection, they will be checked for - // with a separate check. - // We want it at workflow function, but not in the subfunction that validates the parameter for PSParameterCollection - if (!innerfunction) - { - functionPrefix += - @" - [hashtable[]] $" + Constants.PSParameterCollection; - } - - //Add all the parametersets to each of the common WF parameter - foreach (string commonParameter in commonParameters) - { - if (commonParameter.Equals("$InputObject")) - { - functionPrefix += @", - " + "[Parameter(ValueFromPipeline=$true)]" + commonParameter; - } - else - { - functionPrefix += @", - " + commonParameter; - } - } - - //Close the param block - functionPrefix += @" - )"; - - return functionPrefix; - } - - const string FunctionBodyTemplate = @" - - $PSInputCollection = New-Object 'System.Collections.Generic.List[PSObject]' - }} - - process {{ - if ($PSBoundParameters.ContainsKey('InputObject')) - {{ - $PSInputCollection.Add($InputObject) - }} - }} - - end {{ - - {0} - if ($PSBoundParameters['" + Constants.Credential + @"']) - {{ - $CredentialTransform = New-Object System.Management.Automation.CredentialAttribute - $LocalCredential = $CredentialTransform.Transform($ExecutionContext, $PSCredential) - $PSBoundParameters['PSCredential'] = [system.management.automation.pscredential]$LocalCredential - - if (!$PSBoundParameters['" + Constants.ComputerName + @"'] -and !$PSBoundParameters['" + Constants.ConnectionURI + @"']) - {{ - $PSBoundParameters['" + Constants.ComputerName + @"'] = New-Object string @(,'localhost') - }} - }} - - # Extract the job name if specified - $jobName = '' - if ($PSBoundParameters['" + Constants.JobName + @"']) - {{ - $jobName = $PSBoundParameters['" + Constants.JobName + @"'] - [void] $PSBoundParameters.Remove('" + Constants.JobName + @"'); - }} - - # Extract the PSParameterCollection if specified - [hashtable[]] $jobSpecifications = @() - $parametersCollection = $null; - if ($PSBoundParameters['" + Constants.PSParameterCollection + @"']) - {{ - $parameterSCollection = $PSBoundParameters['" + Constants.PSParameterCollection + @"'] - [void] $PSBoundParameters.Remove('" + Constants.PSParameterCollection + @"'); - }} - - # Remove the InputObject parameter from the bound parameters collection - if ($PSBoundParameters['" + Constants.InputObject + @"']) - {{ - [void] $PSBoundParameters.Remove('" + Constants.InputObject + @"'); - }} - - # Remove parameters consumed by this function or PowerShell itself - $null = $PSBoundParameters.Remove('" + Constants.AsJob + @"') - $null = $psBoundParameters.Remove('" + Constants.WarningVariable + @"') - $null = $psBoundParameters.Remove('" + Constants.ErrorVariable + @"') - $null = $psBoundParameters.Remove('" + Constants.OutVariable + @"') - $null = $psBoundParameters.Remove('" + Constants.OutBuffer + @"') - $null = $psBoundParameters.Remove('" + Constants.PipelineVariable + @"') - $null = $psBoundParameters.Remove('" + Constants.InformationVariable + @"') - - # Add parameter to add the path of the workflow module, needed by Import-LocalizedData - # which uses this as a base path to find localized content files. - $psBoundParameters['" + Constants.ModulePath + @"'] = '{2}' - - # Variable that contains the source language mode. - [string] $SourceLanguageMode = '{3}' - - if (Test-Path variable:\PSSenderInfo) - {{ - $psBoundParameters['" + Constants.PSSenderInfo + @"'] = $PSSenderInfo - }} - - $psBoundParameters['" + Constants.PSCurrentDirectory + @"'] = $pwd.Path - $psBoundParameters['" + Constants.PSSuspendOnError + @"'] = $suspendOnError - - # Process author-specified metadata which is set using - # the Private member in the module manifest - $myCommand = $MyInvocation.MyCommand - $myModule = $myCommand.Module - if ($myModule) - {{ - # The function was defined in a module so look for - # the PrivateData member - [Hashtable] $privateData = $myModule.PrivateData -as [Hashtable] - - if ($privateData) - {{ - # Extract the nested hashtable corresponding to this - # command - [hashtable] $authorMetadata = $privateData[$myCommand.Name] - if ($authorMetadata) - {{ - # Copy the author-supplied hashtable so we can safely - # modify it. - $authorMetadata = @{{}} + $authorMetadata - if ($psBoundParameters['PSPrivateMetadata']) - {{ - # merge in the user-supplied metadata - foreach ($pair in $psPrivateMetadata.GetEnumerator()) - {{ - $authorMetadata[$pair.Key] = $pair.Value - }} - }} - # and update the bound parameter to include the merged data - $psBoundParameters['" + Constants.PrivateMetadata + @"'] = $authorMetadata - }} - }} - }} - - # Add in the input collection if there wasn't one explicitly passed - # which can only happen through PSParameterCollection - if (! $PSBoundParameters['" + Constants.PSInputCollection + @"']) - {{ - $PSBoundParameters['" + Constants.PSInputCollection + @"'] = $PSInputCollection - }} - - # Populate Verbose / Debug / Error from preference variables - if (-not $PSBoundParameters.ContainsKey('" + Constants.Verbose + @"')) - {{ - if($verbosePreference -in ""Continue"",""Inquire"") - {{ - $PSBoundParameters['" + Constants.Verbose + @"'] = [System.Management.Automation.SwitchParameter]::Present - }} - }} - - if (-not $PSBoundParameters.ContainsKey('" + Constants.Debug + @"')) - {{ - if($debugPreference -in ""Continue"",""Inquire"") - {{ - $PSBoundParameters['" + Constants.Debug + @"'] = [System.Management.Automation.SwitchParameter]::Present - }} - }} - - if (-not $PSBoundParameters.ContainsKey('" + Constants.ErrorAction + @"')) - {{ - $PSBoundParameters['" + Constants.ErrorAction + @"'] = $errorActionPreference - }} - - if(Test-Path variable:\errorActionPreference) - {{ - $errorAction = $errorActionPreference - }} - else - {{ - $errorAction = ""Continue"" - }} - - if ($PSBoundParameters['" + Constants.ErrorAction + @"'] -eq ""SilentlyContinue"") - {{ - $errorAction = ""SilentlyContinue"" - }} - - if($PSBoundParameters['" + Constants.ErrorAction + @"'] -eq ""Ignore"") - {{ - $PSBoundParameters['" + Constants.ErrorAction + @"'] = ""SilentlyContinue"" - $errorAction = ""SilentlyContinue"" - }} - - if ($PSBoundParameters['" + Constants.ErrorAction + @"'] -eq ""Inquire"") - {{ - $PSBoundParameters['" + Constants.ErrorAction + @"'] = ""Continue"" - $errorAction = ""Continue"" - }} - - if (-not $PSBoundParameters.ContainsKey('" + Constants.WarningAction + @"')) - {{ - $PSBoundParameters['" + Constants.WarningAction + @"'] = $warningPreference - }} - - if(Test-Path variable:\warningPreference) - {{ - $warningAction = $warningPreference - }} - else - {{ - $warningAction = ""Continue"" - }} - - if ($PSBoundParameters['" + Constants.WarningAction + @"'] -in ""SilentlyContinue"",""Ignore"") - {{ - $warningAction = ""SilentlyContinue"" - }} - - if ($PSBoundParameters['" + Constants.WarningAction + @"'] -eq ""Inquire"") - {{ - $PSBoundParameters['" + Constants.WarningAction + @"'] = ""Continue"" - $warningAction = ""Continue"" - }} - - - if (-not $PSBoundParameters.ContainsKey('" + Constants.InformationAction + @"')) - {{ - $PSBoundParameters['" + Constants.InformationAction + @"'] = $informationPreference - }} - - if(Test-Path variable:\informationPreference) - {{ - $informationAction = $informationPreference - }} - else - {{ - $informationAction = ""Continue"" - }} - - if ($PSBoundParameters['" + Constants.InformationAction + @"'] -in ""SilentlyContinue"",""Ignore"") - {{ - $informationAction = ""SilentlyContinue"" - }} - - if ($PSBoundParameters['" + Constants.InformationAction + @"'] -eq ""Inquire"") - {{ - $PSBoundParameters['" + Constants.InformationAction + @"'] = ""Continue"" - $informationAction = ""Continue"" - }} - - - # Create the final parameter collection... - $finalParameterCollection = $null - if ($PSParameterCollection -ne $null) - {{ - $finalParameterCollection = $PSParameterCollection - }} - else - {{ - $finalParameterCollection = $PSBoundParameters - }} - - try - {{ - # Start the workflow and return the job object... - $debuggerActive = (@(Get-PSBreakpoint).Count -gt 0) - if (($debuggerActive -eq $false) -and - ($host -ne $null) -and - ($host.Runspace -ne $null) -and - ($host.Runspace.Debugger -ne $null)) - {{ - $debuggerActive = $host.Runspace.Debugger.IsActive - }} - $job = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::StartWorkflowApplication( - $PSCmdlet, - $jobName, - '{1}', - $AsJob, - $parameterCollectionProcessed, - $finalParameterCollection, - $debuggerActive, - $SourceLanguageMode) - }} - catch - {{ - # extract exception from the error record - $e = $_.Exception - # this is probably a method invocation exception so we want the inner exception - # if it exists - if ($e -is [System.Management.Automation.MethodException] -and $e.InnerException) - {{ - $e = $e.InnerException - }} - - $msg = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::UnableToStartWorkflowMessageMessage -f ` - $MyInvocation.MyCommand.Name, $e.Message - - $newException = New-Object System.Management.Automation.RuntimeException $msg, $e - - throw (New-Object System.Management.Automation.ErrorRecord $newException, StartWorkflow.InvalidArgument, InvalidArgument, $finalParameterCollection) - }} - - if (-not $AsJob -and $job -ne $null) - {{ - try - {{ - Receive-Job -Job $job -Wait -Verbose -Debug -ErrorAction $errorAction -WarningAction $warningAction -InformationAction $informationAction - - $PSCmdlet.InvokeCommand.HasErrors = $job.State -eq 'failed' - }} - finally - {{ - if($job.State -ne ""Suspended"" -and $job.State -ne ""Stopped"") - {{ - Remove-Job -Job $job -Force - }} - else - {{ - $job - }} - }} - }} - else - {{ - $job - }} - }} -"; - #endregion Moved from AstToWorkflowConverter - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/LocalRunspaceProvider.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/LocalRunspaceProvider.cs deleted file mode 100644 index 0f8a7410539..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/LocalRunspaceProvider.cs +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright (c) 2011 Microsoft Corporation. All rights reserved - */ -using System; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Security; -using System.Management.Automation.Tracing; -using System.Threading; -using Microsoft.PowerShell.Activities; - -namespace Microsoft.PowerShell.Workflow -{ - internal class LocalRunspaceAsyncResult : ConnectionAsyncResult - { - internal LocalRunspaceAsyncResult(object state, AsyncCallback callback, Guid ownerId) - :base(state, callback, ownerId) - { - - } - - internal Runspace Runspace { get; set; } - } - - internal class LocalRunspaceProvider : RunspaceProvider, IDisposable - { - private readonly TimeBasedCache _runspaceCache; - private readonly int _maxRunspaces; - private readonly PSLanguageMode? _languageMode; - - private readonly ConcurrentQueue _requests = - new ConcurrentQueue(); - - private readonly ConcurrentQueue _callbacks = - new ConcurrentQueue(); - - private int _isServicing; - private int _isServicingCallbacks; - private const int Servicing = 1; - private const int NotServicing = 0; - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// -1 indicates unlimited - /// - private const int MaxRunspacesPossible = -1; - private const int DefaultMaxRunspaces = MaxRunspacesPossible; - - internal TimeBasedCache RunspaceCache - { - get { return _runspaceCache; } - } - - internal LocalRunspaceProvider(int timeoutSeconds, PSLanguageMode? languageMode) : this(timeoutSeconds, DefaultMaxRunspaces, languageMode) - { - - } - - internal LocalRunspaceProvider(int timeoutSeconds, int maxRunspaces, PSLanguageMode? languageMode) - { - _runspaceCache = new TimeBasedCache(timeoutSeconds); - _maxRunspaces = maxRunspaces; - _languageMode = languageMode; - } - - /// - /// - /// - /// - /// - /// - /// - public override Runspace GetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval) - { - IAsyncResult asyncResult = BeginGetRunspace(connectionInfo, 0, 0, null, null); - - return EndGetRunspace(asyncResult); - } - - private Runspace AssignRunspaceIfPossible(PSLanguageMode? sourceLanguageMode = null) - { - Runspace runspace = null; - PSLanguageMode languageMode = (sourceLanguageMode != null) ? sourceLanguageMode.Value : - (_languageMode != null) ? _languageMode.Value : GetSystemLanguageMode(); - lock (_runspaceCache.TimerServicingSyncObject) - { - // Retrieve or create a local runspace having the same language mode as the source, if provided. - foreach (Item item in _runspaceCache.Cast>().Where(item => !item.Busy)) - { - if (item.Value.SessionStateProxy.LanguageMode == languageMode) - { - item.Idle = false; - item.Busy = true; - runspace = item.Value; - break; - } - } - - if ((runspace == null || runspace.RunspaceStateInfo.State != RunspaceState.Opened) && - (_maxRunspaces == MaxRunspacesPossible || _runspaceCache.Cache.Count < _maxRunspaces)) - { - runspace = CreateLocalActivityRunspace(languageMode); - - runspace.Open(); - _tracer.WriteMessage("New local runspace created"); - - _runspaceCache.Add(new Item(runspace, runspace.InstanceId)); - } - } - - return runspace; - } - - private static PSLanguageMode GetSystemLanguageMode() - { - return (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) ? - PSLanguageMode.ConstrainedLanguage : PSLanguageMode.FullLanguage; - } - - private void TraceThreadPoolInfo(string message) - { -#if DEBUG - int maxWorkerThreads, maxCompletionPortThreads, availableWorkerThreads, availableCompletionPortThreads, - minWorkerThreads, minCompletionPortThreads; - - ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads); - ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads); - ThreadPool.GetAvailableThreads(out availableWorkerThreads, out availableCompletionPortThreads); - - _tracer.WriteMessage(string.Format(System.Globalization.CultureInfo.InvariantCulture, - "LocalRunspaceProvider: {0} minwt:{1} mincpt:{2} wt:{3} ct:{4} awt:{5} act:{6}", - message, minWorkerThreads, minCompletionPortThreads, maxWorkerThreads, - maxCompletionPortThreads, availableWorkerThreads, - availableCompletionPortThreads)); -#else - _tracer.WriteMessage(string.Format(System.Globalization.CultureInfo.InvariantCulture, - "PSW ConnMgr: {0}", - message)); -#endif - } - - /// - /// Checks to see if a thread is already servicing and if not starts one - /// - private void CheckAndStartRequestServicingThread() - { - if (Interlocked.CompareExchange(ref _isServicing, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("QueueUserWorkItem Request Servicing thread"); - ThreadPool.QueueUserWorkItem(ServiceRequests); - } - - private void ServiceRequests(object state) - { - Debug.Assert(_maxRunspaces != MaxRunspacesPossible, - "When infinite runspaces are specified, then we should never hit the servicing thread"); - - lock(_runspaceCache.TimerServicingSyncObject) - { - LocalRunspaceAsyncResult asyncResult; - - Runspace runspace = AssignRunspaceIfPossible(); - - bool assigned = false; - while (runspace != null && _requests.TryDequeue(out asyncResult)) - { - asyncResult.Runspace = runspace; - assigned = true; - AddToPendingCallbacks(asyncResult); - - runspace = _runspaceCache.Cache.Count < _maxRunspaces ? AssignRunspaceIfPossible() : null; - } - - if (!assigned && runspace != null) - ReleaseRunspace(runspace); - } - - Interlocked.CompareExchange(ref _isServicing, NotServicing, Servicing); - } - - private void AddToPendingCallbacks(LocalRunspaceAsyncResult asyncResult) - { - _callbacks.Enqueue(asyncResult); - - if (Interlocked.CompareExchange(ref _isServicingCallbacks, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("Callback thread"); - ThreadPool.QueueUserWorkItem(ServiceCallbacks); - } - - private void ServiceCallbacks(object state) - { - LocalRunspaceAsyncResult result; - while (_callbacks.TryDequeue(out result)) - { - result.SetAsCompleted(null); - } - Interlocked.CompareExchange(ref _isServicingCallbacks, NotServicing, Servicing); - } - - /// - /// Begin for obtaining a runspace for the specified ConnectionInfo - /// - /// connection info to be used for remote connections - /// number of times to retry - /// optional user defined callback - /// optional user specified state - /// time in milliseconds before the next retry has to be attempted - /// async result - public override IAsyncResult BeginGetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval, AsyncCallback callback, object state) - { - if (connectionInfo != null) - { - throw new InvalidOperationException(); - } - - LocalRunspaceAsyncResult asyncResult = new LocalRunspaceAsyncResult(state, callback, Guid.Empty); - - // Get the source language mode from the activity arguments if available and pass to runspace fetching. - PSLanguageMode? sourceLanguageMode = null; - RunCommandsArguments args = state as RunCommandsArguments; - if (args != null) - { - PSWorkflowRuntime wfRuntime = args.WorkflowHost as PSWorkflowRuntime; - if (wfRuntime != null) - { - PSWorkflowJob wfJob = wfRuntime.JobManager.GetJob(args.PSActivityContext.JobInstanceId); - if (wfJob != null) - { - sourceLanguageMode = wfJob.SourceLanguageMode; - } - } - } - - Runspace runspace = AssignRunspaceIfPossible(sourceLanguageMode); - if (runspace != null) - { - asyncResult.Runspace = runspace; - asyncResult.CompletedSynchronously = true; - asyncResult.SetAsCompleted(null); - } - else - { - // queue the request - _requests.Enqueue(asyncResult); - CheckAndStartRequestServicingThread(); - } - - return asyncResult; - } - - /// - /// End for obtaining a runspace for the specified connection info - /// - /// async result to end on - /// remote runspace to invoke commands on - public override Runspace EndGetRunspace(IAsyncResult asyncResult) - { - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); - - LocalRunspaceAsyncResult result = asyncResult as LocalRunspaceAsyncResult; - - if (result == null) - throw new ArgumentException(Resources.InvalidAsyncResultSpecified, "asyncResult"); - - // this will throw an exception when a runspace is not successfully - // available - result.EndInvoke(); - - Debug.Assert(result.Runspace != null, "EndInvoke() should throw an exception if runspace is null"); - - _tracer.WriteMessage("LocalRunspaceProvider: Request serviced and runspace returned"); - Runspace runspace = result.Runspace; - - Debug.Assert(runspace.RunspaceStateInfo.State == RunspaceState.Opened, "Only opened runspace should be returned"); - - return runspace; - } - - /// - /// - /// - /// - public override void ReleaseRunspace(Runspace runspace) - { - runspace.ResetRunspaceState(); - lock(_runspaceCache.TimerServicingSyncObject) - { - bool found = false; - foreach (Item item in - _runspaceCache.Cast>().Where(item => item.InstanceId == runspace.InstanceId)) - { - item.Busy = false; - found = true; - } - - if (!found) - throw new InvalidOperationException(); - } - - if (_maxRunspaces != MaxRunspacesPossible) - CheckAndStartRequestServicingThread(); - } - - private readonly static object SyncObject = new object(); - private static TypeTable _sharedTypeTable; - internal static TypeTable SharedTypeTable - { - get - { - if (_sharedTypeTable == null) - { - lock (SyncObject) - { - if (_sharedTypeTable == null) - { - _sharedTypeTable = TypeTable.LoadDefaultTypeFiles(); - } - } - } - return _sharedTypeTable; - } - } - - private static InitialSessionState GetInitialSessionStateWithSharedTypesAndNoFormat() - { - InitialSessionState initialSessionState = InitialSessionState.CreateDefault(); - - // clear existing types and format - initialSessionState.Types.Clear(); - initialSessionState.Formats.Clear(); - - // add default types and formats - initialSessionState.Types.Add(new SessionStateTypeEntry(SharedTypeTable.Clone(unshared: true))); - initialSessionState.DisableFormatUpdates = true; - - return initialSessionState; - } - - /// - /// Creates a local runspace with the autoloading turned on. - /// - /// - internal static Runspace CreateLocalActivityRunspace(PSLanguageMode? languageMode = null, bool useCurrentThreadForExecution = true) - { - InitialSessionState iss = GetInitialSessionStateWithSharedTypesAndNoFormat(); - if (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) - { - iss.LanguageMode = PSLanguageMode.ConstrainedLanguage; - } - - if (languageMode != null && languageMode.HasValue) - { - iss.LanguageMode = languageMode.Value; - } - - // Add a variable indicating that we're in Workflow endpoint. This enables the - // autoloading feature. - SessionStateVariableEntry ssve = new SessionStateVariableEntry("RunningInPSWorkflowEndpoint", - true, "True if we're in a Workflow Endpoint", ScopedItemOptions.Constant); - iss.Variables.Add(ssve); - Runspace runspace = RunspaceFactory.CreateRunspace(iss); - - if (useCurrentThreadForExecution) - runspace.ThreadOptions = PSThreadOptions.UseCurrentThread; - - return runspace; - } - - /// - /// Dispose resources - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// - /// - /// - protected void Dispose(bool disposing) - { - if (disposing) - { - _runspaceCache.Dispose(); - } - } - - /// - /// Helper method only to use from test - /// - internal void Reset() - { - foreach(Item item in _runspaceCache) - { - item.Value.Dispose(); - } - _runspaceCache.Cache.Clear(); - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/NewPSWorkflowExecutionOptionCommand.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/NewPSWorkflowExecutionOptionCommand.cs deleted file mode 100644 index fcede3c6723..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/NewPSWorkflowExecutionOptionCommand.cs +++ /dev/null @@ -1,876 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Globalization; -using System.Management.Automation; -using System.Collections; -using System.Xml; -using Microsoft.PowerShell.Workflow; -using System.Reflection; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// This class is used to specify Workflow related options in the Register-PSSessionConfiguration - /// - public sealed class PSWorkflowExecutionOption : PSSessionTypeOption - { - private const string PrivateDataFormat = @"{0}"; - private const string ParamToken = @""; - - private string persistencePath = PSWorkflowConfigurationProvider.DefaultPersistencePath; - private long maxPersistenceStoreSizeGB = PSWorkflowConfigurationProvider.DefaultMaxPersistenceStoreSizeGB; - private bool persistWithEncryption = PSWorkflowConfigurationProvider.DefaultPersistWithEncryption; - private int maxRunningWorkflows = PSWorkflowConfigurationProvider.DefaultMaxRunningWorkflows; - private string[] allowedActivity = new List(PSWorkflowConfigurationProvider.DefaultAllowedActivity).ToArray(); - private bool enableValidation = PSWorkflowConfigurationProvider.DefaultEnableValidation; - private string[] outOfProcessActivity = new List(PSWorkflowConfigurationProvider.DefaultOutOfProcessActivity).ToArray(); - private int maxDisconnectedSessions = PSWorkflowConfigurationProvider.DefaultMaxDisconnectedSessions; - private int maxConnectedSessions = PSWorkflowConfigurationProvider.DefaultMaxConnectedSessions; - private int maxSessionsPerWorkflow = PSWorkflowConfigurationProvider.DefaultMaxSessionsPerWorkflow; - private int maxSessionsPerRemoteNode = PSWorkflowConfigurationProvider.DefaultMaxSessionsPerRemoteNode; - private int maxActivityProcesses = PSWorkflowConfigurationProvider.DefaultMaxActivityProcesses; - private int activityProcessIdleTimeoutSec = PSWorkflowConfigurationProvider.DefaultActivityProcessIdleTimeoutSec; - private int workflowApplicationPersistUnloadTimeoutSec = PSWorkflowConfigurationProvider.DefaultWorkflowApplicationPersistUnloadTimeoutSec; - private int wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = PSWorkflowConfigurationProvider.DefaultWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec; - private int activitiesCacheCleanupIntervalMSec = PSWorkflowConfigurationProvider.DefaultActivitiesCacheCleanupIntervalMSec; - private int remoteNodeSessionIdleTimeoutSec = PSWorkflowConfigurationProvider.DefaultRemoteNodeSessionIdleTimeoutSec; - private int sessionThrottleLimit = PSWorkflowConfigurationProvider.DefaultSessionThrottleLimit; - private int workflowShutdownTimeoutMSec = PSWorkflowConfigurationProvider.DefaultWorkflowShutdownTimeoutMSec; - - /// - /// Constructor that loads from default values - /// - internal PSWorkflowExecutionOption() - { - } - - private void ValidateRange(int min, int max, int value) - { - if (value >= min && value <= max) - return; - - string msg = string.Format(CultureInfo.InvariantCulture, Resources.ProvidedValueIsOutOfRange, value, min, max); - throw new ArgumentException(msg); - } - - /// - /// SessionThrottleLimit - /// - public int SessionThrottleLimit - { - get - { - return sessionThrottleLimit; - } - set { - ValidateRange(PSWorkflowConfigurationProvider.MinSessionThrottleLimit, PSWorkflowConfigurationProvider.MaxSessionThrottleLimit, value); - sessionThrottleLimit = value; - } - } - - /// - /// PersistencePath - /// - public string PersistencePath - { - get - { - return persistencePath; - } - set - { - persistencePath = value; - } - } - - /// - /// MaxPersistenceStoreSizeGB - /// - public long MaxPersistenceStoreSizeGB - { - get - { - return maxPersistenceStoreSizeGB; - } - set - { - maxPersistenceStoreSizeGB = value; - } - } - - /// - /// PersistWithEncryption - /// - public bool PersistWithEncryption - { - get { - return persistWithEncryption; - } - set - { - persistWithEncryption = value; - } - } - - /// - /// MaxRunningWorkflows - /// - public int MaxRunningWorkflows { - get - { - return maxRunningWorkflows; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxRunningWorkflows, PSWorkflowConfigurationProvider.MaxMaxRunningWorkflows, value); - maxRunningWorkflows = value; - } - } - - /// - /// AllowedActivity - /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] AllowedActivity - { - get - { - return allowedActivity; - } - set - { - allowedActivity = value; - } - } - - /// - /// OutOfProcActivity - /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] OutOfProcessActivity - { - get - { - return outOfProcessActivity; - } - set - { - outOfProcessActivity = value; - } - } - - - /// - /// EnableValidation - /// - public bool EnableValidation - { - get - { - return enableValidation; - } - set - { - enableValidation = value; - } - } - - /// - /// MaxDisconnectedSessions - /// - public int MaxDisconnectedSessions - { - get - { - return maxDisconnectedSessions; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxDisconnectedSessions, PSWorkflowConfigurationProvider.MaxMaxDisconnectedSessions, value); - maxDisconnectedSessions = value; - } - } - - /// - /// MaxConnectedSessions - /// - public int MaxConnectedSessions - { - get - { - return maxConnectedSessions; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxConnectedSessions, PSWorkflowConfigurationProvider.MaxMaxConnectedSessions, value); - maxConnectedSessions = value; - } - } - - /// - /// MaxSessionsPerWorkflow - /// - public int MaxSessionsPerWorkflow - { - get - { - return maxSessionsPerWorkflow; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxSessionsPerWorkflow, PSWorkflowConfigurationProvider.MaxMaxSessionsPerWorkflow, value); - maxSessionsPerWorkflow = value; - } - } - - /// - /// MaxSessionsPerRemoteNode - /// - public int MaxSessionsPerRemoteNode - { - get - { - return maxSessionsPerRemoteNode; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxSessionsPerRemoteNode, PSWorkflowConfigurationProvider.MaxMaxSessionsPerRemoteNode, value); - maxSessionsPerRemoteNode = value; - } - } - - /// - /// MaxActivityProcesses - /// - public int MaxActivityProcesses - { - get - { - return maxActivityProcesses; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxActivityProcesses, PSWorkflowConfigurationProvider.MaxMaxActivityProcesses, value); - maxActivityProcesses = value; - } - } - - /// - /// WorkflowApplicationPersistUnloadTimeoutSec - /// - internal int WorkflowApplicationPersistUnloadTimeoutSec - { - get - { - return workflowApplicationPersistUnloadTimeoutSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinWorkflowApplicationPersistUnloadTimeoutSec, PSWorkflowConfigurationProvider.MaxWorkflowApplicationPersistUnloadTimeoutSec, value); - workflowApplicationPersistUnloadTimeoutSec = value; - } - } - - /// - /// WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec - /// - internal int WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec - { - get - { - return wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec, PSWorkflowConfigurationProvider.MaxWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec, value); - wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = value; - } - } - - /// - /// ActivitiesCacheCleanupIntervalMSec - /// - internal int ActivitiesCacheCleanupIntervalMSec - { - get - { - return activitiesCacheCleanupIntervalMSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinActivitiesCacheCleanupIntervalMSec, PSWorkflowConfigurationProvider.MaxActivitiesCacheCleanupIntervalMSec, value); - activitiesCacheCleanupIntervalMSec = value; - } - } - - /// - /// ActivityProcessIdleTimeOutSec - /// - public int ActivityProcessIdleTimeoutSec - { - get - { - return activityProcessIdleTimeoutSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinActivityProcessIdleTimeoutSec, PSWorkflowConfigurationProvider.MaxActivityProcessIdleTimeoutSec, value); - activityProcessIdleTimeoutSec = value; - } - } - - /// - /// RemoteNodeSessionIdleTimeOut - /// - public int RemoteNodeSessionIdleTimeoutSec - { - get - { - return remoteNodeSessionIdleTimeoutSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinRemoteNodeSessionIdleTimeoutSec, PSWorkflowConfigurationProvider.MaxRemoteNodeSessionIdleTimeoutSec, value); - remoteNodeSessionIdleTimeoutSec = value; - } - } - - /// - /// WorkflowShutdownTimeoutMSec - the maximum time allowed to suspend the workflows before aborting them. - /// - public int WorkflowShutdownTimeoutMSec - { - get - { - return workflowShutdownTimeoutMSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinWorkflowShutdownTimeoutMSec, PSWorkflowConfigurationProvider.MaxWorkflowShutdownTimeoutMSec, value); - workflowShutdownTimeoutMSec = value; - } - } - - /// - /// Copies values from updated. Only non default values are copies. - /// - /// - protected override void CopyUpdatedValuesFrom(PSSessionTypeOption updated) - { - if (updated == null) - throw new ArgumentNullException("updated"); - - PSWorkflowExecutionOption modified = updated as PSWorkflowExecutionOption; - if (modified == null) - throw new ArgumentNullException("updated"); - - if (modified.activityProcessIdleTimeoutSec != PSWorkflowConfigurationProvider.DefaultActivityProcessIdleTimeoutSec) - this.activityProcessIdleTimeoutSec = modified.activityProcessIdleTimeoutSec; - - if (modified.workflowApplicationPersistUnloadTimeoutSec != PSWorkflowConfigurationProvider.DefaultWorkflowApplicationPersistUnloadTimeoutSec) - this.workflowApplicationPersistUnloadTimeoutSec = modified.workflowApplicationPersistUnloadTimeoutSec; - - if (modified.wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec != PSWorkflowConfigurationProvider.DefaultWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec) - this.wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = modified.wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec; - - if (modified.activitiesCacheCleanupIntervalMSec != PSWorkflowConfigurationProvider.DefaultActivitiesCacheCleanupIntervalMSec) - this.activitiesCacheCleanupIntervalMSec = modified.activitiesCacheCleanupIntervalMSec; - - if (!ListsMatch(modified.allowedActivity, PSWorkflowConfigurationProvider.DefaultAllowedActivity)) - this.allowedActivity = modified.allowedActivity; - - if (!string.Equals(modified.persistencePath, PSWorkflowConfigurationProvider.DefaultPersistencePath, StringComparison.OrdinalIgnoreCase)) - this.persistencePath = modified.persistencePath; - - if (modified.maxPersistenceStoreSizeGB != PSWorkflowConfigurationProvider.DefaultMaxPersistenceStoreSizeGB) - this.maxPersistenceStoreSizeGB = modified.maxPersistenceStoreSizeGB; - - if (modified.persistWithEncryption != PSWorkflowConfigurationProvider.DefaultPersistWithEncryption) - this.persistWithEncryption = modified.persistWithEncryption; - - if (modified.remoteNodeSessionIdleTimeoutSec != PSWorkflowConfigurationProvider.DefaultRemoteNodeSessionIdleTimeoutSec) - this.remoteNodeSessionIdleTimeoutSec = modified.remoteNodeSessionIdleTimeoutSec; - - if (modified.maxActivityProcesses != PSWorkflowConfigurationProvider.DefaultMaxActivityProcesses) - this.maxActivityProcesses = modified.maxActivityProcesses; - - if (modified.maxConnectedSessions != PSWorkflowConfigurationProvider.DefaultMaxConnectedSessions) - this.maxConnectedSessions = modified.maxConnectedSessions; - - if (modified.maxDisconnectedSessions != PSWorkflowConfigurationProvider.DefaultMaxDisconnectedSessions) - this.maxDisconnectedSessions = modified.maxDisconnectedSessions; - - if (modified.maxRunningWorkflows != PSWorkflowConfigurationProvider.DefaultMaxRunningWorkflows) - this.maxRunningWorkflows = modified.maxRunningWorkflows; - - if (modified.maxSessionsPerRemoteNode != PSWorkflowConfigurationProvider.DefaultMaxSessionsPerRemoteNode) - this.maxSessionsPerRemoteNode = modified.maxSessionsPerRemoteNode; - - if (modified.maxSessionsPerWorkflow != PSWorkflowConfigurationProvider.DefaultMaxSessionsPerWorkflow) - this.maxSessionsPerWorkflow = modified.maxSessionsPerWorkflow; - - if (!ListsMatch(modified.outOfProcessActivity, PSWorkflowConfigurationProvider.DefaultOutOfProcessActivity)) - this.outOfProcessActivity = modified.outOfProcessActivity; - - if (modified.enableValidation != PSWorkflowConfigurationProvider.DefaultEnableValidation) - this.enableValidation = modified.enableValidation; - - if (modified.sessionThrottleLimit != PSWorkflowConfigurationProvider.DefaultSessionThrottleLimit) - this.sessionThrottleLimit = modified.sessionThrottleLimit; - - if (modified.workflowShutdownTimeoutMSec != PSWorkflowConfigurationProvider.DefaultWorkflowShutdownTimeoutMSec) - this.workflowShutdownTimeoutMSec = modified.workflowShutdownTimeoutMSec; - } - - private static bool ListsMatch(IEnumerable a, IEnumerable b) - { - foreach (string strA in a) - { - bool found = false; - foreach (string strB in b) - { - if (string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase) == 0) - { - found = true; - break; - } - } - if (!found) - return false; - } - - foreach (string strB in b) - { - bool found = false; - foreach (string strA in a) - { - if (string.Compare(strB, strA, StringComparison.OrdinalIgnoreCase) == 0) - { - found = true; - break; - } - } - if (!found) - return false; - } - return true; - } - - /// - /// Returns a new instance constructed from privateData string. - /// - /// - protected override PSSessionTypeOption ConstructObjectFromPrivateData(string privateData) - { - return PSWorkflowConfigurationProvider.LoadConfig(privateData, null); - } - - /// - /// Implementation of the abstract method - /// - /// - protected override string ConstructPrivateData() - { - StringBuilder privateDataParams = new StringBuilder(); - - bool usesDefaultPath = string.Compare(persistencePath, PSWorkflowConfigurationProvider.DefaultPersistencePath, StringComparison.OrdinalIgnoreCase) != 0; - if (usesDefaultPath) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenPersistencePath, persistencePath)); - - if (maxPersistenceStoreSizeGB != PSWorkflowConfigurationProvider.DefaultMaxPersistenceStoreSizeGB) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxPersistenceStoreSizeGB, maxPersistenceStoreSizeGB)); - - if (persistWithEncryption != PSWorkflowConfigurationProvider.DefaultPersistWithEncryption) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenPersistWithEncryption, persistWithEncryption)); - - if (maxRunningWorkflows != PSWorkflowConfigurationProvider.DefaultMaxRunningWorkflows) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxRunningWorkflows, maxRunningWorkflows)); - - if (enableValidation != PSWorkflowConfigurationProvider.DefaultEnableValidation) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenEnableValidation, enableValidation)); - - StringBuilder allowedAttribute = new StringBuilder(); - foreach (string a in allowedActivity ?? new string[0]) - { - allowedAttribute.Append(a); - allowedAttribute.Append(','); - } - if (allowedAttribute.Length > 0) - { - allowedAttribute.Remove(allowedAttribute.Length - 1, 1); - } - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenAllowedActivity, allowedAttribute.ToString())); - - StringBuilder outOfProcAttribute = new StringBuilder(); - foreach (string a in outOfProcessActivity ?? new string[0]) - { - outOfProcAttribute.Append(a); - outOfProcAttribute.Append(','); - } - if (outOfProcAttribute.Length > 0) - { - outOfProcAttribute.Remove(outOfProcAttribute.Length - 1, 1); - } - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenOutOfProcessActivity, outOfProcAttribute.ToString())); - - if (maxDisconnectedSessions != PSWorkflowConfigurationProvider.DefaultMaxDisconnectedSessions) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxDisconnectedSessions, maxDisconnectedSessions)); - - if (maxConnectedSessions != PSWorkflowConfigurationProvider.DefaultMaxConnectedSessions) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxConnectedSessions, maxConnectedSessions)); - - if (maxSessionsPerWorkflow != PSWorkflowConfigurationProvider.DefaultMaxSessionsPerWorkflow) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxSessionsPerWorkflow, maxSessionsPerWorkflow)); - - if (maxSessionsPerRemoteNode != PSWorkflowConfigurationProvider.DefaultMaxSessionsPerRemoteNode) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxSessionsPerRemoteNode, maxSessionsPerRemoteNode)); - - if (maxActivityProcesses != PSWorkflowConfigurationProvider.DefaultMaxActivityProcesses) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxActivityProcesses, maxActivityProcesses)); - - if (activityProcessIdleTimeoutSec != PSWorkflowConfigurationProvider.DefaultActivityProcessIdleTimeoutSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenActivityProcessIdleTimeoutSec, activityProcessIdleTimeoutSec)); - - if (workflowApplicationPersistUnloadTimeoutSec != PSWorkflowConfigurationProvider.DefaultWorkflowApplicationPersistUnloadTimeoutSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenWorkflowApplicationPersistUnloadTimeoutSec, workflowApplicationPersistUnloadTimeoutSec)); - - if (wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec != PSWorkflowConfigurationProvider.DefaultWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec, wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec)); - - if (activitiesCacheCleanupIntervalMSec != PSWorkflowConfigurationProvider.DefaultActivitiesCacheCleanupIntervalMSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenActivitiesCacheCleanupIntervalMSec, activitiesCacheCleanupIntervalMSec)); - - if (remoteNodeSessionIdleTimeoutSec != PSWorkflowConfigurationProvider.DefaultRemoteNodeSessionIdleTimeoutSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenRemoteNodeSessionIdleTimeoutSec, remoteNodeSessionIdleTimeoutSec)); - - if (sessionThrottleLimit != PSWorkflowConfigurationProvider.DefaultSessionThrottleLimit) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenSessionThrottleLimit, sessionThrottleLimit)); - - if (workflowShutdownTimeoutMSec != PSWorkflowConfigurationProvider.DefaultWorkflowShutdownTimeoutMSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenWorkflowShutdownTimeoutMSec, workflowShutdownTimeoutMSec)); - - return string.Format(CultureInfo.InvariantCulture, PrivateDataFormat, privateDataParams.ToString()); - } - - internal string ConstructPrivateDataInternal() - { - return ConstructPrivateData(); - } - } - - /// - /// Command to create an object for PSWorkflowExecutionOption - /// - [Cmdlet(VerbsCommon.New, "PSWorkflowExecutionOption",HelpUri = "https://go.microsoft.com/fwlink/?LinkID=210609")] - [OutputType(typeof(PSWorkflowExecutionOption))] - public sealed class NewPSWorkflowExecutionOptionCommand : PSCmdlet - { - private PSWorkflowExecutionOption option = new PSWorkflowExecutionOption(); - private bool enableValidationParamSpecified = false; - - /// - /// PersistencePath - /// - [Parameter] - public string PersistencePath - { - get - { - return option.PersistencePath; - } - set - { - if (value != null) - { - string rootedPath = value; - bool isPathTooLong = false; - if (!Path.IsPathRooted(value)) - { - try - { - rootedPath = Path.GetPathRoot(value); - - if (String.IsNullOrEmpty(rootedPath)) - { - rootedPath = value; - } - } - catch (PathTooLongException) - { - isPathTooLong = true; - } - } - - if (isPathTooLong || (rootedPath != null && rootedPath.Length > Constants.MaxAllowedPersistencePathLength)) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, Resources.PersistencePathToolLong, value, Constants.MaxAllowedPersistencePathLength)); - } - } - - option.PersistencePath = value; - } - } - - /// - /// MaxPersistenceStoreSizeGB - /// - [Parameter] - public long MaxPersistenceStoreSizeGB - { - get - { - return option.MaxPersistenceStoreSizeGB; - } - set - { - option.MaxPersistenceStoreSizeGB = value; - } - } - - /// - /// UseEncryption - /// - [Parameter] - public SwitchParameter PersistWithEncryption { - get - { - return option.PersistWithEncryption; - } - set - { - option.PersistWithEncryption = value; - } - } - - /// - /// MaxRunningWorkflows - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxRunningWorkflows, PSWorkflowConfigurationProvider.MaxMaxRunningWorkflows)] - public int MaxRunningWorkflows { - get - { - return option.MaxRunningWorkflows; - } - set - { - option.MaxRunningWorkflows = value; - } - } - - /// - /// AllowedActivity - /// - [Parameter, SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] AllowedActivity - { - get - { - return option.AllowedActivity; - } - set - { - option.AllowedActivity = value; - } - } - - /// - /// OutOfProcActivity - /// - [Parameter, SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] OutOfProcessActivity - { - get - { - return option.OutOfProcessActivity; - } - set - { - option.OutOfProcessActivity = value; - } - } - - /// - /// EnableValidation - /// - [Parameter] - public SwitchParameter EnableValidation - { - get - { - return option.EnableValidation; - } - set - { - option.EnableValidation = value; - enableValidationParamSpecified = true; - } - } - - /// - /// MaxDisconnectedSessions - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxDisconnectedSessions, PSWorkflowConfigurationProvider.MaxMaxDisconnectedSessions)] - public int MaxDisconnectedSessions - { - get - { - return option.MaxDisconnectedSessions; - } - set - { - option.MaxDisconnectedSessions = value; - } - } - - /// - /// MaxConnectedSessions - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxConnectedSessions, PSWorkflowConfigurationProvider.MaxMaxConnectedSessions)] - public int MaxConnectedSessions - { - get - { - return option.MaxConnectedSessions; - } - set - { - option.MaxConnectedSessions = value; - } - } - - /// - /// MaxSessionsPerWorkflow - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxSessionsPerWorkflow, PSWorkflowConfigurationProvider.MaxMaxSessionsPerWorkflow)] - public int MaxSessionsPerWorkflow - { - get - { - return option.MaxSessionsPerWorkflow; - } - set - { - option.MaxSessionsPerWorkflow = value; - } - } - - /// - /// MaxSessionsPerRemoteNode - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxSessionsPerRemoteNode, PSWorkflowConfigurationProvider.MaxMaxSessionsPerRemoteNode)] - public int MaxSessionsPerRemoteNode - { - get - { - return option.MaxSessionsPerRemoteNode; - } - set - { - option.MaxSessionsPerRemoteNode = value; - } - } - - /// - /// MaxActivityProcess - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxActivityProcesses, PSWorkflowConfigurationProvider.MaxMaxActivityProcesses)] - public int MaxActivityProcesses - { - get - { - return option.MaxActivityProcesses; - } - set - { - option.MaxActivityProcesses = value; - } - } - - /// - /// ActivityProcessIdleTimeOutSec - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinActivityProcessIdleTimeoutSec, PSWorkflowConfigurationProvider.MaxActivityProcessIdleTimeoutSec)] - public int ActivityProcessIdleTimeoutSec - { - get - { - return option.ActivityProcessIdleTimeoutSec; - } - set - { - option.ActivityProcessIdleTimeoutSec = value; - } - } - - /// - /// RemoteNodeSessionIdleTimeOutSec - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinRemoteNodeSessionIdleTimeoutSec, PSWorkflowConfigurationProvider.MaxRemoteNodeSessionIdleTimeoutSec)] - public int RemoteNodeSessionIdleTimeoutSec - { - get - { - return option.RemoteNodeSessionIdleTimeoutSec; - } - set - { - option.RemoteNodeSessionIdleTimeoutSec = value; - } - } - - /// - /// SessionThrottleLimit - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinSessionThrottleLimit, PSWorkflowConfigurationProvider.MaxSessionThrottleLimit)] - public int SessionThrottleLimit - { - get - { - return option.SessionThrottleLimit; - } - set - { - option.SessionThrottleLimit = value; - } - } - - /// - /// WorkflowShutdownTimeoutMSec - the maximum time allowed to suspend the workflows before aborting them. - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinWorkflowShutdownTimeoutMSec, PSWorkflowConfigurationProvider.MaxWorkflowShutdownTimeoutMSec)] - public int WorkflowShutdownTimeoutMSec - { - get - { - return option.WorkflowShutdownTimeoutMSec; - } - set - { - option.WorkflowShutdownTimeoutMSec = value; - } - } - - /// - /// ProcessRecord - /// - protected override void ProcessRecord() - { - // By default EnableValidation should be TRUE for NewPSWorkflowExecutionOption, so that activity validation is done in user created endpoints. - // - if(!enableValidationParamSpecified) - { - option.EnableValidation = true; - } - - this.WriteObject(option); - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityBase.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityBase.cs deleted file mode 100644 index 265ec303569..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityBase.cs +++ /dev/null @@ -1,6577 +0,0 @@ -using System; -using System.Collections; -using System.Data; -using System.Linq; -using System.Activities; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Globalization; -using System.Management.Automation.Remoting; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Activities.Hosting; -using System.Activities.Statements; -using System.ComponentModel; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.Management.Automation.Runspaces; -using Microsoft.Management.Infrastructure; -using Microsoft.Management.Infrastructure.Options; -using System.Management.Automation.Tracing; -using Dbg=System.Diagnostics.Debug; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation.Host; -using System.Management; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - internal class ActivityParameters - { - internal uint? ConnectionRetryCount - { - get; - private set; - } - - internal uint? ConnectionRetryInterval - { - get; - private set; - } - - internal uint? ActionRetryCount - { - get; - private set; - } - - internal uint? ActionRetryInterval - { - get; - private set; - } - - internal string[] PSRequiredModules - { - get; - private set; - } - - internal ActivityParameters(uint? connectionRetryCount, uint? connectionRetryInterval, uint? actionRetryCount, uint? actionRetryInterval, string[] requiredModule) - { - this.ConnectionRetryCount = connectionRetryCount; - this.ConnectionRetryInterval = connectionRetryInterval; - this.ActionRetryCount = actionRetryCount; - this.ActionRetryInterval = actionRetryInterval; - this.PSRequiredModules = requiredModule; - } - } - - internal delegate void PrepareSessionDelegate(ActivityImplementationContext implementationContext); - - internal class RunCommandsArguments - { - internal ActivityParameters ActivityParameters { get; private set; } - internal PSDataCollection Output { get; private set; } - internal PSDataCollection Input { get; private set; } - internal PSActivityContext PSActivityContext { get; private set; } - internal Dictionary ParameterDefaults { get; private set; } - internal Type ActivityType { get; private set; } - internal PrepareSessionDelegate Delegate { get; private set; } - internal object ActivityObject { get; private set; } - internal PSWorkflowHost WorkflowHost { get; private set; } - internal ActivityImplementationContext ImplementationContext { get; private set; } - internal int CommandExecutionType { get; private set; } - internal System.Management.Automation.PowerShell HelperCommand { get; set; } - internal PSDataCollection HelperCommandInput { get; set; } - internal int CleanupTimeout { get; set; } - - internal RunCommandsArguments(ActivityParameters activityParameters, PSDataCollection output, PSDataCollection input, - PSActivityContext psActivityContext, PSWorkflowHost workflowHost, - bool runInProc, Dictionary parameterDefaults, Type activityType, - PrepareSessionDelegate prepareSession, object activityObject, ActivityImplementationContext implementationContext) - { - ActivityParameters = activityParameters; - Output = output; - Input = input; - PSActivityContext = psActivityContext; - ParameterDefaults = parameterDefaults; - ActivityType = activityType; - Delegate = prepareSession; - ActivityObject = activityObject; - WorkflowHost = workflowHost; - ImplementationContext = implementationContext; - - CommandExecutionType = - DetermineCommandExecutionType( - implementationContext.ConnectionInfo, runInProc, - activityType, psActivityContext); - } - - internal static int DetermineCommandExecutionType(WSManConnectionInfo connectionInfo, bool runInProc, Type activityType, PSActivityContext psActivityContext) - { - // check for cleanup activity first - if (typeof(PSCleanupActivity).IsAssignableFrom(activityType)) - { - return PSActivity.CleanupActivity; - } - - if (connectionInfo != null) - { - // check if this is an activity with custom remoting - // then we need to run it in proc - if (psActivityContext != null && psActivityContext.RunWithCustomRemoting) - return PSActivity.CommandRunInProc; - - return PSActivity.CommandRunRemotely; - } - - // if connectionInfo is null, then the command needs to be executed - // on this machine. #1 to #3 from above is possible - if (!runInProc) - { - return PSActivity.CommandRunOutOfProc; - } - - // at this point the command is being run in-proc - - // Check for WMI activity - if (typeof(WmiActivity).IsAssignableFrom(activityType) || typeof(GenericCimCmdletActivity).IsAssignableFrom(activityType)) - { - return PSActivity.RunInProcNoRunspace; - } - - if (typeof(PSGeneratedCIMActivity).IsAssignableFrom(activityType)) - { - return PSActivity.CimCommandRunInProc; - } - - return PSActivity.CommandRunInProc; - } - } - - /// - /// Implementing this interface indicates that the activity supports connection retry. - /// - public interface IImplementsConnectionRetry - { - /// - /// Defines the number of retries that the activity will make to connect to a remote - /// machine when it encounters an error. The default is to not retry. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - InArgument PSConnectionRetryCount - { - get; - set; - } - - /// - /// Defines the delay, in seconds, between connection retry attempts. - /// The default is one second. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - InArgument PSConnectionRetryIntervalSec - { - get; - set; - } - - } - - /// - /// Special variables that can be defined in a workflow to - /// control the behaviour of PowerShell activities. - /// - public static class WorkflowPreferenceVariables - { - /// - /// The parent activity ID to be used for all progress records - /// that are written in the enclosing scope - /// - public const string PSParentActivityId = "PSParentActivityID"; - - /// - /// Workflow variable that controls when activities are run - /// in process. If true, all activities in the enclosing scope - /// will be run in process - /// - public const string PSRunInProcessPreference = "PSRunInProcessPreference"; - - /// - /// Workflow variable that is used to determine if a PowerShell activity should - /// persist when it's done. if true, then all PSActivities in that scope - /// will not persist at the end of their execution. - /// - public const string PSPersistPreference = "PSPersistPreference"; - } - - /// - /// Parameter default, contains all the information which needs to be passed to Workflow context. - /// - public sealed class HostParameterDefaults : IDisposable - { - /// - /// Default constructor. - /// - public HostParameterDefaults() - { - Parameters = new Dictionary(); - HostCommandMetadata = new HostSettingCommandMetadata(); - Runtime = null; - HostPersistenceDelegate = null; - ActivateDelegate = null; - AsyncExecutionCollection = null; - RemoteActivityState = null; - } - - /// - /// All the activity level default parameter values are passed here. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary Parameters - { - get; - set; - } - - /// - /// Metadata / symbolic information about the currently-running workflow. - /// - public HostSettingCommandMetadata HostCommandMetadata - { - get; - set; - } - - /// - /// Job instance id. - /// - public Guid JobInstanceId - { - get; - set; - } - - /// - /// The workflow runtime. - /// - public PSWorkflowHost Runtime - { - get; - set; - } - - /// - /// The host persistence delegate. - /// - public Func HostPersistenceDelegate - { - get; - set; - } - - /// - /// The Workflow activation delegate. - /// - public Action ActivateDelegate - { - get; - set; - } - - /// - /// The asynchronous execution collection. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary AsyncExecutionCollection - { - get; - set; - } - - /// - /// Currently executing remote activities state with runspace id or completion state. - /// - public PSWorkflowRemoteActivityState RemoteActivityState - { - get; - set; - } - - /// - /// Dispose method - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose of managed resources - /// - /// true if disposing - private void Dispose(bool disposing) - { - if (!disposing) return; - - Parameters = null; - ActivateDelegate = null; - AsyncExecutionCollection = null; - RemoteActivityState = null; - HostPersistenceDelegate = null; - Runtime = null; - } - } - - /// - /// Runtime metadata that represents the currently-running command. - /// - public class HostSettingCommandMetadata - { - /// - /// The command name that generated this workflow. - /// - public string CommandName - { - get; - set; - } - - /// - /// The start line of the command name that generated this section of - /// the workflow. - /// - public int StartLineNumber - { - get; - set; - } - - /// - /// The start column of the command name that generated this section of - /// the workflow. - /// - public int StartColumnNumber - { - get; - set; - } - - /// - /// The end line of the command name that generated this section of - /// the workflow. - /// - public int EndLineNumber - { - get; - set; - } - - /// - /// The end column of the command name that generated this section of - /// the workflow. - /// - public int EndColumnNumber - { - get; - set; - } - } - - /// - /// The activity context. - /// - [Serializable] - public class PSActivityContext : IDisposable - { - // Holds our list of commands to run, mapped to a RetryCount class of: - // Connection Attempts, Action Attempts - - [NonSerialized] - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - [NonSerialized] - internal Dictionary runningCommands; - - [NonSerialized] - internal ConcurrentQueue commandQueue; - - /// - /// IsCanceled. - /// - public bool IsCanceled { get; set; } - internal Dictionary UserVariables = new Dictionary(); - internal List exceptions = new List(); - internal PSDataCollection errors = new PSDataCollection(); - internal bool Failed; - internal bool SuspendOnError; - - [NonSerialized] - internal readonly ConcurrentQueue AsyncResults = new ConcurrentQueue(); - - [NonSerialized] - internal EventHandler HandleRunspaceStateChanged; - - [NonSerialized] - internal PSDataCollection progress; - - [NonSerialized] internal object SyncRoot = new object(); - [NonSerialized] internal bool AllCommandsStarted; - [NonSerialized] internal int CommandsRunningCount = 0; - - [NonSerialized] internal WaitCallback Callback; - [NonSerialized] internal object AsyncState; - [NonSerialized] internal Guid JobInstanceId; - - [NonSerialized] - internal ActivityParameters ActivityParams; - - [NonSerialized] - internal PSDataCollection Input; - - [NonSerialized] - internal PSDataCollection Output; - - [NonSerialized] - internal PSWorkflowHost WorkflowHost; - - [NonSerialized] - internal HostParameterDefaults HostExtension; - - [NonSerialized] - internal bool RunInProc; - - [NonSerialized] - internal bool MergeErrorToOutput; - - [NonSerialized] - internal Dictionary ParameterDefaults; - - [NonSerialized] - internal Type ActivityType; - - [NonSerialized] - internal PrepareSessionDelegate PrepareSession; - - [NonSerialized] - internal object ActivityObject; - - /// - /// The .NET type implementing the cmdlet to call, used for - /// direct-call cmdlets. - /// - internal Type TypeImplementingCmdlet { get; set; } - - internal bool RunWithCustomRemoting { get; set; } - - /// - /// Cancelling the Async execution. - /// - public void Cancel() - { - if (WorkflowHost == null) - { - throw new InvalidOperationException("WorkflowHost"); - } - - // First, clear pending items - if (this.commandQueue != null) - { - while (!this.commandQueue.IsEmpty) - { - ActivityImplementationContext implementationContext; - bool gotCommand = this.commandQueue.TryDequeue(out implementationContext); - if (gotCommand) - { - System.Management.Automation.PowerShell command = implementationContext.PowerShellInstance; - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity: Cancelling pending command {0}.", command)); - - command.Dispose(); - } - } - } - - // cancel all pending invocations in activityhostmanager - PSResumableActivityHostController resumablecontroller = WorkflowHost.PSActivityHostController as PSResumableActivityHostController; - if (resumablecontroller != null) - { - resumablecontroller.StopAllResumablePSCommands(this.JobInstanceId); - } - else - { - PSOutOfProcessActivityController delegateController = WorkflowHost.PSActivityHostController as PSOutOfProcessActivityController; - if (delegateController != null) - { - while (this.AsyncResults.Count > 0) - { - IAsyncResult result; - this.AsyncResults.TryDequeue(out result); - delegateController.CancelInvokePowerShell(result); - } - } - } - - while (this.runningCommands.Count > 0) - { - System.Management.Automation.PowerShell currentCommand = null; - - lock (this.runningCommands) - { - foreach (System.Management.Automation.PowerShell command in this.runningCommands.Keys) - { - currentCommand = command; - break; - } - - if (currentCommand == null) - { - return; - } - } - - if (currentCommand.InvocationStateInfo.State == PSInvocationState.Running) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity: Stopping command {0}.", currentCommand)); - - try - { - currentCommand.Stop(); - } - catch (NullReferenceException) - { - // The PowerShell API has a bug where it sometimes throws NullReferenceException - // when being stopped. Ignore in this case. - } - catch (InvalidOperationException) - { - // It's possible that the pipeline stopped just before stopping it. - // (In-process case) - } - } - - - // If anything fails, then set the overall failure state for the activity... - if (currentCommand.InvocationStateInfo.State != PSInvocationState.Completed || currentCommand.HadErrors) - { - this.Failed = true; - } - - int commandType = - RunCommandsArguments.DetermineCommandExecutionType( - currentCommand.Runspace.ConnectionInfo as WSManConnectionInfo, RunInProc, ActivityType, this); - if (commandType != PSActivity.RunInProcNoRunspace) - { - PSActivity.CloseRunspaceAndDisposeCommand(currentCommand, WorkflowHost, this, commandType); - } - - this.runningCommands.Remove(currentCommand); - } - } - - /// - /// - /// - /// - /// This function is designed to be lightweight and to - /// run on the Workflow thread when Execute() is called. When - /// any changes are made to this function the contract needs to - /// be honored - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Microsoft.Reliability", - "CA2000:Dispose objects before losing scope", - Justification = "Disposed in EndExecute.")] - public bool Execute() - { - // Create the template of a thread that just pulls from the commandQueue - // and processes the SMA.PowerShell given to it. - /* - - A note about cancellation - ------------------------- - - While a worker thread is running, there's - the possibility of the user cancelling the activity as a whole. - That means, at any line, there's the potential of us wanting - to stop what the thread is doing. - In the worst case of a race condition, the user attempts to cancel - the activity, but we ignore it. - The natural way to prevent this kind of race condition is through - locking so that the 'if(canceled)' condition doesn't change during - individual lines of the thread's invocation. - However, adding this lock would mean that the running thread would - _have_ the lock, which would would prevent the cancellation thread - from _entering_ the lock, preventing the cancellation thread from - signalling its condition. This is, in fact, the very worst case of - the race condition: the user wants to cancel the activity, but we - ignore it. - Verification of cancellation, then, is on a "best-effort" basis. - */ - - if (commandQueue == null) - { - throw new InvalidOperationException("commandQueue"); - } - - ActivityImplementationContext implementationContext; - bool result = commandQueue.TryDequeue(out implementationContext); - - lock (SyncRoot) - { - while (result) - { - RunCommandsArguments args = new RunCommandsArguments(ActivityParams, Output, Input, - this, WorkflowHost, RunInProc, - ParameterDefaults, ActivityType, PrepareSession, - ActivityObject, implementationContext); - // increment count of running commands - Interlocked.Increment(ref CommandsRunningCount); - PSActivity.BeginRunOneCommand(args); - - result = commandQueue.TryDequeue(out implementationContext); - } - - // at this point all commands in the activity context should have been - // started - AllCommandsStarted = true; - } - - return true; - } - - /// - /// Dispose - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose - /// - /// - [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", Justification = "errors is disposed at the time workflow is removed.")] - protected virtual void Dispose(bool disposing) - { - if (!disposing) - return; - - _tracer.Dispose(); - - Callback = null; - PrepareSession = null; - HandleRunspaceStateChanged = null; - ActivityObject = null; - ParameterDefaults = null; - Input = null; - Output = null; - errors = null; - progress = null; - WorkflowHost = null; - ActivityParams = null; - exceptions = null; - runningCommands = null; - commandQueue = null; - - } - } - - /// - /// Defines the activity on-resume action. - /// - public enum ActivityOnResumeAction - { - /// - /// Indicates the resumption is normal. - /// - Resume = 0, - - /// - /// Indicates that the activity needs to be restarted. - /// - Restart = 1, - } - - /// - /// Activities derived from this class can be used in the Pipeline activity - /// - public abstract class PipelineEnabledActivity : NativeActivity - { - - /// - /// The Input stream for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument> Input - { - get; - set; - } - - /// - /// Determines whether to connect the input stream for this activity. - /// - [InputAndOutputCategory] - [DefaultValue(false)] - public bool UseDefaultInput - { - get; - set; - } - - /// - /// The output stream from the activity - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> Result - { - get; - set; - } - - /// - /// Determines whether to append output to Result. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public bool? AppendOutput - { - get; - set; - } - } - - internal class BookmarkContext - { - internal PSWorkflowInstanceExtension BookmarkResumingExtension { get; set; } - internal Bookmark CurrentBookmark { get; set; } - } - - /// - /// Base class for PowerShell-based workflow activities - /// - public abstract class PSActivity : PipelineEnabledActivity - { - /// - /// The bookmark prefix for Powershell activities. - /// - public static readonly string PSBookmarkPrefix = "Microsoft_PowerShell_Workflow_Bookmark_"; - - /// - /// The bookmark prefix for Powershell suspend activities. - /// - public static readonly string PSSuspendBookmarkPrefix = "Microsoft_PowerShell_Workflow_Bookmark_Suspend_"; - - /// - /// The bookmark prefix for Powershell persist activities. - /// - public static readonly string PSPersistBookmarkPrefix = "Microsoft_PowerShell_Workflow_Bookmark_PSPersist_"; - - /// - /// Constructor for the PSActivity class. - /// - protected PSActivity() - { - cancelTimer = new Delay - { - Duration = new InArgument((context) => - new TimeSpan(0, 0, (int)context.GetValue(this.PSActionRunningTimeoutSec))) - }; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public virtual string PSCommandName - { - get - { - return String.Empty; - } - } - - /// - /// Returns the module defining the command called by this activity. - /// It may be null. - /// - protected virtual string PSDefiningModule - { - get - { - return null; - } - } - - /// - /// Tracer initialization. - /// - protected PowerShellTraceSource Tracer - { - get - { - return _tracer; - } - } - - /// - /// In addition to the display name PSProgress Message will provide - /// the way to append the additional information into the activity progress message - /// like branch name or iteration number in case of parallel foreach. - /// - [DefaultValue(null)] - public InArgument PSProgressMessage - { - get; - set; - } - - /// - /// The Error stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSError - { - get; - set; - } - - /// - /// The Progress stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSProgress - { - get; - set; - } - - /// - /// The Verbose stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSVerbose - { - get; - set; - } - - /// - /// The Debug stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSDebug - { - get; - set; - } - - /// - /// The Warning stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSWarning - { - get; - set; - } - - /// - /// The Information stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSInformation - { - get; - set; - } - - /// - /// Forces the activity to return non-serialized objects. Resulting objects - /// have functional methods and properties (as opposed to serialized versions - /// of them), but will not survive persistence when the Workflow crashes or is - /// persisted. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSDisableSerialization - { - get; - set; - } - - /// - /// Forces the activity to not call the persist functionality, which will be responsible for - /// persisting the workflow state onto the disk. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSPersist - { - get; - set; - } - - /// - /// Determines whether to merge error data to the output stream - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument MergeErrorToOutput - { - get; - set; - } - - /// - /// Defines the maximum amount of time, in seconds, that this activity may run. - /// The default is unlimited. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSActionRunningTimeoutSec - { - get; - set; - } - - /// - /// This the list of module names (or paths) that are required to run this Activity successfully. - /// The default is null. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSRequiredModules - { - get; - set; - } - - /// - /// Defines the number of retries that the activity will make when it encounters - /// an error during execution of its action. The default is to not retry. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSActionRetryCount - { - get; - set; - } - - /// - /// Defines the delay, in seconds, between action retry attempts. - /// The default is one second. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSActionRetryIntervalSec - { - get; - set; - } - - /// - /// Determines whether to emit verbose output of the activity. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument Verbose - { - get; - set; - } - - /// - /// Determines whether to emit debug output of the activity. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument Debug - { - get; - set; - } - - /// - /// Determines how errors should be handled by the activity. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument ErrorAction - { - get; - set; - } - - /// - /// Determines how warnings should be handled by the activity. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument WarningAction - { - get; - set; - } - - /// - /// Determines how information records should be handled by the activity. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument InformationAction - { - get; - set; - } - - /// - /// Provides access to the parameter defaults dictionary - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design","CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is forced by the interaction of PowerShell and Workflow.")] - protected Variable> ParameterDefaults - { - get; - set; - } - - private Variable psActivityContextImplementationVariable = new Variable("psActivityContextImplementationVariable"); - private Variable psRunningTimeoutDelayActivityInstanceVar = new Variable("psRunningTimeoutDelayActivityInstanceVar"); - private Delay cancelTimer; - - // Instance variables. The following are OK to be an instance variables, as they are - // not modified during runtime. If any variable holds activity-specific state, - // it must be stored in PSActivityContext. - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static readonly Tracer _structuredTracer = new Tracer(); - - private Variable noPersistHandle = new Variable(); - private Variable bookmarking = new Variable(); - private TerminateWorkflow terminateActivity = new TerminateWorkflow() { Reason = Resources.RunningTimeExceeded }; - private SuspendOnError suspendActivity = new SuspendOnError(); - - /// - /// Records a non-terminating error. If the runtime has associated an error stream, this - /// error will be written to that stream. Otherwise, this will be thrown as an exception. - /// - /// The exception associated with the error. - /// The error ID associated with the error. This should be a non-localized string. - /// The error category associated with the error. - /// The object that was being processed while encountering this error. - /// The powershell activity context. - private static void WriteError(Exception exception, string errorId, ErrorCategory errorCategory, object originalTarget, PSActivityContext psActivityContext) - { - if (psActivityContext.errors != null) - { - ErrorRecord errorRecord = new ErrorRecord(exception, errorId, errorCategory, originalTarget); - lock (psActivityContext.errors) - { - psActivityContext.errors.Add(errorRecord); - } - } - else - { - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(exception); - } - } - } - - /// - /// In order for an activity to go idle, 'CanInduceIdle' should be true. - /// - protected override bool CanInduceIdle - { - get - { - return true; - } - } - - /// - /// Begins the execution of the activity. - /// - /// The NativeActivityContext provided by the workflow. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Microsoft.Reliability", - "CA2000:Dispose objects before losing scope", - Justification = "Disposed in EndExecute.")] - protected override void Execute(NativeActivityContext context) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Beginning execution.", context.ActivityInstanceId)); - - string displayName = this.DisplayName; - - if (string.IsNullOrEmpty(displayName)) - displayName = this.GetType().Name; - - if (_structuredTracer.IsEnabled) - { - _structuredTracer.ActivityExecutionStarted(displayName, this.GetType().FullName); - } - - // bookmarking will only be enabled when the PSPersist variable is set to 'true' by the author. - // so we are retrieving the information before host overrides. - // if the value is false or not provided then we will not go into the unloaded mode. - - bool intBookmarking = InternalBookmarkingRequired(context); - - if (intBookmarking == false) - { - NoPersistHandle handle = this.noPersistHandle.Get(context); - handle.Enter(context); - } - this.bookmarking.Set(context, intBookmarking); - - Dictionary parameterDefaults = new Dictionary(StringComparer.OrdinalIgnoreCase); - HostParameterDefaults hostExtension = null; - - // Retrieve our host overrides - hostExtension = context.GetExtension(); - if (hostExtension != null) - { - Dictionary incomingArguments = hostExtension.Parameters; - foreach (KeyValuePair parameterDefault in incomingArguments) - { - parameterDefaults[parameterDefault.Key] = parameterDefault.Value; - } - - if (parameterDefaults.ContainsKey("PSComputerName")) - { - if (parameterDefaults["PSComputerName"] is String) - { - // If we are given a single string as a parameter default, change it to our required array - parameterDefaults["PSComputerName"] = new object[] { (string) parameterDefaults["PSComputerName"] }; - } - } - - } - - // If they haven't specified 'UseDefaultInput', ignore that host override - if( (! this.UseDefaultInput) && - parameterDefaults.ContainsKey("Input")) - { - parameterDefaults.Remove("Input"); - } - - // Store the values in context - context.SetValue>(this.ParameterDefaults, parameterDefaults); - - // Prepare our state - PSActivityContext psActivityContextInstance = new PSActivityContext(); - psActivityContextInstance.runningCommands = new Dictionary(); - psActivityContextInstance.commandQueue = new ConcurrentQueue(); - psActivityContextInstance.IsCanceled = false; - psActivityContextInstance.HostExtension = hostExtension; - - // If this is a GenericCimCmdletActivity, then copy the type of the cmdlet int0 - // the context so we'll know which type to instantiate at runtime. - GenericCimCmdletActivity genericCimActivity = this as GenericCimCmdletActivity; - if (genericCimActivity != null) - { - psActivityContextInstance.TypeImplementingCmdlet = genericCimActivity.TypeImplementingCmdlet; - } - - foreach (PSActivityArgumentInfo currentArgument in GetActivityArguments()) - { - Argument argumentInstance = currentArgument.Value; - PopulateParameterFromDefault(argumentInstance, context, currentArgument.Name, parameterDefaults); - - // Log the bound parameter - if (argumentInstance.Get(context) != null) - { - Tracer.WriteMessage( - String.Format( - CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Using parameter {1}, with value '{2}'.", - context.ActivityInstanceId, - currentArgument.Name, - argumentInstance.Get(context))); - } - } - - // Commands that get hooked up to the input stream when that - // input stream is empty should not run: - // PS > function foo { $input | Write-Output "Hello" } - // PS > foo - // PS > - PSDataCollection input = Input.Get(context); - if ((input != null) && (input.Count == 0)) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Execution skipped due to supplied (but empty) pipeline input.", context.ActivityInstanceId)); - return; - } - - // Get the output. If it's null (but has been assigned by the user), - // then populate it. - bool needToCreateOutput = false; - PSDataCollection output = Result.Get(context); - - // Set serialization properties on the stream if there is one. - // If there isn't one, the properties will be set when we - // create it. - if (output != null) - { - // Set the collection to serialize by default if that option is specified - if (!GetDisableSerialization(context)) - { - output.SerializeInput = true; - } - else - { - output.SerializeInput = false; - } - } - - if (((output == null) || (! output.IsOpen)) && (this.Result.Expression != null)) - { - if (output == null) - { - output = CreateOutputStream(context); - } - else - { - needToCreateOutput = true; - } - } - else - { - // See if it's a host default stream. If so, the expectation is that this stream will - // remain and hold all results at the end. Otherwise, clear it every invocation. - if ((ParameterDefaults != null) && - parameterDefaults.ContainsKey("Result") && - (parameterDefaults["Result"] == this.Result.Get(context))) - { - // Do nothing - } - else - { - // This is a user-supplied output stream, and should be cleared the same - // way that variables are overwritten when assigned multiple times in a - // C# method. - if (output != null) - { - bool appendOutput = false; - if ((this.AppendOutput != null) && (this.AppendOutput.Value)) - { - appendOutput = true; - } - - if (!appendOutput) - { - needToCreateOutput = true; - } - } - } - } - - // Get the error stream. If it's null (but has been assigned by the user), - // then populate it. - psActivityContextInstance.errors = PSError.Get(context); - if (this.PSError.Expression != null) - { - if ((psActivityContextInstance.errors == null) || psActivityContextInstance.errors.IsAutoGenerated) - { - psActivityContextInstance.errors = new PSDataCollection(); - psActivityContextInstance.errors.IsAutoGenerated = true; - - this.PSError.Set(context, psActivityContextInstance.errors); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: No ErrorStream was passed in; creating a new stream.", - context.ActivityInstanceId)); - } - } - - // Merge error stream to the output stream - if (this.MergeErrorToOutput.Get(context) != null && - this.MergeErrorToOutput.Get(context).GetValueOrDefault(false) && - output != null && psActivityContextInstance.errors != null) - { - // See if it's a host default stream. If so, we need to create our own instance - // to hold the error, so that the host won't consume the event first - if (ParameterDefaults != null && - parameterDefaults.ContainsKey("PSError") && - (parameterDefaults["PSError"] == PSError.Get(context))) - { - psActivityContextInstance.errors = new PSDataCollection(); - psActivityContextInstance.errors.IsAutoGenerated = true; - - this.PSError.Set(context, psActivityContextInstance.errors); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Merge error to the output stream and current error stream is the host default; creating a new stream.", - context.ActivityInstanceId)); - } - - psActivityContextInstance.MergeErrorToOutput = true; - } - - // Get the progress stream. If it's null (but has been assigned by the user), - // then populate it. - PSDataCollection progress = PSProgress.Get(context); - if (this.PSProgress.Expression != null) - { - if ((progress == null) || progress.IsAutoGenerated) - { - progress = new PSDataCollection(); - progress.IsAutoGenerated = true; - - this.PSProgress.Set(context, progress); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No ProgressStream was passed in; creating a new stream.", context.ActivityInstanceId)); - } - } - - psActivityContextInstance.progress = progress; - - // Write the Activity Starting progress record... - WriteProgressRecord(context, progress, Resources.RunningString, ProgressRecordType.Processing); - - // Get the verbose stream. If it's null (but has been assigned by the user), - // then populate it. - PSDataCollection verbose = PSVerbose.Get(context); - if (this.PSVerbose.Expression != null) - { - if ((verbose == null) || verbose.IsAutoGenerated) - { - verbose = new PSDataCollection(); - verbose.IsAutoGenerated = true; - - this.PSVerbose.Set(context, verbose); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No VerboseStream was passed in; creating a new stream.", context.ActivityInstanceId)); - } - } - - // Get the debug stream. If it's null (but has been assigned by the user), - // then populate it. - PSDataCollection debug = PSDebug.Get(context); - if (this.PSDebug.Expression != null) - { - if ((debug == null) || debug.IsAutoGenerated) - { - debug = new PSDataCollection(); - debug.IsAutoGenerated = true; - - this.PSDebug.Set(context, debug); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No DebugStream was passed in; creating a new stream.", context.ActivityInstanceId)); - } - } - - // Get the warning stream. If it's null (but has been assigned by the user), - // then populate it. - PSDataCollection warning = PSWarning.Get(context); - if (this.PSWarning.Expression != null) - { - if ((warning == null) || warning.IsAutoGenerated) - { - warning = new PSDataCollection(); - warning.IsAutoGenerated = true; - - this.PSWarning.Set(context, warning); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No WarningStream was passed in; creating a new stream.", context.ActivityInstanceId)); - } - } - - // Get the information stream. If it's null (but has been assigned by the user), - // then populate it. - PSDataCollection information = PSInformation.Get(context); - if (this.PSInformation.Expression != null) - { - if ((information == null) || information.IsAutoGenerated) - { - information = new PSDataCollection(); - information.IsAutoGenerated = true; - - this.PSInformation.Set(context, information); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No InformationStream was passed in; creating a new stream.", context.ActivityInstanceId)); - } - } - - // Clear the input stream so that it is consumed, but only if streamed from - // the host. We don't want to clear user-supplied streams. - input = Input.Get(context); - if (input != null) - { - bool usedDefaultOutput = false; - - if ((ParameterDefaults != null) && - parameterDefaults.ContainsKey("Input") && - (parameterDefaults["Input"] == this.Input.Get(context))) - { - usedDefaultOutput = true; - } - - if (usedDefaultOutput) - { - PSDataCollection newInput = new PSDataCollection(input); - newInput.IsAutoGenerated = true; - - this.Input.Set(context, newInput); - input.Clear(); - - input = Input.Get(context); - } - } - - // Auto reconnect feature is enabled only for the remoting activity with persistence - bool enableAutoConnectToManagedNode = this is PSRemotingActivity && intBookmarking; - var hostParamValues = context.GetExtension(); - bool remoteActivityResumeOperation = false; - PSWorkflowRemoteActivityState remoteActivityState = null; - if (enableAutoConnectToManagedNode && hostParamValues != null && hostParamValues.RemoteActivityState != null) - { - remoteActivityResumeOperation = hostParamValues.RemoteActivityState.RemoteActivityResumeRequired(this, false); - remoteActivityState = hostParamValues.RemoteActivityState; - } - - // Prepare the command(s) - List commandsToRun = GetTasks(context); - int taskId = 1; - foreach (ActivityImplementationContext commandToRun in commandsToRun) - { - if (remoteActivityState != null) - { - if (remoteActivityResumeOperation == true) - { - // task state is maintained in remoteActivityState as - // state value can be NotStarted, RunspaceId (running), Completed - - object taskEntry = null; - taskEntry = remoteActivityState.GetRemoteActivityRunspaceEntry(this.Id, taskId); - - if (taskEntry != null) - { - // re-execution of completed tasks is not required during the resume operation - string taskCompletion = taskEntry as string; - if ((taskCompletion != null) && (String.Compare(taskCompletion, "completed", StringComparison.OrdinalIgnoreCase) == 0)) - { - taskId++; - continue; - } - - if (taskEntry is Guid) - commandToRun.DisconnectedRunspaceInstanceId = (Guid)taskEntry; - } - } - else - { - // After crash/shutdown, this entry is required for checking all activity level fanout tasks are completed - remoteActivityState.SetRemoteActivityRunspaceEntry(this.Id, taskId, "notstarted", - commandToRun.ConnectionInfo != null ? commandToRun.ConnectionInfo.ComputerName: null); - } - } - - - // If the activity contains a ScriptBlock, then we set the user variables from the workflow. - bool hasScriptBlock = false; - - foreach (PropertyInfo field in this.GetType().GetProperties()) - { - // See if it's an argument - if (typeof(Argument).IsAssignableFrom(field.PropertyType)) - { - // Get the argument - Argument currentArgument = (Argument)field.GetValue(this, null); - - if (currentArgument != null && - (currentArgument.ArgumentType.IsAssignableFrom(typeof(ScriptBlock)) || - currentArgument.ArgumentType.IsAssignableFrom(typeof(ScriptBlock[])))) - { - hasScriptBlock = true; - break; - } - } - } - - // Populate the user variables for locally running activities or activities with scriptblock - if (hasScriptBlock || !(this is PSRemotingActivity)) - { - PopulateRunspaceFromContext(commandToRun, psActivityContextInstance, context); - } - - commandToRun.Id = taskId++; - commandToRun.EnableRemotingActivityAutoResume = enableAutoConnectToManagedNode; - - // If user execute activity with credential but no computername/connectionUri, credential will be bypassed. - // We throw a warning here. - if ((commandToRun.PSCredential != null) && - !(((commandToRun.PSComputerName != null) && !commandToRun.PSComputerName.All(name => string.IsNullOrEmpty(name))) || - ((commandToRun.PSConnectionUri != null) && (commandToRun.PSConnectionUri.Length > 0)))) - - { - commandToRun.PSWarning.Add(new WarningRecord(Resources.CredentialParameterAssignedWithNoComputerName)); - } - psActivityContextInstance.commandQueue.Enqueue(commandToRun); - } - - // Launch our delegate to actually run the given command, - // possibly across many machines. - //Func, PSDataCollection, PSActivityContext, string, - // PSWorkflowHost, bool, Dictionary, Type, PrepareSessionDelegate, object, bool> RunCommandsDelegate = Execute; - - // Set up the max running time trigger - uint? maxRunningTime = PSActionRunningTimeoutSec.Get(context); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Max running time: {1}.", context.ActivityInstanceId, maxRunningTime)); - - if (maxRunningTime.HasValue) - { - psRunningTimeoutDelayActivityInstanceVar.Set(context, context.ScheduleActivity(cancelTimer, MaxRunTimeElapsed)); - } - - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Invoking command.", context.ActivityInstanceId)); - - OnActivityCreated(this, new ActivityCreatedEventArgs(null)); - - uint? connectionRetryCount = null; - uint? connectionRetryInterval = null; - - IImplementsConnectionRetry implementsRetry = this as IImplementsConnectionRetry; - if (implementsRetry != null) - { - connectionRetryCount = implementsRetry.PSConnectionRetryCount.Get(context); - connectionRetryInterval = implementsRetry.PSConnectionRetryIntervalSec.Get(context); - } - - List modulesToLoad = new List(); - if (!string.IsNullOrEmpty(PSDefiningModule)) - { - modulesToLoad.Add(PSDefiningModule); - } - string[] requiredModules = PSRequiredModules.Get(context); - if (requiredModules != null) - { - modulesToLoad.AddRange(requiredModules); - } - - Action activateDelegate = null; - if ((hostExtension != null) && (hostExtension.ActivateDelegate != null)) - { - activateDelegate = hostExtension.ActivateDelegate; - } - - Guid jobInstanceId = Guid.Empty; - object asyncState = null; - WaitCallback callback = null; - Bookmark bookmark = context.CreateBookmark(PSBookmarkPrefix + Guid.NewGuid().ToString(), this.OnResumeBookmark); - - if (activateDelegate != null) - { - asyncState = bookmark; - callback = new WaitCallback(activateDelegate); - jobInstanceId = hostExtension.JobInstanceId; - - if ((hostExtension != null) && (hostExtension.AsyncExecutionCollection != null)) - { - Dictionary asyncExecutionCollection = null; - asyncExecutionCollection = hostExtension.AsyncExecutionCollection; - if (asyncExecutionCollection != null) - { - if (asyncExecutionCollection.ContainsKey(context.ActivityInstanceId)) - { - asyncExecutionCollection.Remove(context.ActivityInstanceId); - } - - asyncExecutionCollection.Add(context.ActivityInstanceId, psActivityContextInstance); - } - } - } - else - { - PSWorkflowInstanceExtension extension = context.GetExtension(); - BookmarkContext resumeContext = new BookmarkContext - { - CurrentBookmark = bookmark, - BookmarkResumingExtension = extension - }; - - callback = OnComplete; - asyncState = resumeContext; - } - - psActivityContextImplementationVariable.Set(context, psActivityContextInstance); - - psActivityContextInstance.Callback = callback; - psActivityContextInstance.AsyncState = asyncState; - psActivityContextInstance.JobInstanceId = jobInstanceId; - - psActivityContextInstance.ActivityParams = new ActivityParameters(connectionRetryCount, connectionRetryInterval, - PSActionRetryCount.Get(context), PSActionRetryIntervalSec.Get(context), - modulesToLoad.ToArray()); - psActivityContextInstance.Input = input; - - if (needToCreateOutput) - { - output = CreateOutputStream(context); - } - - psActivityContextInstance.Output = output; - psActivityContextInstance.WorkflowHost = GetWorkflowHost(hostExtension); - psActivityContextInstance.RunInProc = GetRunInProc(context); - psActivityContextInstance.ParameterDefaults = parameterDefaults; - psActivityContextInstance.ActivityType = GetType(); - psActivityContextInstance.PrepareSession = PrepareSession; - psActivityContextInstance.ActivityObject = this; - - if (IsActivityInlineScript(this) && RunWithCustomRemoting(context)) - { - psActivityContextInstance.RunWithCustomRemoting = true; - } - - // One more time writing the parameter defaults into the context - context.SetValue>(this.ParameterDefaults, parameterDefaults); - - // Execution - psActivityContextInstance.Execute(); - - if (_structuredTracer.IsEnabled) - { - _structuredTracer.ActivityExecutionFinished(displayName); - } - - } - - private bool GetDisableSerialization(NativeActivityContext context) - { - // First check parameter override - bool valueByParameter = PSDisableSerialization.Get(context).GetValueOrDefault(false); - if (valueByParameter) { return valueByParameter; } - - // Next check preference variable - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (string.Equals(property.DisplayName, "PSDisableSerializationPreference", StringComparison.OrdinalIgnoreCase)) - { - object serializationPreference = property.GetValue(context.DataContext); - if (serializationPreference != null) - { - bool variableValue; - if (LanguagePrimitives.TryConvertTo(serializationPreference, CultureInfo.InvariantCulture, out variableValue)) - { - return variableValue; - } - } - } - } - - // Finally, check URI setting. - // Always say "disable serialization" for the Server Manager endpoint. - // This should NOT be removed at the same time as any RDS changes and should be - // evaluated separately. - if (PSSessionConfigurationData.IsServerManager) - { - return true; - } - - return false; - } - - private bool InternalBookmarkingRequired(NativeActivityContext context) - { - bool? activityPersistFlag = GetActivityPersistFlag(context); - return activityPersistFlag.HasValue ? (bool)activityPersistFlag : false; - } - - private void MaxRunTimeElapsed(NativeActivityContext context, ActivityInstance instance) - { - if (instance.State != ActivityInstanceState.Canceled) - { - string message = String.Format(System.Globalization.CultureInfo.CurrentCulture, - Resources.RunningTimeExceeded, PSActionRunningTimeoutSec.Get(context)); - throw new TimeoutException(message); - } - } - - private PSDataCollection CreateOutputStream(NativeActivityContext context) - { - PSDataCollection output = new PSDataCollection(); - output.IsAutoGenerated = true; - output.EnumeratorNeverBlocks = true; - - // Set the collection to serialize by default if that option is specified - if (!GetDisableSerialization(context)) - { - output.SerializeInput = true; - } - else - { - output.SerializeInput = false; - } - - this.Result.Set(context, output); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No OutputStream was passed in; creating a new stream.", context.ActivityInstanceId)); - - return output; - } - - /// - /// Write a progress record fo the current activity - /// - /// Workflow engine context - /// The progress stream to write to - /// The status string to display - /// the Progress record type - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] - protected void WriteProgressRecord(NativeActivityContext context, PSDataCollection progress, string statusDescription, ProgressRecordType type) - { - if (progress == null) - { - // While it seems like we should throw and exception, since we want to run in a non-M3P - // environment, we need to silently ignore this.... - return; - } - - string activityProgMsg = null; - - if ((context != null) && (this.PSProgressMessage != null)) - { - activityProgMsg = this.PSProgressMessage.Get(context); - - // there is no need to write the progress message since the value of psprogressmessage is explicitly provided with null - if (this.PSProgressMessage.Expression != null && string.IsNullOrEmpty(activityProgMsg)) - return; - } - - - string progressActivity; - - if (activityProgMsg == null) - { - progressActivity = this.DisplayName; - - if (string.IsNullOrEmpty(progressActivity)) - progressActivity = this.GetType().Name; - } - else - { - progressActivity = this.DisplayName + ": " + activityProgMsg; - } - - ProgressRecord pr = new ProgressRecord(0, progressActivity, statusDescription); - pr.RecordType = type; - - string currentOperation = this.Id + ":"; - - // Add in position information from the host override - if (context != null) - { - HostParameterDefaults hostExtension = context.GetExtension(); - if (hostExtension != null) - { - HostSettingCommandMetadata commandMetadata = hostExtension.HostCommandMetadata; - if (commandMetadata != null) - { - currentOperation += String.Format(CultureInfo.CurrentCulture, - Resources.ProgressPositionMessage, - commandMetadata.CommandName, commandMetadata.StartLineNumber, commandMetadata.StartColumnNumber); - } - } - } - - pr.CurrentOperation = currentOperation; - - // Look to see if the parent id has been set for this scope... - if (context != null) - { - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (string.Equals(property.DisplayName, WorkflowPreferenceVariables.PSParentActivityId, StringComparison.OrdinalIgnoreCase)) - { - object parentId = property.GetValue(context.DataContext); - if (parentId != null) - { - int idToUse; - if (LanguagePrimitives.TryConvertTo(parentId, CultureInfo.InvariantCulture, out idToUse)) - { - pr.ParentActivityId = idToUse; - } - } - } - else if (string.Equals(property.DisplayName, "ProgressPreference", StringComparison.OrdinalIgnoreCase)) - { - string progressPreference = property.GetValue(context.DataContext) as string; - if (!string.IsNullOrEmpty(progressPreference) && - (string.Equals(progressPreference, "SilentlyContinue", StringComparison.OrdinalIgnoreCase) || - string.Equals(progressPreference, "Ignore", StringComparison.OrdinalIgnoreCase))) - { - // See if we should skip writing out the progress record... - return; - } - } - } - } - - progress.Add(pr); - } - - - private static void OnComplete(object state) - { - _structuredTracer.Correlate(); - - //BookmarkContext bookmarkContext = (BookmarkContext)result.AsyncState; - Dbg.Assert(state != null, "State not passed correctly to OnComplete"); - BookmarkContext bookmarkContext = state as BookmarkContext; - Dbg.Assert(bookmarkContext != null, "BookmarkContext not passed correctly to OnComplete"); - PSWorkflowInstanceExtension extension = bookmarkContext.BookmarkResumingExtension; - Bookmark bookmark = bookmarkContext.CurrentBookmark; - - // Resuming the bookmark - ThreadPool.QueueUserWorkItem(o => extension.BeginResumeBookmark(bookmark, null, ar => extension.EndResumeBookmark(ar), null)); - - } - - private void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object value) - { - _structuredTracer.Correlate(); - - if (this.bookmarking.Get(context) == false) - { - NoPersistHandle handle = this.noPersistHandle.Get(context); - handle.Exit(context); - } - - //context.RemoveBookmark(bookmark); - - // Check for the activity if it is restarting - // this is possible when the activity in bookmarked stated get crashed/terminated - // in this case we restart this activity - ActivityOnResumeAction action = ActivityOnResumeAction.Resume; - if (value != null && value.GetType() == typeof(ActivityOnResumeAction)) - action = (ActivityOnResumeAction) value; - - // All activity level fanout tasks/commands might have finished before the process crash for the remoting activity - // Check activity restart is not required. - var hostParamValues = context.GetExtension(); - if (this is PSRemotingActivity && hostParamValues != null && hostParamValues.RemoteActivityState != null) - { - // Auto reconnect is enabled for remoting activity with PSPersist value true - if (InternalBookmarkingRequired(context)) - { - bool remoteActivityCompleted = !(hostParamValues.RemoteActivityState.RemoteActivityResumeRequired(this, true)); - - if (remoteActivityCompleted == true) - action = ActivityOnResumeAction.Resume; - } - } - - if (action == ActivityOnResumeAction.Restart) - { - this.Execute(context); - return; - } - - // this is expected when there would be a disconnected execution - // here we expects the PSActivityHostArguments to be passed - // argument contains the result from the execution - PSResumableActivityContext arguments = null; - if (value != null && value.GetType() == typeof(PSResumableActivityContext)) - { - arguments = (PSResumableActivityContext)value; - } - if (arguments != null) - { - HostParameterDefaults hostValues = context.GetExtension(); - if (hostValues != null) - { - if (arguments.SupportDisconnectedStreams && arguments.Streams != null) - { - PopulateSteamsData(arguments, context, hostValues); - } - - PSDataCollection psprogress = null; - if (this.PSProgress.Expression != null) - { - psprogress = PSProgress.Get(context); - } - else - { - if (hostValues.Parameters["PSProgress"] != null && hostValues.Parameters["PSProgress"].GetType() == typeof(PSDataCollection)) - { - psprogress = hostValues.Parameters["PSProgress"] as PSDataCollection; - } - } - - // If we got an exception, throw it here. - if (arguments.Error != null) - { - WriteProgressRecord(context, psprogress, Resources.FailedString, ProgressRecordType.Completed); - - Tracer.WriteMessage("PSActivity", "OnResumeBookmark", context.WorkflowInstanceId, - @"We are about to rethrow the exception in order to preserve the stack trace writing it into the logs."); - Tracer.TraceException(arguments.Error); - Dbg.Assert(String.IsNullOrWhiteSpace(arguments.Error.Message) == false, "Exception to be thrown doesnt have proper error message, throwing this will results in Suspended job state instead of Failed state !"); - throw arguments.Error; - } - - // Perform persistence - this.ActivityEndPersistence(context); - - - // Write the activity completed progress record - if (arguments.Failed) - { - WriteProgressRecord(context, psprogress, Resources.FailedString, ProgressRecordType.Completed); - } - else - { - WriteProgressRecord(context, psprogress,Resources.CompletedString, ProgressRecordType.Completed); - } - } - - return; - } - - // In the most of the cases where the state is null - PSActivityContext psActivityContextInstance = null; - PSDataCollection progress = null; - - try - { - if (this.bookmarking.Get(context) == false) - { - progress = PSProgress.Get(context); - psActivityContextInstance = psActivityContextImplementationVariable.Get(context); - - //TODO: add below block in finally for whole function - psActivityContextImplementationVariable.Set(context, null); - - HostParameterDefaults hostValues = context.GetExtension(); - if (hostValues != null) - { - - if ((hostValues != null) && (hostValues.AsyncExecutionCollection != null)) - { - Dictionary asyncExecutionCollection = null; - asyncExecutionCollection = hostValues.AsyncExecutionCollection; - if (asyncExecutionCollection != null) - { - if (asyncExecutionCollection.ContainsKey(context.ActivityInstanceId)) - { - asyncExecutionCollection.Remove(context.ActivityInstanceId); - } - } - } - } - - - } - else - { - HostParameterDefaults hostValues = context.GetExtension(); - if (hostValues != null) - { - - if ((hostValues != null) && (hostValues.AsyncExecutionCollection != null)) - { - Dictionary asyncExecutionCollection = null; - asyncExecutionCollection = hostValues.AsyncExecutionCollection; - if (asyncExecutionCollection != null) - { - if (asyncExecutionCollection.ContainsKey(context.ActivityInstanceId)) - { - psActivityContextInstance = asyncExecutionCollection[context.ActivityInstanceId]; - asyncExecutionCollection.Remove(context.ActivityInstanceId); - } - } - } - } - - if (psActivityContextInstance != null) - { - progress = psActivityContextInstance.progress; - - if (this.Result.Expression != null) - { - this.Result.Set(context, psActivityContextInstance.Output); - } - } - } - - - // Kill the MaxRunningTime timer - var runningCancelTimerActivityInstance = psRunningTimeoutDelayActivityInstanceVar.Get(context); - if (runningCancelTimerActivityInstance != null) - { - psRunningTimeoutDelayActivityInstanceVar.Set(context, null); - context.CancelChild(runningCancelTimerActivityInstance); - } - - if (psActivityContextInstance != null) - { - // If we got an exception, throw it here. - if (psActivityContextInstance.exceptions.Count > 0) - { - WriteProgressRecord(context, progress, Resources.FailedString, ProgressRecordType.Completed); - - Tracer.WriteMessage("PSActivity", "OnResumeBookmark", context.WorkflowInstanceId, - @"We are about to rethrow the exception in order to preserve the stack trace writing it into the logs."); - Tracer.TraceException(psActivityContextInstance.exceptions[0]); - - Dbg.Assert(String.IsNullOrWhiteSpace(psActivityContextInstance.exceptions[0].Message) == false, "Exception to be thrown doesnt have proper error message, throwing this will results in Suspended job state instead of Failed state !"); - throw psActivityContextInstance.exceptions[0]; - } - } - - // Perform persistence - this.ActivityEndPersistence(context); - - - // Write the activity completed progress record - if (psActivityContextInstance != null && psActivityContextInstance.Failed) - { - WriteProgressRecord(context, progress, Resources.FailedString, ProgressRecordType.Completed); - } - else - { - WriteProgressRecord(context, progress, Resources.CompletedString, ProgressRecordType.Completed); - } - } - finally - { - if (psActivityContextInstance != null) - { - // If we had an error to suspend on, schedule the suspend activity - if (psActivityContextInstance.SuspendOnError) - { - context.ScheduleActivity(this.suspendActivity); - } - - psActivityContextInstance.Dispose(); - psActivityContextInstance = null; - } - } - } - - /// - /// The method is override-able by the derived classes in case they would like to implement different logic at the end of persistence. - /// The default behavior would be to schedule the 'Persist' activity if the PSPersist flag is true or Host is asking for it. - /// - /// The native activity context of execution engine. - protected virtual void ActivityEndPersistence(NativeActivityContext context) - { - bool? activityPersist = GetActivityPersistFlag(context); - - bool? HostPSPersistVariable = null; - if (this.PSPersist.Expression == null && this.PSPersist.Get(context).HasValue) - { - HostPSPersistVariable = this.PSPersist.Get(context).Value; - } - - bool hostPersist = false; - hostPersist = this.GetHostPersistFlag(context); - - // Schedule Persist activity if the preference variable was true, the activity explicitly requests it - // or the host has requested persistence. - if (((activityPersist == null) && (HostPSPersistVariable == true)) || hostPersist == true || activityPersist == true) - { - string bookmarkname = PSActivity.PSPersistBookmarkPrefix + Guid.NewGuid().ToString().Replace("-", "_"); - context.CreateBookmark(bookmarkname, BookmarkResumed); - } - } - private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value) - { - } - - - private bool GetHostPersistFlag(NativeActivityContext context) - { - Func hostDelegate = null; - - HostParameterDefaults hostValues = context.GetExtension(); - - if (hostValues != null) - { - if ((hostValues != null) && (hostValues.HostPersistenceDelegate != null)) - { - hostDelegate = hostValues.HostPersistenceDelegate; - } - } - - if (hostDelegate == null) - { - return false; - } - - bool value = hostDelegate(); - - return value; - } - private bool? GetActivityPersistFlag(NativeActivityContext context) - { - bool? activityPersist = null; - if (this.PSPersist.Expression != null && this.PSPersist.Get(context).HasValue) - { - activityPersist = this.PSPersist.Get(context).Value; - } - else - { - // Look to see if there is a PSPersistPreference variable in scope... - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (string.Equals(property.DisplayName, WorkflowPreferenceVariables.PSPersistPreference, StringComparison.OrdinalIgnoreCase)) - { - object variableValue = property.GetValue(context.DataContext); - if (variableValue != null) - { - bool persist; - if (LanguagePrimitives.TryConvertTo(variableValue, CultureInfo.InvariantCulture, out persist)) - { - activityPersist = persist; - } - } - } - } - } - - // if activity level PSPersist value OR $PSPersistPreference are not found try to get the PSPersist value specified at workflow level - if (activityPersist == null) - { - HostParameterDefaults hostExtension = context.GetExtension(); - if (hostExtension != null) - { - Dictionary incomingArguments = hostExtension.Parameters; - if (incomingArguments.ContainsKey(Constants.Persist)) - { - activityPersist = (bool)incomingArguments[Constants.Persist]; - } - } - } - return activityPersist; - } - - private void PopulateSteamsData(PSResumableActivityContext arguments, NativeActivityContext context, HostParameterDefaults hostValues) - { - // setting the output from arguments - if (arguments.Streams.OutputStream != null) - { - if (this.Result.Expression != null) - { - this.Result.Set(context, arguments.Streams.OutputStream); - } - else - { - if (hostValues.Parameters["Result"] != null && hostValues.Parameters["Result"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection output = hostValues.Parameters["Result"] as PSDataCollection; - if (output != arguments.Streams.OutputStream && output != null && output.IsOpen) - { - foreach (PSObject obj in arguments.Streams.OutputStream) - { - output.Add(obj); - } - } - } - } - } - - // setting the input from the arguments - if (arguments.Streams.InputStream != null) - { - if (this.Input.Expression != null) - { - this.Input.Set(context, arguments.Streams.InputStream); - } - else - { - if (hostValues.Parameters["Input"] != null && hostValues.Parameters["Input"].GetType() == typeof(PSDataCollection)) - { - hostValues.Parameters["Input"] = arguments.Streams.InputStream; - } - } - } - - // setting the error from arguments - if (arguments.Streams.ErrorStream != null) - { - if (this.PSError.Expression != null) - { - this.PSError.Set(context, arguments.Streams.ErrorStream); - } - else - { - if (hostValues.Parameters["PSError"] != null && hostValues.Parameters["PSError"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection error = hostValues.Parameters["PSError"] as PSDataCollection; - if (error != arguments.Streams.ErrorStream && error != null && error.IsOpen) - { - foreach (ErrorRecord obj in arguments.Streams.ErrorStream) - { - error.Add(obj); - } - } - } - } - } - - // setting the warning from arguments - if (arguments.Streams.WarningStream != null) - { - if (this.PSWarning.Expression != null) - { - this.PSWarning.Set(context, arguments.Streams.WarningStream); - } - else - { - if (hostValues.Parameters["PSWarning"] != null && hostValues.Parameters["PSWarning"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection warning = hostValues.Parameters["PSWarning"] as PSDataCollection; - if (warning != arguments.Streams.WarningStream && warning != null && warning.IsOpen) - { - foreach (WarningRecord obj in arguments.Streams.WarningStream) - { - warning.Add(obj); - } - } - } - } - } - // setting the progress from arguments - if (arguments.Streams.ProgressStream != null) - { - if (this.PSProgress.Expression != null) - { - this.PSProgress.Set(context, arguments.Streams.ProgressStream); - } - else - { - if (hostValues.Parameters["PSProgress"] != null && hostValues.Parameters["PSProgress"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection tmpProgress = hostValues.Parameters["PSProgress"] as PSDataCollection; - if (tmpProgress != arguments.Streams.ProgressStream && tmpProgress != null && tmpProgress.IsOpen) - { - foreach (ProgressRecord obj in arguments.Streams.ProgressStream) - { - tmpProgress.Add(obj); - } - } - } - } - } - - // setting the verbose from arguments - if (arguments.Streams.VerboseStream != null) - { - if (this.PSVerbose.Expression != null) - { - this.PSVerbose.Set(context, arguments.Streams.VerboseStream); - } - else - { - if (hostValues.Parameters["PSVerbose"] != null && hostValues.Parameters["PSVerbose"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection verbose = hostValues.Parameters["PSVerbose"] as PSDataCollection; - if (verbose != arguments.Streams.VerboseStream && verbose != null && verbose.IsOpen) - { - foreach (VerboseRecord obj in arguments.Streams.VerboseStream) - { - verbose.Add(obj); - } - } - } - } - } - // setting the debug from arguments - if (arguments.Streams.DebugStream != null) - { - if (this.PSDebug.Expression != null) - { - this.PSDebug.Set(context, arguments.Streams.DebugStream); - } - else - { - if (hostValues.Parameters["PSDebug"] != null && hostValues.Parameters["PSDebug"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection debug = hostValues.Parameters["PSDebug"] as PSDataCollection; - if (debug != arguments.Streams.DebugStream && debug != null && debug.IsOpen) - { - foreach (DebugRecord obj in arguments.Streams.DebugStream) - { - debug.Add(obj); - } - } - } - } - } - - // setting the information stream from arguments - if (arguments.Streams.InformationStream != null) - { - if (this.PSInformation.Expression != null) - { - this.PSInformation.Set(context, arguments.Streams.InformationStream); - } - else - { - if (hostValues.Parameters["PSInformation"] != null && hostValues.Parameters["PSInformation"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection information = hostValues.Parameters["PSInformation"] as PSDataCollection; - if (information != arguments.Streams.InformationStream && information != null && information.IsOpen) - { - foreach (InformationRecord obj in arguments.Streams.InformationStream) - { - information.Add(obj); - } - } - } - } - } - } - - /// - /// Populates the runspace with user variables from the current context - /// - /// - /// - /// - private void PopulateRunspaceFromContext(ActivityImplementationContext implementationContext, PSActivityContext activityContext, NativeActivityContext context) - { - System.Diagnostics.Debug.Assert(implementationContext != null, "Implementation context cannot be null"); - - if (implementationContext.PowerShellInstance != null) - { - // Then, set the variables from the workflow - SetActivityVariables(context, activityContext.UserVariables); - } - } - - /// - /// Populates the required variables to set in runspace - /// - /// - /// - internal void SetActivityVariables(NativeActivityContext context, Dictionary activityVariables) - { - Dictionary defaults = this.ParameterDefaults.Get(context); - - string[] streams = - { - "Result", "PSError", "PSWarning", "PSVerbose", "PSDebug", "PSProgress", "PSInformation" - }; - - // First, set the variables from the user's variables - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (String.Equals(property.Name, "ParameterDefaults", StringComparison.OrdinalIgnoreCase)) - continue; - - Object value = property.GetValue(context.DataContext); - if (value != null) - { - object tempValue = value; - - PSDataCollection collectionObject = value as PSDataCollection; - - if (collectionObject != null && collectionObject.Count == 1) - { - tempValue = collectionObject[0]; - } - - activityVariables[property.Name] = tempValue; - } - } - - // Then, set anything we received from parameters - foreach (PSActivityArgumentInfo currentArgument in GetActivityArguments()) - { - string @default = currentArgument.Name; - if (streams.Any(item => string.Equals(item, @default, StringComparison.OrdinalIgnoreCase))) - continue; - - object argumentValue = currentArgument.Value.Get(context); - if (argumentValue != null && !activityVariables.ContainsKey(currentArgument.Name)) - { - activityVariables[currentArgument.Name] = argumentValue; - } - } - - // Then, set the variables from the host defaults - if (defaults != null) - { - foreach (string hostDefault in defaults.Keys) - { - string @default = hostDefault; - if (streams.Any(item => string.Equals(item, @default, StringComparison.OrdinalIgnoreCase))) - continue; - - object propertyValue = defaults[hostDefault]; - if (propertyValue != null && !activityVariables.ContainsKey(hostDefault)) - { - activityVariables[hostDefault] = propertyValue; - } - } - } - } - - /// - /// Populates a parameter from the defaults supplied by the workflow host - /// - /// The argument to modify (i.e.: Input, Result, ComputerName, etc) - /// The activity context to use - /// The name of the argument - /// The parameter defaults - private void PopulateParameterFromDefault(Argument argument, NativeActivityContext context, string argumentName, Dictionary parameterDefaults) - { - // See if they haven't specified a value - if ((argument != null) && - (argument.Expression == null) && - (argument.Direction != ArgumentDirection.Out)) - { - // See if we have a parameter defaults collection - if (ParameterDefaults != null) - { - // See if it has this parameter - if (parameterDefaults.ContainsKey(argumentName)) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Using default {1} value.", context.ActivityInstanceId, argumentName)); - Object parameterDefault = parameterDefaults[argumentName]; - - // Map any switch parameters to booleans if required - if ((argument.ArgumentType == typeof(Boolean)) || - (argument.ArgumentType == typeof(Nullable))) - { - if (parameterDefault is SwitchParameter) - { - parameterDefault = ((SwitchParameter) parameterDefault).ToBool(); - } - } - - // If the argument type is nullable, but the provided default - // is a non-nullable version, create a nullable version of it - // to set. - if ((argument.ArgumentType.IsGenericType) && - (argument.ArgumentType.GetGenericTypeDefinition() == typeof(Nullable<>)) && - (!(parameterDefault is Nullable))) - { - parameterDefault = LanguagePrimitives.ConvertTo(parameterDefault, - argument.ArgumentType, - CultureInfo.InvariantCulture); - } - - if (argument.ArgumentType.IsAssignableFrom(typeof(PSCredential)) && - parameterDefault.GetType().IsAssignableFrom(typeof(PSObject))) - { - parameterDefault = LanguagePrimitives.ConvertTo(parameterDefault, - typeof(PSCredential), CultureInfo.InvariantCulture); - } - - argument.Set(context, parameterDefault); - } - } - } - } - - // Populates the activity task variables from the current PSActivity instance - private void PopulateActivityImplementationContext(ActivityImplementationContext implementationContext, NativeActivityContext context, int index) - { - // Go through all of the public PSActivity arguments, and populate their value - // into the ActivityImplementationContext. - foreach (PSActivityArgumentInfo field in GetActivityArguments()) - { - // Get the field of the same name from the 'ActivityImplementationContext' class - PropertyInfo implementationContextField = implementationContext.GetType().GetProperty(field.Name); - if (implementationContextField == null) - { - // If you have added a new argument then please added it to 'ActivityImplementationContext' classs. - throw new Exception("Could not find corresponding task context field for activity argument: " + field.Name); - } - - if (string.Equals(field.Name, PSComputerName, StringComparison.OrdinalIgnoreCase) && index != -1) - { - // set only the corresponding entry for computername - PopulatePSComputerName(implementationContext, context, field, index); - } - else - { - // And set it in the task context - implementationContextField.SetValue(implementationContext, field.Value.Get(context), null); - } - } - } - - private const string PSComputerName = "PSComputerName"; - private static void PopulatePSComputerName(ActivityImplementationContext implementationContext, NativeActivityContext context, PSActivityArgumentInfo field, int index) - { - Dbg.Assert(string.Equals(field.Name, PSComputerName, StringComparison.OrdinalIgnoreCase), "PSActivityArgumentInfo passed should be for PSComputerName"); - - PropertyInfo computerNameField = implementationContext.GetType().GetProperty(field.Name); - string[] computerNames = (string[]) field.Value.Get(context); - - computerNameField.SetValue(implementationContext, new string[]{computerNames[index]}, null); - } - - /// - /// Overload this method to implement any command-type specific preparations. - /// If this command needs any workflow-specific information during its PrepareSession call, - /// it should be stored in ActivityImplementationContext.WorkflowContext. - /// - /// The activity context to use - protected virtual List GetImplementation(NativeActivityContext context) - { - ActivityImplementationContext implementationContext = GetPowerShell(context); - UpdateImplementationContextForLocalExecution(implementationContext, context); - - return new List() { implementationContext }; - } - - /// - /// Updates the ImplementationContext returned from GetPowerShell() to support local execution - /// against the host's runspace pool. - /// - /// The implementation context returned by GetPowerShell() - /// The activity context to use - protected internal void UpdateImplementationContextForLocalExecution(ActivityImplementationContext implementationContext, ActivityContext context) - { - } - - - /// - /// The method for derived activities to return a configured instance of System.Management.Automation.PowerShell. - /// The implementor should have added all of the commands and parameters required to launch their command through - /// the standard AddCommand() and AddParameter() methods. Derived activities should not manage the Runspace property - /// directly, as the PSActivity class configures the runspace afterward to enable remote connections. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected abstract ActivityImplementationContext GetPowerShell(NativeActivityContext context); - - - /// - /// The method for derived activities to customize the runspace of a System.Management.Automation.PowerShell instance - /// that the runtime has prepared for them. - /// If the command needs any workflow-specific information during this PrepareSession call, - /// it should be stored in ActivityImplementationContext.WorkflowContext during the GetCommand preparation phase. - /// - /// The ActivityImplementationContext returned by the call to GetCommand. - protected virtual void PrepareSession(ActivityImplementationContext implementationContext) - { - } - - /// - /// If an activity needs to load a module before it can execute, override this member - /// to return the name of that module. - /// - protected string DefiningModule - { - get - { - return string.Empty; - } - } - - /// - /// Retrieve all of the default arguments from the type and its parents. - /// - /// All of the default arguments from the type and its parents - protected IEnumerable GetActivityArguments() - { - // Walk up the type hierarchy, looking for types that we should pull in - // parameter defaults. - Type currentType = this.GetType(); - - while (currentType != null) - { - // We don't want to support parameter defaults for arguments on - // concrete types (as they almost guaranteed to collide with other types), - // but base classes make sense. - if (currentType.IsAbstract) - { - // Populate any parameter defaults. We only look at fields that are defined on this - // specific type (as opposed to derived types) so that we don't make assumptions about - // other activities and their defaults. - foreach (PropertyInfo field in currentType.GetProperties()) - { - // See if it's an argument - if (typeof(Argument).IsAssignableFrom(field.PropertyType)) - { - // Get the argument - Argument currentArgument = (Argument)field.GetValue(this, null); - yield return new PSActivityArgumentInfo { Name = field.Name, Value = currentArgument }; - } - } - } - - // Go to our base type, but stop when we go above PSActivity - currentType = currentType.BaseType; - if (!typeof(PSActivity).IsAssignableFrom(currentType)) - currentType = null; - } - } - - // Get the tasks from the derived activity, and save the state we require. - private List GetTasks(NativeActivityContext context) - { - List tasks = GetImplementation(context); - - if (IsActivityInlineScript(this)) - { - // in case of inlinescript activity supporting custom remoting we need - // to ensure that PSComputerName is populated only for the specified - // index - if (RunWithCustomRemoting(context)) - { - for(int i=0; i - /// Indicates if preference variables need to be updated - /// - protected virtual bool UpdatePreferenceVariable - { - get { return true; } - } - - // Common configuration of a SMA.PowerShell instance. - /// - /// Add the other streams and enqueue the command to be run - /// - /// The activity context to use - /// The powershell activity context. - /// The activity type. - /// The prepare session delegate. - /// This object representing activity. - private static void UpdatePowerShell(ActivityImplementationContext implementationContext, PSActivityContext psActivityContext, Type ActivityType, PrepareSessionDelegate PrepareSession, object activityObject) - { - try - { - PrepareSession(implementationContext); - System.Management.Automation.PowerShell invoker = implementationContext.PowerShellInstance; - - // Prepare the streams - if (implementationContext.PSError != null) - { - invoker.Streams.Error = implementationContext.PSError; - } - - if (implementationContext.PSProgress != null) - { - invoker.Streams.Progress = implementationContext.PSProgress; - } - - if (implementationContext.PSVerbose != null) - { - invoker.Streams.Verbose = implementationContext.PSVerbose; - } - - if (implementationContext.PSDebug != null) - { - invoker.Streams.Debug = implementationContext.PSDebug; - } - - if (implementationContext.PSWarning != null) - { - invoker.Streams.Warning = implementationContext.PSWarning; - } - - if (implementationContext.PSInformation != null) - { - invoker.Streams.Information = implementationContext.PSInformation; - } - - // InlineScript activity needs to handle these in its own way - PSActivity activityBase = activityObject as PSActivity; - Dbg.Assert(activityBase != null, "Only activities derived from PSActivityBase are supported"); - if (activityBase.UpdatePreferenceVariable) - { - UpdatePreferenceVariables(implementationContext); - } - - //OnActivityCreated(activityObject, new ActivityCreatedEventArgs(invoker)); - } - catch (Exception e) - { - // Catch all exceptions and add them to the exception list. - // This way, they will be reported on EndExecute(), rather than - // killing the process if an exception happens on the background thread. - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(e); - } - } - } - - private static void UpdatePreferenceVariables(ActivityImplementationContext implementationContext) - { - // Update the PowerShell ubiquitous parameters - Command activityCommand = implementationContext.PowerShellInstance.Commands.Commands[0]; - - if (implementationContext.Verbose != null) - { - activityCommand.Parameters.Add("Verbose", implementationContext.Verbose); - } - - if (implementationContext.Debug != null) - { - activityCommand.Parameters.Add("Debug", implementationContext.Debug); - } - - if (implementationContext.WhatIf != null) - { - activityCommand.Parameters.Add("WhatIf", implementationContext.WhatIf); - } - - if (implementationContext.ErrorAction != null) - { - activityCommand.Parameters.Add("ErrorAction", implementationContext.ErrorAction); - } - - if (implementationContext.WarningAction != null) - { - activityCommand.Parameters.Add("WarningAction", implementationContext.WarningAction); - } - - if (implementationContext.InformationAction != null) - { - activityCommand.Parameters.Add("InformationAction", implementationContext.InformationAction); - } - } - - internal const int CommandRunInProc = 0; - internal const int RunInProcNoRunspace = 1; - internal const int CommandRunOutOfProc = 2; - internal const int CommandRunRemotely = 3; - internal const int CimCommandRunInProc = 4; - internal const int CleanupActivity = 5; - - /// - /// - /// - /// - /// THREADING CONTRACT: This function is designed to be - /// lightweight and to run on the Workflow thread when Execute() - /// is called. When any changes are made to this function the contract - /// needs to be honored - internal static void BeginRunOneCommand(RunCommandsArguments args) - { - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Beginning action to run command {0}.", commandToRun)); - - if (CheckForCancel(psActivityContext)) return; - - // initialize for command execution based on type - InitializeOneCommand(args); - - // run the command based on type - if (args.CommandExecutionType != CommandRunRemotely && - args.CommandExecutionType != CommandRunInProc && - args.CommandExecutionType != CimCommandRunInProc) - { - // when the command is run on a remote runspace it is - // executed on the callback thread from connection - // manager - BeginExecuteOneCommand(args); - } - } - } - - /// - /// Initialize a single command for execution. - /// - /// - /// THREADING CONTRACT: This function is designed to be - /// lightweight and to run on the Workflow thread when Execute() - /// is called. When any changes are made to this function the - /// contract needs to be honored - private static void InitializeOneCommand(RunCommandsArguments args) - { - ActivityParameters activityParameters = args.ActivityParameters; - PSActivityContext psActivityContext = args.PSActivityContext; - PSWorkflowHost workflowHost = args.WorkflowHost; - Dictionary parameterDefaults = args.ParameterDefaults; - Type activityType = args.ActivityType; - PrepareSessionDelegate prepareSession = args.Delegate; - object activityObject = args.ActivityObject; - ActivityImplementationContext implementationContext = args.ImplementationContext; - int commandExecutionType = args.CommandExecutionType; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Beginning initialization for command '{0}'.", commandToRun)); - // - // Common Initialization - // - - // Store which command we're running. We need to lock this one - // because Dictionary is not thread-safe (while ConcurrentQueue is, - // by definition.) - lock (psActivityContext.runningCommands) - { - if (CheckForCancel(psActivityContext)) return; - - if (!psActivityContext.runningCommands.ContainsKey(commandToRun)) - psActivityContext.runningCommands[commandToRun] = new RetryCount(); - } - - if (CheckForCancel(psActivityContext)) return; - - // Record the invocation attempt - psActivityContext.runningCommands[commandToRun].ActionAttempts++; - - // NOTE: previously UpdatePowerShell was called after the runspace - // for the command was open and available. An assumption is made - // that this is not required and hence the code is moved before - // the runspace is available. If there are any issues found flip - // the order back - - // Let the activity prepare the PowerShell instance. - if (commandExecutionType != CleanupActivity) - UpdatePowerShell(implementationContext, psActivityContext, activityType, prepareSession, - activityObject); - - // - // Initialization based on type - // - switch (commandExecutionType) - { - case CommandRunInProc: - { - // good to run on current thread - InitializeActivityEnvironmentAndAddRequiredModules(implementationContext, activityParameters); - workflowHost.LocalRunspaceProvider.BeginGetRunspace(null, 0, 0, - LocalRunspaceProviderCallback, args); - } - break; - - case RunInProcNoRunspace: - { - - // These commands don't have a runspace - // so we don't need to do anything here - } - break; - - case CimCommandRunInProc: - { - workflowHost.LocalRunspaceProvider.BeginGetRunspace(null, 0, 0, - LocalRunspaceProviderCallback, args); - } - break; - - case CommandRunOutOfProc: - { - // out of proc commands do not require the runspace in the - // powershell. However since this runspace was created by - // the local runspace provider it need to be released and - // not disposed We need to release the connection - // before calling into activity host manager so as to not - // risk closing an out of process runspace if - // things happen too quickly - - InitializeActivityEnvironmentAndAddRequiredModules(implementationContext, activityParameters); - - } - break; - case CommandRunRemotely: - { - // when a command is run remotely, the connection manager - // assigns a remote runspace. Dispose the existing one - // We should not set the runspace to null as it required in - // the callback to retrieve connection info in case there - // is a connection failure. We need to close the connection - // before calling into connection manager so as to not - // risk closing a connection manager assigned runspace if - // things happen too quickly - DisposeRunspaceInPowerShell(commandToRun, false); - - InitializeActivityEnvironmentAndAddRequiredModules(implementationContext, activityParameters); - - // can block - should be run on a different thread - WSManConnectionInfo connectionInfo = - commandToRun.Runspace.ConnectionInfo as WSManConnectionInfo; - workflowHost.RemoteRunspaceProvider.BeginGetRunspace(connectionInfo, - activityParameters.ConnectionRetryCount. - GetValueOrDefault(0), - activityParameters.ConnectionRetryInterval. - GetValueOrDefault(0), - ConnectionManagerCallback, - args); - - } - break; - - case CleanupActivity: - { - // no initialization required - } - break; - } - } - } - - private static void InitializeActivityEnvironmentAndAddRequiredModules(ActivityImplementationContext implementationContext, - ActivityParameters activityParameters) - { - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - if (implementationContext.PSActivityEnvironment == null) - implementationContext.PSActivityEnvironment = new PSActivityEnvironment(); - - PSActivityEnvironment policy = implementationContext.PSActivityEnvironment; - - foreach (string module in activityParameters.PSRequiredModules ?? new string[0]) - { - actionTracer.WriteMessage("Adding dependent module to policy: " + module); - policy.Modules.Add(module); - } - } - } - - private static void DisposeRunspaceInPowerShell(System.Management.Automation.PowerShell commandToRun, bool setToNull=true) - { - Dbg.Assert(commandToRun.Runspace != null, "Method cannot be called when runspace is null"); - commandToRun.Runspace.Dispose(); - commandToRun.Runspace.Close(); - if (setToNull) - commandToRun.Runspace = null; - } - - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This function runs on the workflow thread when Execute() - /// is called for all cases except the remote runspace case - /// where it runs on a WinRM thread or the connection manager - /// servicing thread - /// Therefore this function is designed to be lightweight in - /// all cases - /// When any changes are made to this function the contract needs to - /// be honored - private static void BeginExecuteOneCommand(RunCommandsArguments args) - { - PSActivityContext psActivityContext = args.PSActivityContext; - PSWorkflowHost workflowHost = args.WorkflowHost; - ActivityImplementationContext implementationContext = args.ImplementationContext; - PSDataCollection input = args.Input; - PSDataCollection output = args.Output; - int commandExecutionType = args.CommandExecutionType; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "BEGIN BeginExecuteOneCommand {0}.", commandToRun)); - - switch (commandExecutionType) - { - case RunInProcNoRunspace: - { - // execute on threadpool thread - ThreadPool.QueueUserWorkItem(ExecuteOneRunspaceFreeCommandWorker, args); - } - break; - - case CimCommandRunInProc: - { - // execute on threadpool thread - ThreadPool.QueueUserWorkItem(InitializeRunspaceAndExecuteCommandWorker, args); - } - break; - - case CommandRunOutOfProc: - { - Dbg.Assert(implementationContext.PSActivityEnvironment != null, - "Policy should have been initialized correctly by the initialization method"); - - if (CheckForCancel(psActivityContext)) return; - - PSResumableActivityHostController resumableController = workflowHost.PSActivityHostController as PSResumableActivityHostController; - if (resumableController != null) - { - PowerShellStreams streams = new PowerShellStreams(); - - if (resumableController.SupportDisconnectedPSStreams) - { - streams.InputStream = input; - streams.OutputStream = new PSDataCollection(); - streams.ErrorStream = new PSDataCollection(); - streams.DebugStream = new PSDataCollection(); - streams.ProgressStream = new PSDataCollection(); - streams.VerboseStream = new PSDataCollection(); - streams.WarningStream = new PSDataCollection(); - streams.InformationStream = new PSDataCollection(); - } - else - { - streams.InputStream = input; - streams.OutputStream = output; - streams.DebugStream = commandToRun.Streams.Debug; - streams.ErrorStream = commandToRun.Streams.Error; - streams.ProgressStream = commandToRun.Streams.Progress; - streams.VerboseStream = commandToRun.Streams.Verbose; - streams.WarningStream = commandToRun.Streams.Warning; - streams.InformationStream = commandToRun.Streams.Information; - } - - resumableController.StartResumablePSCommand(args.PSActivityContext.JobInstanceId, - (Bookmark)args.PSActivityContext.AsyncState, - commandToRun, - streams, - implementationContext.PSActivityEnvironment, - (PSActivity)psActivityContext.ActivityObject); - } - else - { - PSOutOfProcessActivityController delegateController = workflowHost.PSActivityHostController as PSOutOfProcessActivityController; - if (delegateController != null) - { - AddHandlersToStreams(commandToRun, args); - - IAsyncResult asyncResult = - delegateController.BeginInvokePowerShell(commandToRun, input, - output, - implementationContext.PSActivityEnvironment, - ActivityHostManagerCallback, - args); - psActivityContext.AsyncResults.Enqueue(asyncResult); - } - } - } - break; - - case CommandRunRemotely: - { - ArgsTableForRunspaces.TryAdd(commandToRun.Runspace.InstanceId, args); - InitializeRunspaceAndExecuteCommandWorker(args); - } - break; - case CommandRunInProc: - { - commandToRun.Runspace.ThreadOptions = PSThreadOptions.UseCurrentThread; - ThreadPool.QueueUserWorkItem(InitializeRunspaceAndExecuteCommandWorker, args); - } - break; - case CleanupActivity: - { - ExecuteCleanupActivity(args); - } - break; - } - actionTracer.WriteMessage("END BeginExecuteOneCommand"); - } - } - - /// - /// Calls the DoCleanup method of the cleanup activity - /// - /// RunCommandsArguments - /// - /// THREADING CONTRACT: - /// This function runs on the workflow thread when Execute() - /// is called - /// Therefore this function is designed to be lightweight in - /// all cases - /// When any changes are made to this function the contract needs to - /// be honored - private static void ExecuteCleanupActivity(RunCommandsArguments args) - { - PSCleanupActivity cleanupActivity = args.ActivityObject as PSCleanupActivity; - if (cleanupActivity == null) - { - throw new ArgumentNullException("args"); - } - cleanupActivity.DoCleanup(args, CleanupActivityCallback); - } - - /// - /// Callback when all connections to the specified computer - /// are closed - /// - /// RunCommandsArguments that the activity - /// passed int - /// - /// THREADING CONTRACT: - /// The callback will happen on a WinRM thread. Hence the - /// function needs to be lightweight to release the thread - /// back to WinRM - /// - private static void CleanupActivityCallback(object state) - { - RunCommandsArguments args = state as RunCommandsArguments; - Dbg.Assert(args != null, "Clean up activity should pass back RunCommandsArguments"); - DecrementRunningCountAndCheckForEnd(args.PSActivityContext); - } - - private static void ExecuteOneRunspaceFreeCommandWorker(object state) - { - Dbg.Assert(state != null, "State needs to be passed to ExecuteOneWmiCommandWorker"); - RunCommandsArguments args = state as RunCommandsArguments; - Dbg.Assert(args != null, "RunCommandsArguments not passed correctly to ExecuteOneWmiCommandWorker"); - - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - PSDataCollection input = args.Input; - PSDataCollection output = args.Output; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - bool attemptRetry = false; - try - { - actionTracer.WriteMessage("Running WMI/CIM generic activity on ThreadPool thread"); - RunDirectExecutionActivity(implementationContext.PowerShellInstance, input, output, psActivityContext, implementationContext); - } - catch (Exception e) - { - PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource(); - tracer.TraceException(e); - - attemptRetry = HandleRunOneCommandException(args, e); - if (attemptRetry) - BeginActionRetry(args); - } - finally - { - implementationContext.CleanUp(); - - RunOneCommandFinally(args, attemptRetry); - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity: Finished running command.")); - // decrement count of running commands - DecrementRunningCountAndCheckForEnd(psActivityContext); - } - } - - } - - private static void BeginActionRetry(RunCommandsArguments args) - { - Dbg.Assert(args != null, "Arguments not passed correctly to BeginActionRetry"); - PSActivityContext psActivityContext = args.PSActivityContext; - - Interlocked.Increment(ref psActivityContext.CommandsRunningCount); - BeginRunOneCommand(args); - } - - private static bool HandleRunOneCommandException(RunCommandsArguments args, Exception e) - { - bool attemptRetry = false; - - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - ActivityParameters activityParameters = args.ActivityParameters; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Exception handling for command {0}.", commandToRun)); - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Got exception running command: {0}.", e.Message)); - -#if(DEBUG) - // Synchronization code to help tests verify that canceling during error handling doesn't - // cause unhandled exceptions. - const string timingTest = "Test.Cancellation.During.Error.Handling"; - if (Environment.GetEnvironmentVariable(timingTest) != null) - { - while (!String.Equals(Environment.GetEnvironmentVariable(timingTest), - "TestReady", StringComparison.OrdinalIgnoreCase)) - { - Thread.Sleep(50); - } - Environment.SetEnvironmentVariable(timingTest, null); - } - Thread.Sleep(100); -#endif - - // If we've used more attempts than retries, - // this is a fatal error. We initialize this to MaxValue in case the activity was canceled, - // in which case we want no more retries. - int attempts = Int32.MaxValue; - - // No need add the exceptions in the list if the activity is already canceled. - if (!psActivityContext.IsCanceled) - { - // if there is a connection failure then Connection manager - // callback will handle the same. However if we got a connection - // and it broke while we are preparing we need to attempt a retry - if (psActivityContext.runningCommands.ContainsKey(commandToRun)) - attempts = psActivityContext.runningCommands[commandToRun].ActionAttempts; - - attemptRetry = HandleFailure(attempts, activityParameters.ActionRetryCount, - activityParameters.ActionRetryInterval, - implementationContext, "ActivityActionFailed", e, - psActivityContext); - } - } - - return attemptRetry; - } - - private static void RunOneCommandFinally(RunCommandsArguments args, bool attemptRetry) - { - if (attemptRetry) return; - - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - PSWorkflowHost workflowHost = args.WorkflowHost; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - - // Once we're done, remove this command from the list - // of running commands. - lock (psActivityContext.runningCommands) - { - psActivityContext.runningCommands.Remove(commandToRun); - } - - // Discard the runspace - don't need to do this for activities that don't use a runspace - if (!psActivityContext.IsCanceled && args.CommandExecutionType != RunInProcNoRunspace) - { - CloseRunspaceAndDisposeCommand(commandToRun, workflowHost, psActivityContext, args.CommandExecutionType); - } - } - - /// - /// Callback from connection manager - /// - /// - /// - /// THREADING CONTRACT: - /// The callback happens either in a WinRM thread or in the - /// connection manager servicing thread. Therefore any - /// operations that this thread initiates is supposed to - /// be very small. Make sure that this contract is maintained - /// when any changes are made to the function - private static void ConnectionManagerCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState not returned correctly by connection manager"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - PSWorkflowHost workflowHost = args.WorkflowHost; - PSActivityContext psActivityContext = args.PSActivityContext; - WSManConnectionInfo connectionInfo = commandToRun.Runspace.ConnectionInfo as WSManConnectionInfo; - Dbg.Assert(connectionInfo != null, "ConnectionInfo cannot be null"); - string[] psComputerName = implementationContext.PSComputerName; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for GetRunspace for computer ", - commandToRun.Runspace.ConnectionInfo.ComputerName); - Runspace runspace = null; - try - { - runspace = workflowHost.RemoteRunspaceProvider.EndGetRunspace(asyncResult); - } - catch (Exception exception) - { - // there is an error in connecting to the specified computer - // handle the same - tracer.WriteMessage("Error in connecting to remote computer ", - commandToRun.Runspace.ConnectionInfo.ComputerName); - // If this was a multi-machine activity, this is an error, not an exception.) - if ((psComputerName != null) && (psComputerName.Length > 1)) - { - WriteError(exception, "ConnectionAttemptFailed", - ErrorCategory.InvalidResult, - psComputerName, psActivityContext); - } - else - { - var failureErrorRecord = new ErrorRecord(exception, "ConnectionAttemptFailed", ErrorCategory.OpenError, - psComputerName); - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(new RuntimeException(exception.Message, exception, - failureErrorRecord)); - } - } - - // when runspace connection cannot be obtained we will not retry - RunOneCommandFinally(args, false); - // when runspace cannot be obtained we cannot execute the command - // simply decrement count and return - DecrementRunningCountAndCheckForEnd(psActivityContext); - return; - } - - // runspace should be in an opened state when connection manager - // assigns the same - tracer.WriteMessage("Runspace successfully obtained with guid ", runspace.InstanceId.ToString()); - - // Before the connection was assigned, the activity could have been cancelled, therefore - // we need to check and release before return. - if (psActivityContext.IsCanceled) - { - CloseRunspace(runspace, CommandRunRemotely, workflowHost, psActivityContext); - if (CheckForCancel(psActivityContext)) return; - } - - // In case of auto reconnect after crash/shutdown, the disconnected remote runspace is already assigned to the disconnected powershell instance - // Assign the runspace only if instance ids are different - if (!commandToRun.Runspace.InstanceId.Equals(runspace.InstanceId)) - commandToRun.Runspace = runspace; - - // the handler is saved in the context so that the registration - // can be unregistered later - psActivityContext.HandleRunspaceStateChanged = HandleRunspaceStateChanged; - commandToRun.Runspace.StateChanged += psActivityContext.HandleRunspaceStateChanged; - - // Invocation state can be in running or final state in reconnect scenario - // Command should be invoked only when invocation state is not started, - // during the normal scenario, invocation state is always NotStarted as runspace is just assigned - if (commandToRun.InvocationStateInfo.State == PSInvocationState.NotStarted) - { - BeginExecuteOneCommand(args); - } - - tracer.WriteMessage("Returning from callback for GetRunspace for computer ", - commandToRun.Runspace.ConnectionInfo.ComputerName); - } - } - - private static void HandleRunspaceStateChanged(object sender, RunspaceStateEventArgs eventArgs) - { - if (!( (eventArgs.RunspaceStateInfo.State == RunspaceState.Opened) || - (eventArgs.RunspaceStateInfo.State == RunspaceState.Disconnected))) - return; - - Runspace borrowedRunspace = sender as Runspace; - RunCommandsArguments args; - - Dbg.Assert(borrowedRunspace != null, "Sender needs to be Runspace object"); - ArgsTableForRunspaces.TryGetValue(borrowedRunspace.InstanceId, out args); - - if (args == null) - return; - - System.Management.Automation.PowerShell commandToRun = args.ImplementationContext.PowerShellInstance; - PSWorkflowHost workflowHost = args.PSActivityContext.WorkflowHost; - - if (eventArgs.RunspaceStateInfo.State == RunspaceState.Opened && - commandToRun.InvocationStateInfo.State == PSInvocationState.Disconnected) - { - commandToRun.ConnectAsync(); - return; - } - - if (eventArgs.RunspaceStateInfo.State == RunspaceState.Disconnected) - { - // the connection manager can disconnect a runspace on purpose, if that - // is not the case then it is a case of network disconnect. In this case - // we force a failure - if (!workflowHost.RemoteRunspaceProvider.IsDisconnectedByRunspaceProvider(borrowedRunspace)) - { - ArgsTableForRunspaces.TryRemove(borrowedRunspace.InstanceId, out args); - RuntimeException exception = - new RuntimeException( - String.Format(CultureInfo.CurrentCulture, - Resources.ActivityFailedDueToRunspaceDisconnect, - borrowedRunspace.ConnectionInfo.ComputerName), - eventArgs.RunspaceStateInfo.Reason); - RunspaceDisconnectedCallback(args, exception); - } - } - } - - /// - /// Sets the $pwd variable in the current runspace - /// - /// - /// - private static void SetCurrentDirectory(PSActivityContext psActivityContext, Runspace runspace) - { - if (psActivityContext.ParameterDefaults != null) - { - if (psActivityContext.ParameterDefaults.ContainsKey(Constants.PSCurrentDirectory)) - { - string path = psActivityContext.ParameterDefaults[Constants.PSCurrentDirectory] as string; - - if (path != null) - { - runspace.SessionStateProxy.Path.SetLocation(path); - } - } - } - } - - - /// - /// Callback from local runspace provider - /// - /// This callback happens in the workflow - /// thread or a threadpool callback servicing thread. - /// However there is only one thread for servicing all callbacks and - /// so all operations have to be small - private static void LocalRunspaceProviderCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState not returned correctly by LocalRunspaceProvider"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - PSWorkflowHost workflowHost = args.WorkflowHost; - PSActivityContext psActivityContext = args.PSActivityContext; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for LocalRunspaceProvider"); - Runspace runspace = null; - try - { - runspace = workflowHost.LocalRunspaceProvider.EndGetRunspace(asyncResult); - - if (runspace.ConnectionInfo == null) - { - if (psActivityContext.UserVariables.Count != 0) - { - foreach (KeyValuePair entry in psActivityContext.UserVariables) - { - runspace.SessionStateProxy.SetVariable(entry.Key, entry.Value); - } - } - - SetCurrentDirectory(psActivityContext, runspace); - } - } - catch (Exception exception) - { - - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(exception); - } - - // when runspace connection cannot be obtained we will not retry - RunOneCommandFinally(args, false); - // when runspace cannot be obtained we cannot execute the command - // simply decrement count and return - DecrementRunningCountAndCheckForEnd(psActivityContext); - return; - } - - // runspace should be in an opened state when connection manager - // assigns the same - tracer.WriteMessage("Local Runspace successfully obtained with guid ", runspace.InstanceId.ToString()); - - // Before the runspace is assigned, the activity could have been cancelled, therefore - // we need to check and release before return. - if (psActivityContext.IsCanceled) - { - CloseRunspace(runspace, CommandRunInProc, workflowHost, psActivityContext); - if (CheckForCancel(psActivityContext)) return; - } - - commandToRun.Runspace = runspace; - - // only after the runspace is set to commandToRun is the - // activity completely created. Raise the ActivityCreated - // event now - OnActivityCreated(args.ActivityObject, new ActivityCreatedEventArgs(commandToRun)); - - if (args.CommandExecutionType == CimCommandRunInProc) - { - CimActivityImplementationContext cimImplContext = - implementationContext as CimActivityImplementationContext; - Dbg.Assert(cimImplContext != null, - "If CommandExecutionType is CimCommand, then implementationContext must be of type CimActivityImplementationContext"); - - // Configure the runspace by executing the module definition scriptblock... - Dbg.Assert(cimImplContext.ModuleScriptBlock != null, "A Generated CIM activity should never have a null module scriptblock"); - runspace.SessionStateProxy.InvokeCommand.InvokeScript(false, cimImplContext.ModuleScriptBlock, null); - - // Get the CIM session if needed... - if (cimImplContext.Session == null && !string.IsNullOrEmpty(cimImplContext.ComputerName) && - !string.Equals(cimImplContext.ComputerName, "localhost", - StringComparison.OrdinalIgnoreCase)) - { - bool useSsl = false; - if (cimImplContext.PSUseSsl.HasValue) - { - useSsl = cimImplContext.PSUseSsl.Value; - } - - uint port = 0; - if (cimImplContext.PSPort.HasValue) - { - port = cimImplContext.PSPort.Value; - } - - - AuthenticationMechanism authenticationMechanism = AuthenticationMechanism.Default; - if (cimImplContext.PSAuthentication.HasValue) - { - authenticationMechanism = cimImplContext.PSAuthentication.Value; - } - - cimImplContext.Session = - CimConnectionManager.GetGlobalCimConnectionManager().GetSession(cimImplContext.ComputerName, - cimImplContext.PSCredential, - cimImplContext.PSCertificateThumbprint, - authenticationMechanism, - cimImplContext.SessionOptions, - useSsl, - port, - cimImplContext.PSSessionOption); - - if (cimImplContext.Session == null) - { - throw new InvalidOperationException(); - } - commandToRun.AddParameter("CimSession", cimImplContext.Session); - } - } - - BeginExecuteOneCommand(args); - - tracer.WriteMessage("Returning from callback for GetRunspace for LocalRunspaceProvider"); - } - } - - private static void ActivityHostManagerCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState not returned correctly by activity host manager"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - - PSWorkflowHost workflowHost = args.WorkflowHost; - PSActivityContext psActivityContext = args.PSActivityContext; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for Executing command out of proc"); - bool attemptRetry = false; - try - { - ((PSOutOfProcessActivityController)workflowHost.PSActivityHostController).EndInvokePowerShell(asyncResult); - } - catch (Exception e) - { - attemptRetry = HandleRunOneCommandException(args, e); - if (attemptRetry) - BeginActionRetry(args); - } - finally - { - RemoveHandlersFromStreams(args.ImplementationContext.PowerShellInstance, args); - RunOneCommandFinally(args, attemptRetry); - tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity: Finished running command.")); - DecrementRunningCountAndCheckForEnd(psActivityContext); - } - } - } - - private static void DecrementRunningCountAndCheckForEnd(PSActivityContext psActivityContext) - { - Interlocked.Decrement(ref psActivityContext.CommandsRunningCount); - - if (psActivityContext.CommandsRunningCount != 0) return; - - RaiseTerminalCallback(psActivityContext); - } - - private static void RaiseTerminalCallback(PSActivityContext psActivityContext) - { - lock (psActivityContext.SyncRoot) - { - if (psActivityContext.AllCommandsStarted || psActivityContext.commandQueue.Count == 0) - { - // This signals all commands are done - // time to start finish routines - Dbg.Assert(psActivityContext.Callback != null, "A callback should have been assigned in the context"); - ThreadPool.QueueUserWorkItem(psActivityContext.Callback, psActivityContext.AsyncState); - } - } - } - - private static bool CheckForCancel(PSActivityContext psActivityContext) - { - bool canceled = psActivityContext.IsCanceled; - - if (canceled) - RaiseTerminalCallback(psActivityContext); - - return canceled; - } - - private static void RunDirectExecutionActivity(System.Management.Automation.PowerShell commandToRun, PSDataCollection input, - PSDataCollection output, PSActivityContext psActivityContext, ActivityImplementationContext implementationContext) - { - Command command = commandToRun.Commands.Commands[0]; - var cmdName = command.CommandText; - - Cmdlet cmdlet = null; - bool takesInput = false; - if (string.Equals(cmdName, "Get-WMIObject", StringComparison.OrdinalIgnoreCase)) - { - cmdlet = new Microsoft.PowerShell.Commands.GetWmiObjectCommand(); - } - else if (string.Equals(cmdName, "Invoke-WMIMethod", StringComparison.OrdinalIgnoreCase)) - { - cmdlet = new Microsoft.PowerShell.Commands.InvokeWmiMethod(); - takesInput = true; - } - - if (CheckForCancel(psActivityContext)) return; - - // Set up the command runtime instance... - DirectExecutionActivitiesCommandRuntime cmdRuntime = new DirectExecutionActivitiesCommandRuntime(output != null ? output : new PSDataCollection(), - implementationContext, cmdlet != null ? cmdlet.GetType() : psActivityContext.TypeImplementingCmdlet); - cmdRuntime.Error = commandToRun.Streams.Error; - cmdRuntime.Warning = commandToRun.Streams.Warning; - cmdRuntime.Progress = commandToRun.Streams.Progress; - cmdRuntime.Verbose = commandToRun.Streams.Verbose; - cmdRuntime.Debug = commandToRun.Streams.Debug; - cmdRuntime.Information = commandToRun.Streams.Information; - - // If the cmdlet takes input and there is or may be some input, then - // iterate processing the input - - if (cmdlet != null) - { - cmdlet.CommandRuntime = cmdRuntime; - - // Copy the parameters from the PowerShell object to the cmdlet object - PSObject wrappedCmdlet = PSObject.AsPSObject(cmdlet); - InitializeCmdletInstanceParameters(command, wrappedCmdlet, false, psActivityContext, null, implementationContext); - - if (takesInput && input != null && (input.Count > 0 || input.IsOpen)) - { - Microsoft.PowerShell.Commands.InvokeWmiMethod iwm = cmdlet as Microsoft.PowerShell.Commands.InvokeWmiMethod; - foreach (PSObject inputObject in input) - { - try - { - var managementObject = LanguagePrimitives.ConvertTo(inputObject); - - iwm.InputObject = managementObject; - iwm.Invoke().GetEnumerator().MoveNext(); - } - catch (PSInvalidCastException psice) - { - if (psice.ErrorRecord != null) - { - cmdRuntime.Error.Add(psice.ErrorRecord); - } - } - - if (CheckForCancel(psActivityContext)) return; - } - } - else - { - cmdlet.Invoke().GetEnumerator().MoveNext(); - } - } - else - { - // See if any session options were passed.. - CimActivityImplementationContext cimActivityImplementationContext = implementationContext as CimActivityImplementationContext; - CimSessionOptions cimSessionOptionsToUse = cimActivityImplementationContext != null ? cimActivityImplementationContext.SessionOptions : null; - - if (psActivityContext.TypeImplementingCmdlet == null) - { - throw new InvalidOperationException(cmdName); - } - - - if (input != null && (input.Count > 0 || input.IsOpen)) - { - // Only deals with InputObject property for input - if (psActivityContext.TypeImplementingCmdlet.GetProperty("InputObject") == null) - { - // Throw if the cmdlet does not implement a InputObject property to bind to.... - throw new NotImplementedException(String.Format(CultureInfo.CurrentCulture, - Resources.CmdletDoesNotImplementInputObjectProperty, - cmdName)); - } - - foreach (PSObject inputObject in input) - { - try - { - using (var cimCmdlet = (Microsoft.Management.Infrastructure.CimCmdlets.CimBaseCommand)Activator.CreateInstance(psActivityContext.TypeImplementingCmdlet)) - { - cimCmdlet.CommandRuntime = cmdRuntime; - var cimInstance = LanguagePrimitives.ConvertTo(inputObject); - PSObject wrapper = PSObject.AsPSObject(cimCmdlet); - InitializeCmdletInstanceParameters(command, wrapper, true, psActivityContext, cimSessionOptionsToUse, implementationContext); - var prop = wrapper.Properties["InputObject"]; - prop.Value = cimInstance; - cimCmdlet.Invoke().GetEnumerator().MoveNext(); - } - } - catch (PSInvalidCastException psice) - { - if (psice.ErrorRecord != null) - { - cmdRuntime.Error.Add(psice.ErrorRecord); - } - } - - if (CheckForCancel(psActivityContext)) return; - } - } - else - { - using (var cimCmdlet = (Microsoft.Management.Infrastructure.CimCmdlets.CimBaseCommand)Activator.CreateInstance(psActivityContext.TypeImplementingCmdlet)) - { - cimCmdlet.CommandRuntime = cmdRuntime; - PSObject wrapper = PSObject.AsPSObject(cimCmdlet); - InitializeCmdletInstanceParameters(command, wrapper, true, psActivityContext, cimSessionOptionsToUse, implementationContext); - cimCmdlet.Invoke().GetEnumerator().MoveNext(); - } - } - } - } - - private static void InitializeCmdletInstanceParameters(Command command, PSObject wrappedCmdlet, bool isGenericCim, - PSActivityContext psActivityContext, CimSessionOptions cimSessionOptions, ActivityImplementationContext implementationContext) - { - - bool sessionSet = false; - - foreach (CommandParameter p in command.Parameters) - { - // Note - skipping null common parameters avoids a null pointer exception. - // Also need to replicate parameter set validation for the WMI activities at this point - // since we aren't going through the parameter binder - if (Cmdlet.CommonParameters.Contains(p.Name)) - { - continue; - } - - // If a session was explicitly passed, use it instead of an ambient session for the current computer. - if (p.Name.Equals("CimSession")) - { - sessionSet = true; - } - - if (wrappedCmdlet.Properties[p.Name] != null) - { - wrappedCmdlet.Properties[p.Name].Value = p.Value; - } - else - { - wrappedCmdlet.Properties.Add(new PSNoteProperty(p.Name, p.Value)); - } - } - - string[] computerNameArray = null; - CimActivityImplementationContext cimActivityImplementationContext = - implementationContext as CimActivityImplementationContext; - - // Set the target computer for this command... - if (cimActivityImplementationContext != null && !String.IsNullOrEmpty(cimActivityImplementationContext.ComputerName)) - { - computerNameArray = new string[] { cimActivityImplementationContext.ComputerName }; - } - else if(psActivityContext.ParameterDefaults.ContainsKey("PSComputerName")) - { - computerNameArray = psActivityContext.ParameterDefaults["PSComputerName"] as string[]; - } - - if (computerNameArray != null && computerNameArray.Length > 0) - { - // Not all of the generic CIM cmdlets take a session (e.g. New-CimSession) - if (isGenericCim && wrappedCmdlet.Properties["CimSession"] != null) - { - if (!sessionSet) - { - if (cimActivityImplementationContext == null) - throw new ArgumentException(Resources.InvalidImplementationContext); - - bool useSsl = false; - if (cimActivityImplementationContext.PSUseSsl.HasValue) - { - useSsl = cimActivityImplementationContext.PSUseSsl.Value; - } - - uint port = 0; - if (cimActivityImplementationContext.PSPort.HasValue) - { - port = cimActivityImplementationContext.PSPort.Value; - } - - AuthenticationMechanism authenticationMechanism = AuthenticationMechanism.Default; - if (cimActivityImplementationContext.PSAuthentication.HasValue) - { - authenticationMechanism = cimActivityImplementationContext.PSAuthentication.Value; - } - - // Convert the computer names to session objects. - List cimSessions = computerNameArray - .ToList() - .ConvertAll( - (string computer) => CimConnectionManager.GetGlobalCimConnectionManager().GetSession(computer, - cimActivityImplementationContext.PSCredential, - cimActivityImplementationContext.PSCertificateThumbprint, - authenticationMechanism, - cimSessionOptions, - useSsl, - port, - cimActivityImplementationContext.PSSessionOption)); - wrappedCmdlet.Properties["CimSession"].Value = cimSessions.ToArray(); - - if (computerNameArray.Length > 1) - { - Dbg.Assert(false, "Something in this fix is not right, have a look"); - } - cimActivityImplementationContext.Session = cimSessions[0]; - } - } - else if (wrappedCmdlet.Properties["ComputerName"] == null) - { - // If the cmdlet takes ComputerName and it wasn't explicitly set already, - // then set it to the ambient PSComputerName we got from the context... - wrappedCmdlet.Properties.Add(new PSNoteProperty("ComputerName", computerNameArray)); - } - } - } - - // Script to initialize the runspace variables... - private const string RunspaceInitScript = @" - Get-Variable -Exclude input | Remove-Variable -ErrorAction Ignore; $input | Foreach-Object {$nvp=$_}; foreach($k in $nvp.keys){set-variable -name $k -value $nvp[$k]} - "; - internal static readonly InitialSessionState Iss = InitialSessionState.CreateDefault(); - - private static void SetVariablesInRunspaceUsingProxy(PSActivityEnvironment activityEnvironment, Runspace runspace) - { - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - actionTracer.WriteMessage("BEGIN SetVariablesInRunspaceUsingProxy"); - Dictionary nameValuePairs = GetVariablesToSetInRunspace(activityEnvironment); - // Copy in the workflow variables - foreach (string name in nameValuePairs.Keys) - { - object value = nameValuePairs[name]; - if (value != null) - { - try - { - var psvar = runspace.SessionStateProxy.PSVariable.Get(name); - if (psvar == null || (psvar.Options & ScopedItemOptions.ReadOnly) == 0) - { - // don't try to overwrite read-only variables values - runspace.SessionStateProxy.PSVariable.Set(name, value); - } - } - catch (PSNotSupportedException) - { - actionTracer.WriteMessage("SetVariablesInRunspaceUsingProxy: Copying the workflow variables to a RemoteSessionStateProxy is not supported."); - return; - } - } - } - actionTracer.WriteMessage("END SetVariablesInRunspaceUsingProxy"); - } - } - - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This function runs in either a WinRM thread or in the - /// connection manager servicing thread. Therefore any - /// operations that this thread initiates is supposed to - /// be very small. Make sure that this contract is maintained - /// when any changes are made to the function - /// - private static void BeginSetVariablesInRemoteRunspace(RunCommandsArguments args) - { - Runspace runspace = args.ImplementationContext.PowerShellInstance.Runspace; - PSActivityEnvironment activityEnvironment = args.ImplementationContext.PSActivityEnvironment; - - Dbg.Assert(runspace.ConnectionInfo != null, - "BeginSetVariablesInRemoteRunspace can only be called for remote runspaces"); - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - actionTracer.WriteMessage("BEGIN BeginSetVariablesInRemoteRunspace"); - - System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create(); - ps.Runspace = runspace; - ps.AddScript(RunspaceInitScript); - - Dictionary nameValuePairs = GetVariablesToSetInRunspace(activityEnvironment); - - PSDataCollection vars = new PSDataCollection {nameValuePairs}; - vars.Complete(); - args.HelperCommand = ps; - args.HelperCommandInput = vars; - - BeginInvokeOnPowershellCommand(ps, vars, null, SetVariablesCallback, args); - - actionTracer.WriteMessage("END BeginSetVariablesInRemoteRunspace"); - } - } - - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This function either runs on a thread pool thread, or in the - /// remote case runs on a WinRM/CM servicing thread. Therefore operations - /// need to be light so that the thread can be released back quickly - /// When changes are made this assumption need to be validated - private static void BeginRunspaceInitializeSetup(RunCommandsArguments args) - { - PSActivityEnvironment activityEnvironment= args.ImplementationContext.PSActivityEnvironment; - Runspace runspace = args.ImplementationContext.PowerShellInstance.Runspace; - string[] requiredModules = args.ActivityParameters.PSRequiredModules; - PSActivityContext psActivityContext = args.PSActivityContext; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - actionTracer.WriteMessage("BEGIN BeginRunspaceInitializeSetup"); - if (args.CommandExecutionType != CimCommandRunInProc) - { - if (runspace.ConnectionInfo != null) - { - // for a remote runspace do an async invocation - BeginSetVariablesInRemoteRunspace(args); - return; - } - - // for a local runspace set the variables using proxy - // on the same thread - try - { - SetVariablesInRunspaceUsingProxy(activityEnvironment, runspace); - } - catch (Exception e) - { - bool attemptRetry = HandleRunOneCommandException(args, e); - - if (attemptRetry) - { - actionTracer.WriteMessage("Setting variables for command failed, attempting retry"); - // before attempting a retry we will have to ditch this runspace - // and use a new one - CloseRunspace(args.ImplementationContext.PowerShellInstance.Runspace, - args.CommandExecutionType, - args.WorkflowHost, psActivityContext); - BeginActionRetry(args); - } - else - { - // if we are not attempting a retry we just need to - // return - actionTracer.WriteMessage("Setting variables for command failed, returning"); - RunOneCommandFinally(args, false); - } - DecrementRunningCountAndCheckForEnd(psActivityContext); - return; - } - } - - if (requiredModules.Length > 0) - { - BeginImportRequiredModules(args); - } - else - { - // else we are good to call command invocation this point - // since this function is guaranteed to run on a threadpool - // thread BeginPowerShellInvocation can be directly called here - BeginPowerShellInvocation(args); - } - - actionTracer.WriteMessage("END BeginRunspaceInitializeSetup"); - } - } - - private static void BeginImportRequiredModules(RunCommandsArguments args) - { - Runspace runspace = args.ImplementationContext.PowerShellInstance.Runspace; - PSActivityEnvironment activityEnvironment = args.ImplementationContext.PSActivityEnvironment; - System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create(); - if (activityEnvironment != null) - { - Dbg.Assert(activityEnvironment.Modules.Count > 0, - "When PSActivityEnvironment is specified and modules are imported, PSActivityEnvironment.Modules need to be populated"); - // Setting erroraction to stop for import-module since they are required modules. If not present, stop the execution - ps.AddCommand("Import-Module").AddParameter("Name", activityEnvironment.Modules).AddParameter( - "ErrorAction", ActionPreference.Stop); - } - else - { - // Setting erroraction to stop for import-module since they are required modules. If not present, stop the execution - ps.AddCommand("Import-Module").AddParameter("Name", args.ActivityParameters.PSRequiredModules).AddParameter( - "ErrorAction", ActionPreference.Stop); - } - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - // we need to import required modules only if any are specified - tracer.WriteMessage("Importing modules in runspace ", runspace.InstanceId); - - ps.Runspace = runspace; - args.HelperCommand = ps; - - BeginInvokeOnPowershellCommand(ps, null, null, ImportRequiredModulesCallback, args); - } - } - - private static void BeginInvokeOnPowershellCommand(System.Management.Automation.PowerShell ps, PSDataCollection varsInput, PSInvocationSettings settings, AsyncCallback callback, RunCommandsArguments args) - { - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - try - { - ps.BeginInvoke(varsInput, settings, callback, args); - } - catch (Exception e) - { - bool attemptRetry = ProcessException(args, e); - - if (attemptRetry) - { - BeginActionRetry(args); - } - else - { - ReleaseResourcesAndCheckForEnd(ps, args, false, false); - } - - tracer.TraceException(e); - } - } - } - - private static void ReleaseResourcesAndCheckForEnd(System.Management.Automation.PowerShell ps, RunCommandsArguments args, bool removeHandlersFromStreams, bool attemptRetry) - { - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - PSActivityContext psActivityContext = args.PSActivityContext; - - if (removeHandlersFromStreams) - { - RemoveHandlersFromStreams(ps, args); - } - - RunOneCommandFinally(args, attemptRetry); - - tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity: Finished running command.")); - - var remotingActivity = args.ActivityObject as PSRemotingActivity; - if ((remotingActivity != null) && - (args.PSActivityContext.HostExtension != null) && - (args.PSActivityContext.HostExtension.RemoteActivityState != null) && - (args.ImplementationContext != null)) - { - // remote activity task execution has finished - // change the task's state to Completed in RemoteActivityState - args.PSActivityContext.HostExtension.RemoteActivityState.SetRemoteActivityRunspaceEntry( - remotingActivity.Id, - args.ImplementationContext.Id, - "completed", null); - } - - DecrementRunningCountAndCheckForEnd(psActivityContext); - } - } - - private static bool ProcessException(RunCommandsArguments args, Exception e) - { - bool attemptRetry = false; - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - - if ((args.ActivityParameters.ConnectionRetryCount.HasValue || - args.ActivityParameters.ConnectionRetryInterval.HasValue) && e.InnerException != null && - e.InnerException is IContainsErrorRecord) - { - IContainsErrorRecord er = e as IContainsErrorRecord; - - // CIM specific case - if (er.ErrorRecord.FullyQualifiedErrorId.StartsWith("CimJob_BrokenCimSession", StringComparison.OrdinalIgnoreCase)) - { - int attempts = Int32.MaxValue; - - if (!psActivityContext.IsCanceled) - { - if (psActivityContext.runningCommands.ContainsKey(commandToRun)) - attempts = psActivityContext.runningCommands[commandToRun].ActionAttempts; - - attemptRetry = HandleFailure(attempts, args.ActivityParameters.ConnectionRetryCount, - args.ActivityParameters.ConnectionRetryInterval, implementationContext, "ActivityActionFailed", - null, psActivityContext); - } - - if(!attemptRetry) - { - ErrorRecord errorRecord = er.ErrorRecord; - String computerName; - Guid jobInstanceId; - if (GetComputerNameAndJobIdForCommand(commandToRun.InstanceId, out computerName, out jobInstanceId)) - { - AddIdentifierInfoToErrorRecord(errorRecord, computerName, jobInstanceId); - } - - // If this was a multi-machine activity, this is an error, not an exception. - if ((implementationContext.PSComputerName != null) && (implementationContext.PSComputerName.Length > 1)) - { - WriteError(e, "ActivityActionFailed", ErrorCategory.InvalidResult, - implementationContext.PowerShellInstance.Runspace.ConnectionInfo, psActivityContext); - } - else - { - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(e); - } - } - } - - return attemptRetry; - } - } - - IContainsErrorRecord containsErrorRecord = e as IContainsErrorRecord; - if (containsErrorRecord != null) - { - ErrorRecord errorRecord = containsErrorRecord.ErrorRecord; - String computerName; - Guid jobInstanceId; - if (GetComputerNameAndJobIdForCommand(commandToRun.InstanceId, out computerName, out jobInstanceId)) - { - AddIdentifierInfoToErrorRecord(errorRecord, computerName, jobInstanceId); - } - } - attemptRetry = HandleRunOneCommandException(args, e); - - return attemptRetry; - } - - private static Dictionary GetVariablesToSetInRunspace(PSActivityEnvironment activityEnvironment) - { - // first set the predefined variables to reset the runspace before - // setting the activity specified ones - Dictionary nameValuePairs = Iss.Variables.ToDictionary(entry => entry.Name, entry => entry.Value); - - if (activityEnvironment != null && activityEnvironment.Variables != null) - { - foreach (string name in activityEnvironment.Variables.Keys) - { - object value = activityEnvironment.Variables[name]; - if (value == null) continue; - if (nameValuePairs.ContainsKey(name)) - { - nameValuePairs[name] = value; - } - else - { - nameValuePairs.Add(name, value); - } - } - } - - // workaround: PSPR does not support rehydration of System.Text.ASCIIEncoding - // therefore we need to remove this otherwise FS RI is blocked - per their dev mgr - // once the DCR is implemented on Engines side, we should remove - if (nameValuePairs.ContainsKey("OutputEncoding")) - { - nameValuePairs.Remove("OutputEncoding"); - } - - return nameValuePairs; - } - - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This callback runs in a WinRM thread - in the normal - /// course of operation i.e unless PowerShell.EndInvoke() throws - /// an exception, the operations performed on this thread - /// should be minimal - private static void SetVariablesCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState was not set correctly by SetVariablesInActivityHost() method"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - System.Management.Automation.PowerShell powerShell = args.HelperCommand; - Dbg.Assert(powerShell != null, "Caller did not pass PowerShell instance correctly"); - - PSActivityContext psActivityContext = args.PSActivityContext; - PSActivityEnvironment activityEnvironment = args.ImplementationContext.PSActivityEnvironment; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for setting variables in remote runspace"); - try - { - powerShell.EndInvoke(asyncResult); - } - catch (Exception) - { - // Exception setting variables, try using proxy - tracer.WriteMessage("Setting variables in remote runspace failed using script, trying with proxy"); - - try - { - SetVariablesInRunspaceUsingProxy(activityEnvironment, powerShell.Runspace); - } - catch (Exception ex) - { - bool attemptRetry = HandleRunOneCommandException(args, ex); - if (attemptRetry) - { - tracer.WriteMessage("Runspace initialization failed, attempting retry"); - // Since the error is in runspace initialization, we need to discard this - // runspace and try with a new one - CloseRunspace(args.ImplementationContext.PowerShellInstance.Runspace, - args.CommandExecutionType, - args.WorkflowHost, psActivityContext); - BeginActionRetry(args); - } - else - { - RunOneCommandFinally(args, false); - } - DecrementRunningCountAndCheckForEnd(psActivityContext); - return; - } - } - finally - { - powerShell.Dispose(); - args.HelperCommand = null; - args.HelperCommandInput.Dispose(); - args.HelperCommandInput = null; - } - - if (CheckForCancel(psActivityContext)) return; - - if ((activityEnvironment != null && activityEnvironment.Modules != null && activityEnvironment.Modules.Count > 0) || - (args.ActivityParameters != null && args.ActivityParameters.PSRequiredModules != null && args.ActivityParameters.PSRequiredModules.Length > 0)) - { - // if there are modules to import, begin async invocation - // for importing modules - BeginImportRequiredModules(args); - } - else - { - // else we are good to call command invocation this point - // since this function is guaranteed to run on a threadpool - // thread BeginPowerShellInvocation can be directly called here - BeginPowerShellInvocation(args); - } - } - } - - private static void ImportRequiredModulesCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState not returned correctly by activity host manager"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - System.Management.Automation.PowerShell powerShell = args.HelperCommand; - Dbg.Assert(powerShell != null, "Caller did not pass PowerShell instance correctly"); - - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityParameters activityParameters = args.ActivityParameters; - Type activityType = args.ActivityType; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for importing required modules"); - try - { - powerShell.EndInvoke(asyncResult); - } - catch (Exception e) - { - string psRequiredModuleNames = ""; - foreach (string moduleName in activityParameters.PSRequiredModules) - { - psRequiredModuleNames += moduleName + ", "; - } - psRequiredModuleNames = psRequiredModuleNames.TrimEnd(',',' '); - - string msg = string.Format(CultureInfo.InvariantCulture, - Resources.DependModuleImportFailed, - psRequiredModuleNames, - activityType.Name); - Exception ex = new Exception(msg, e); - tracer.TraceException(ex); - - bool attemptRetry = HandleRunOneCommandException(args, ex); - if (attemptRetry) - { - tracer.WriteMessage("Runspace initialization failed, attempting retry"); - // Since the error is in runspace initialization, we need to discard this - // runspace and try with a new one - CloseRunspace(args.ImplementationContext.PowerShellInstance.Runspace, args.CommandExecutionType, - args.WorkflowHost, psActivityContext); - BeginActionRetry(args); - } - else - { - RunOneCommandFinally(args, false); - } - DecrementRunningCountAndCheckForEnd(psActivityContext); - return; - } - finally - { - powerShell.Dispose(); - args.HelperCommand = null; - } - - if (CheckForCancel(psActivityContext)) return; - - // at this point the initialization is done and we need to execute - // the command - BeginPowerShellInvocation(args); - } - } - - private static void InitializeRunspaceAndExecuteCommandWorker(object state) - { - Dbg.Assert(state != null, "State not passed correctly to worker"); - RunCommandsArguments args = state as RunCommandsArguments; - Dbg.Assert(args != null, "RunCommandsArguments not passed correctly"); - - System.Management.Automation.PowerShell commandToRun = args.ImplementationContext.PowerShellInstance; - PSActivityContext psActivityContext = args.PSActivityContext; - - // Invoke the command. If we want to return the output, then call the overload - // that streams output as well. - try - { - if (CheckForCancel(psActivityContext)) return; - - // we need to set the variables in the runspace before - // invoking the command - BeginRunspaceInitializeSetup(args); - - } - catch (PipelineStoppedException) - { - } - } - - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This function is designed to be lightweight. It always runs - /// on a callback thread - which is a threadpool thread - private static void BeginPowerShellInvocation(object state) - { - Dbg.Assert(state != null, "State not passed correctly to BeginPowerShellInvocation"); - RunCommandsArguments args = state as RunCommandsArguments; - Dbg.Assert(args != null, "Args not passed correctly to BeginPowerShellInvocation"); - - ActivityImplementationContext implementationContext = args.ImplementationContext; - PSDataCollection output = args.Output; - PSDataCollection input = args.Input; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - PSWorkflowHost workflowHost = args.WorkflowHost; - PSActivityContext psActivityContext = args.PSActivityContext; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - PSDataCollection outputForInvoke = output ?? - new PSDataCollection(); - bool exceptionOccurred = false; - - try - { - if (CheckForCancel(psActivityContext)) return; - - AddHandlersToStreams(commandToRun, args); - - if (CheckForCancel(psActivityContext)) return; - - commandToRun.BeginInvoke(input, outputForInvoke, null, - PowerShellInvocationCallback, - args); - } - catch (Exception e) - { - exceptionOccurred = true; - - bool attemptRetry = ProcessException(args, e); - - if (attemptRetry) - { - BeginActionRetry(args); - } - else - { - ReleaseResourcesAndCheckForEnd(commandToRun, args, true, false); - } - - tracer.TraceException(e); - } - - tracer.WriteMessage("Completed BeginInvoke call on PowerShell"); - - if (exceptionOccurred == false && - args.CommandExecutionType == CommandRunRemotely) - { - if ((args.ImplementationContext != null) && (args.ImplementationContext.EnableRemotingActivityAutoResume)) - { - var remotingActivity = args.ActivityObject as PSRemotingActivity; - if ((remotingActivity != null) && - (args.PSActivityContext.HostExtension != null) && - (args.PSActivityContext.HostExtension.RemoteActivityState != null)) - { - // Command invocation has started, save the tasks's runspace instance id to RemoteActivityState - args.PSActivityContext.HostExtension.RemoteActivityState.SetRemoteActivityRunspaceEntry( - remotingActivity.Id, - args.ImplementationContext.Id, - commandToRun.Runspace.InstanceId, null); - } - } - - // if the runspace was obtained from the connection manager - // then it need to be signaled as ready for disconnect/reconnect - workflowHost.RemoteRunspaceProvider.ReadyForDisconnect(commandToRun.Runspace); - } - } - } - - private static readonly ConcurrentDictionary ArgsTableForRunspaces - = new ConcurrentDictionary(); - - #region Object Decoration - - internal static void AddHandlersToStreams(System.Management.Automation.PowerShell commandToRun, RunCommandsArguments args) - { - if (commandToRun == null || args == null) - return; - - bool hasErrorMerged = args.PSActivityContext.MergeErrorToOutput; - - if (hasErrorMerged) - { - commandToRun.Streams.Error.DataAdded += HandleErrorDataAdded; - } - - if (args.PSActivityContext.Output != null) - args.PSActivityContext.Output.DataAdding += HandleOutputDataAdding; - commandToRun.Streams.Error.DataAdding += HandleErrorDataAdding; - commandToRun.Streams.Progress.DataAdding += HandleProgressDataAdding; - commandToRun.Streams.Verbose.DataAdding += HandleInformationalRecordDataAdding; - commandToRun.Streams.Warning.DataAdding += HandleInformationalRecordDataAdding; - commandToRun.Streams.Debug.DataAdding += HandleInformationalRecordDataAdding; - commandToRun.Streams.Information.DataAdding += HandleInformationDataAdding; - - ArgsTable.TryAdd(commandToRun.InstanceId, args); - } - - private static void HandleInformationalRecordDataAdding(object sender, DataAddingEventArgs e) - { - InformationalRecord informationalRecord = (InformationalRecord) e.ItemAdded; - if (informationalRecord == null) return; - - string computerName; - Guid jobInstanceId; - if (GetComputerNameAndJobIdForCommand(e.PowerShellInstanceId, out computerName, out jobInstanceId)) - { - informationalRecord.Message = - AddIdentifierInfoToString(jobInstanceId, computerName, informationalRecord.Message); - } - } - - private static void HandleProgressDataAdding(object sender, DataAddingEventArgs e) - { - ProgressRecord progressRecord = (ProgressRecord)e.ItemAdded; - if (progressRecord == null) return; - - string computerName; - Guid jobInstanceId; - - if (GetComputerNameAndJobIdForCommand(e.PowerShellInstanceId, out computerName, out jobInstanceId)) - { - progressRecord.CurrentOperation = AddIdentifierInfoToString(jobInstanceId, computerName, - progressRecord.CurrentOperation); - } - } - - private static void HandleInformationDataAdding(object sender, DataAddingEventArgs e) - { - InformationRecord informationRecord = (InformationRecord)e.ItemAdded; - if (informationRecord == null) return; - - string computerName; - Guid jobInstanceId; - - if (GetComputerNameAndJobIdForCommand(e.PowerShellInstanceId, out computerName, out jobInstanceId)) - { - informationRecord.Source = AddIdentifierInfoToString(jobInstanceId, computerName, - informationRecord.Source); - } - } - - internal static void AddIdentifierInfoToOutput(PSObject psObject, Guid jobInstanceId, string computerName) - { - Dbg.Assert(psObject != null, "PSObject not passed correctly"); - - if (psObject.Properties["PSComputerName"] != null) - { - // if it is a NoteProperty then try changing the same - PSNoteProperty noteProperty = psObject.Properties["PSComputerName"] as PSNoteProperty; - if (noteProperty != null) - { - try - { - noteProperty.Value = computerName; - } - catch(SetValueException) - { - // this is a best attempt so fine to - // eat exception if PSComputerName is - // defined in type data - } - } - } - else - psObject.Properties.Add(new PSNoteProperty("PSComputerName", computerName)); - - if (psObject.Properties["PSShowComputerName"] != null) - psObject.Properties.Remove("PSShowComputerName"); - psObject.Properties.Add(new PSNoteProperty("PSShowComputerName", true)); - - if (psObject.Properties["PSSourceJobInstanceId"] != null) - psObject.Properties.Remove("PSSourceJobInstanceId"); - psObject.Properties.Add(new PSNoteProperty("PSSourceJobInstanceId", jobInstanceId)); - } - - private static void HandleOutputDataAdding(object sender, DataAddingEventArgs e) - { - PSObject psObject = (PSObject)e.ItemAdded; - if (psObject == null) return; - - string computerName; - Guid jobInstanceId; - - RunCommandsArguments args; - args = GetArgsForCommand(e.PowerShellInstanceId, out computerName, out jobInstanceId); - - if (args == null) - return; - - AddIdentifierInfoToOutput(psObject, jobInstanceId, computerName); - } - - private static void HandleErrorDataAdding(object sender, DataAddingEventArgs e) - { - ErrorRecord errorRecord = (ErrorRecord)e.ItemAdded; - if (errorRecord == null) return; - - string computerName; - Guid jobInstanceId; - RunCommandsArguments args = GetArgsForCommand(e.PowerShellInstanceId, out computerName, out jobInstanceId); - - if (args == null) return; - - AddIdentifierInfoToErrorRecord(errorRecord, computerName, args.PSActivityContext.JobInstanceId); - - bool hasHostExtension = args.PSActivityContext.HostExtension != null; - bool hasErrorMerged = args.PSActivityContext.MergeErrorToOutput; - - if (!hasErrorMerged && !hasHostExtension) return; - - HostSettingCommandMetadata sourceCommandMetadata = hasHostExtension - ? args.PSActivityContext.HostExtension. - HostCommandMetadata - : null; - PSDataCollection outputStream = hasErrorMerged ? args.PSActivityContext.Output : null; - - PowerShellInvocation_ErrorAdding(sender, e, sourceCommandMetadata, outputStream); - } - - /// - /// - /// - /// - /// - /// - /// false if default values were substituted - private static bool GetComputerNameAndJobIdForCommand(Guid powerShellId, out string computerName, out Guid jobInstanceId) - { - RunCommandsArguments args = GetArgsForCommand(powerShellId, out computerName, out jobInstanceId); - return args != null; - } - - private static RunCommandsArguments GetArgsForCommand(Guid powerShellId, out string computerName, out Guid jobInstanceId) - { - RunCommandsArguments args; - ArgsTable.TryGetValue(powerShellId, out args); - - computerName = LocalHost; - - jobInstanceId = args == null - ? Guid.Empty - : args.PSActivityContext.JobInstanceId; - - if (args != null ) - { - if (args.PSActivityContext.HostExtension != null && - args.PSActivityContext.HostExtension.Parameters != null && - args.PSActivityContext.HostExtension.Parameters.ContainsKey("PSComputerName")) - { - string[] computerNames = - (string[]) args.PSActivityContext.HostExtension.Parameters["PSComputerName"]; - - if (computerNames.Length == 1) - { - // this is the workflow fan-out scenario. Use the intended computer - // name in this case - computerName = computerNames[0]; - return args; - } - } - - // looks like an activity level fan-out. If this is a command - // run with a runspace, try to extract the computer name from - // the runspace - switch (args.CommandExecutionType) - { - case CommandRunOutOfProc: - case CommandRunInProc: - case CommandRunRemotely: - computerName = GetComputerNameFromCommand(args.ImplementationContext.PowerShellInstance); - break; - } - } - - if (args != null) - { - ActivityImplementationContext implementationContext = args.ImplementationContext; - - if (implementationContext != null) - { - if (implementationContext.PSRemotingBehavior == RemotingBehavior.Custom) - { - if (implementationContext.PSComputerName != null && - implementationContext.PSComputerName.Length != 0) - { - computerName = implementationContext.PSComputerName[0]; - } - } - } - } - - return args; - } - - internal static void AddIdentifierInfoToErrorRecord(ErrorRecord errorRecord, string computerName, Guid jobInstanceId) - { - RemotingErrorRecord remoteErrorRecord = errorRecord as RemotingErrorRecord; - - if (remoteErrorRecord != null) return; - - if (errorRecord.ErrorDetails == null) - { - errorRecord.ErrorDetails = new ErrorDetails(String.Empty); - errorRecord.ErrorDetails.RecommendedAction = AddIdentifierInfoToString(jobInstanceId, computerName, - errorRecord.ErrorDetails. - RecommendedAction); - } - else - { - errorRecord.ErrorDetails.RecommendedAction = - AddIdentifierInfoToString(jobInstanceId, computerName, errorRecord.ErrorDetails.RecommendedAction); - } - } - - internal static string AddIdentifierInfoToString(Guid instanceId, string computerName, string message) - { - Guid jobInstanceId; - string originalComputerName; - string originalMessage; - string messageToAppend = StringContainsIdentifierInfo(message, out jobInstanceId, out originalComputerName, - out originalMessage) - ? originalMessage - : message; - - var newMessage = new StringBuilder(instanceId.ToString()); - newMessage.Append(":["); - newMessage.Append(computerName); - newMessage.Append("]:"); - newMessage.Append(messageToAppend); - return newMessage.ToString(); - } - - private const string MessagePattern = @"^([\d\w]{8}\-[\d\w]{4}\-[\d\w]{4}\-[\d\w]{4}\-[\d\w]{12}:\[.*\]:).*"; - private static readonly char[] Delimiter = new[]{':'}; - - private static bool StringContainsIdentifierInfo(string message, out Guid jobInstanceId, out string computerName, out string originalString) - { - jobInstanceId = Guid.Empty; - computerName = string.Empty; - originalString = string.Empty; - - if (string.IsNullOrEmpty(message)) return false; - - - if (!Regex.IsMatch(message, MessagePattern)) - return false; - - String[] parts = message.Split(Delimiter, 3); - if (parts.Length != 3) return false; - - if (!Guid.TryParse(parts[0], out jobInstanceId)) - jobInstanceId = Guid.Empty; - - computerName = parts[1]; - originalString = parts[2].Trim(); - return true; - } - - private const string LocalHost = "localhost"; - private static String GetComputerNameFromCommand(System.Management.Automation.PowerShell commandToRun) - { - Runspace runspace = commandToRun.Runspace; - - return runspace.ConnectionInfo == null ? LocalHost : runspace.ConnectionInfo.ComputerName; - } - - private readonly static ConcurrentDictionary ArgsTable = - new ConcurrentDictionary(); - - private static void HandleErrorDataAdded(object sender, DataAddedEventArgs e) - { - RunCommandsArguments args; - ArgsTable.TryGetValue(e.PowerShellInstanceId, out args); - if (args == null) return; - - bool hasErrorMerged = args.PSActivityContext.MergeErrorToOutput; - - if (hasErrorMerged) - { - MergeError_DataAdded(sender, e, args.PSActivityContext.errors); - } - } - - internal static void RemoveHandlersFromStreams(System.Management.Automation.PowerShell commandToRun, RunCommandsArguments args) - { - if (commandToRun == null || args == null) - return; - - bool hasErrorMerged = args.PSActivityContext.MergeErrorToOutput; - - if (hasErrorMerged) - { - commandToRun.Streams.Error.DataAdded -= HandleErrorDataAdded; - } - - if (args.PSActivityContext.Output != null) - args.PSActivityContext.Output.DataAdding -= HandleOutputDataAdding; - commandToRun.Streams.Error.DataAdding -= HandleErrorDataAdding; - commandToRun.Streams.Progress.DataAdding -= HandleProgressDataAdding; - commandToRun.Streams.Verbose.DataAdding -= HandleInformationalRecordDataAdding; - commandToRun.Streams.Warning.DataAdding -= HandleInformationalRecordDataAdding; - commandToRun.Streams.Debug.DataAdding -= HandleInformationalRecordDataAdding; - commandToRun.Streams.Information.DataAdding -= HandleInformationDataAdding; - - RunCommandsArguments arguments; - ArgsTable.TryRemove(commandToRun.InstanceId, out arguments); - } - - private static void MergeError_DataAdded(object sender, DataAddedEventArgs e, PSDataCollection errors) - { - if (errors != null) - { - // Don't use the index from "e" because that may cause race condition. - // It's safe to always remove the first item. - errors.RemoveAt(0); - } - } - - private static void PowerShellInvocation_ErrorAdding(object sender, DataAddingEventArgs e, HostSettingCommandMetadata commandMetadata, PSDataCollection output) - { - ErrorRecord errorRecord = e.ItemAdded as ErrorRecord; - - if (errorRecord != null) - { - if (commandMetadata != null) - { - ScriptPosition scriptStart = new ScriptPosition( - commandMetadata.CommandName, - commandMetadata.StartLineNumber, - commandMetadata.StartColumnNumber, - null); - ScriptPosition scriptEnd = new ScriptPosition( - commandMetadata.CommandName, - commandMetadata.EndLineNumber, - commandMetadata.EndColumnNumber, - null); - ScriptExtent extent = new ScriptExtent(scriptStart, scriptEnd); - - if (errorRecord.InvocationInfo != null) - { - errorRecord.InvocationInfo.DisplayScriptPosition = extent; - } - } - - if (output != null) - { - output.Add(PSObject.AsPSObject(errorRecord)); - } - } - } - - #endregion Object Decoration - - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This methods executes on a WinRM thread - /// - internal static void PowerShellInvocationCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState not returned correctly by PowerShell"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for Executing command using PowerShell - either inproc or remote"); - bool attemptRetry = false; - try - { - if (CheckForCancel(psActivityContext)) return; - commandToRun.EndInvoke(asyncResult); - - // Do any required cleanup here... - implementationContext.CleanUp(); - - if (commandToRun.HadErrors) - { - tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Errors occurred executing the command.")); - psActivityContext.Failed = true; - } - } - catch (Exception e) - { - attemptRetry = ProcessException(args, e); - } - finally - { - if (attemptRetry) - { - Interlocked.Decrement(ref psActivityContext.CommandsRunningCount); - BeginActionRetry(args); - } - else - { - ReleaseResourcesAndCheckForEnd(commandToRun, args, true, false); - } - } - } - } - - /// - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This methods executes on a WinRM thread - /// - private static void RunspaceDisconnectedCallback(RunCommandsArguments args, Exception runspaceDisconnectedException) - { - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - Dbg.Assert(runspaceDisconnectedException != null, "Exception needs to be passed to RunspaceDisconnectedCallback"); - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback when remote runspace got disconnected"); - bool attemptRetry = false; - try - { - if (CheckForCancel(psActivityContext)) return; - - // Do any required cleanup here... - implementationContext.CleanUp(); - - // if there are any helper commands they need to be disposed - if (args.HelperCommand != null) - { - args.HelperCommand.Dispose(); - args.HelperCommand = null; - } - - tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Runspace disconnected is treated as an errors in executing the command.")); - psActivityContext.Failed = true; - - throw runspaceDisconnectedException; - } - catch (Exception e) - { - attemptRetry = HandleRunOneCommandException(args, e); - if (attemptRetry) - BeginActionRetry(args); - } - finally - { - RemoveHandlersFromStreams(commandToRun, args); - RunOneCommandFinally(args, attemptRetry); - tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity: Finished running command.")); - DecrementRunningCountAndCheckForEnd(psActivityContext); - } - } - } - - // Handle a failure. Check if the attempt has gone over the number of allowed attempts, - // and if not, sleep for the retry delay. - static private bool HandleFailure(int attempts, uint? retryCount, uint? retryInterval, - ActivityImplementationContext implementationContext, string errorId, Exception e, PSActivityContext psActivityContext) - { - bool attemptRetry = false; - - // See if the attempt has gone over the quota - if (attempts > retryCount.GetValueOrDefault(0)) - { - // If this was a multi-machine activity, this is an error, not an exception. - if ((implementationContext.PSComputerName != null) && (implementationContext.PSComputerName.Length > 1)) - { - if (e != null) - { - WriteError(e, errorId, ErrorCategory.InvalidResult, implementationContext.PowerShellInstance.Runspace.ConnectionInfo, psActivityContext); - } - } - else - { - if (e != null) - { - // If they wanted to suspend on error, do so. - if (psActivityContext.ParameterDefaults.ContainsKey(Constants.PSSuspendOnError) && - (((bool) psActivityContext.ParameterDefaults[Constants.PSSuspendOnError]) == true)) - { - psActivityContext.SuspendOnError = true; - WriteError(e, errorId, ErrorCategory.InvalidResult, implementationContext.PowerShellInstance.Runspace.ConnectionInfo, psActivityContext); - } - else - { - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(e); - } - } - } - } - } - // We should do a retry - else - { - // Write / Log that an activity retry was required. - if (psActivityContext.progress != null) - { - string progressActivity = ((Activity) psActivityContext.ActivityObject).DisplayName; - - if (string.IsNullOrEmpty(progressActivity)) - progressActivity = psActivityContext.ActivityType.Name; - - string retryMessage = String.Format(CultureInfo.CurrentCulture, Resources.RetryingAction, attempts); - - ProgressRecord progressRecord = new ProgressRecord(0, progressActivity, retryMessage); - lock (psActivityContext.progress) - { - psActivityContext.progress.Add(progressRecord); - } - } - - if (e != null) - { - WriteError(e, errorId, ErrorCategory.InvalidResult, implementationContext.PowerShellInstance.Runspace.ConnectionInfo, psActivityContext); - } - - // Reschedule the command for another attempt. - if (!psActivityContext.IsCanceled) - attemptRetry = true; - - // Wait for the retry delay - for (int currentRetry = 0; currentRetry < retryInterval.GetValueOrDefault(1); currentRetry++) - { - if (CheckForCancel(psActivityContext)) - break; - Thread.Sleep(1000); - } - } - - return attemptRetry; - } - - /// - /// Cancel the running activity - /// - /// The NativeActivityContext provided by the workflow. - protected override void Cancel(NativeActivityContext context) - { - try - { - if (this.bookmarking.Get(context) == true) - { - NoPersistHandle handle = this.noPersistHandle.Get(context); - handle.Enter(context); - } - - PSActivityContext psActivityContextInstance = null; - - HostParameterDefaults hostValues = context.GetExtension(); - - if ((hostValues != null) && (hostValues.AsyncExecutionCollection != null)) - { - Dictionary asyncExecutionCollection = hostValues.AsyncExecutionCollection; - - if (asyncExecutionCollection.ContainsKey(context.ActivityInstanceId)) - { - psActivityContextInstance = asyncExecutionCollection[context.ActivityInstanceId]; - asyncExecutionCollection.Remove(context.ActivityInstanceId); - } - - } - - if (psActivityContextInstance == null) - { - psActivityContextInstance = psActivityContextImplementationVariable.Get(context); - } - - psActivityContextInstance.IsCanceled = true; - psActivityContextImplementationVariable.Set(context, psActivityContextInstance); - - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity: Executing cancel request.")); - - if (psActivityContextInstance.commandQueue != null && !psActivityContextInstance.commandQueue.IsEmpty) - { - foreach(ActivityImplementationContext activityImplementationContext in psActivityContextInstance.commandQueue.ToArray() ) - { - RunCommandsArguments args = null; - ArgsTable.TryGetValue(activityImplementationContext.PowerShellInstance.InstanceId, out args); - - if (args != null) - { - RemoveHandlersFromStreams(activityImplementationContext.PowerShellInstance, args); - } - } - } - - if (psActivityContextInstance.runningCommands != null && psActivityContextInstance.runningCommands.Count > 0) - { - lock (psActivityContextInstance.runningCommands) - { - foreach (System.Management.Automation.PowerShell command in psActivityContextInstance.runningCommands.Keys) - { - RunCommandsArguments args = null; - ArgsTable.TryGetValue(command.InstanceId, out args); - - if (args != null) - { - RemoveHandlersFromStreams(command, args); - } - } - } - } - - psActivityContextInstance.Cancel(); - - } - finally - { - // Marking the workflow state as cancelled. - context.MarkCanceled(); - } - } - - private static void UnregisterAndReleaseRunspace(Runspace runspace, PSWorkflowHost workflowHost, PSActivityContext psActivityContext) - { - Dbg.Assert(runspace.ConnectionInfo != null, "Only remote runspaces can be passed to UnregisterAndReleaseRunspace"); - Dbg.Assert(workflowHost != null, "For remote types PSWorkflowHost should be passed"); - Dbg.Assert(psActivityContext != null, "For remote types, activity context must be passed"); - RunCommandsArguments args; - ArgsTableForRunspaces.TryRemove(runspace.InstanceId, out args); - args = null; - if (psActivityContext.HandleRunspaceStateChanged != null) - runspace.StateChanged -= psActivityContext.HandleRunspaceStateChanged; - workflowHost.RemoteRunspaceProvider.ReleaseRunspace(runspace); - } - - private static void CloseRunspace(Runspace runspace, int commandType = CommandRunInProc, PSWorkflowHost workflowHost = null, PSActivityContext psActivityContext = null) - { - switch (commandType) - { - case CommandRunInProc: - Dbg.Assert(workflowHost != null, "For commands to run in proc PSWorkflowHost should be passed"); - workflowHost.LocalRunspaceProvider.ReleaseRunspace(runspace); - break; - - case CommandRunRemotely: - UnregisterAndReleaseRunspace(runspace, workflowHost, psActivityContext); - break; - - case CimCommandRunInProc: - Dbg.Assert(workflowHost != null, "For CIM commands to run in proc, PSWorkflowHost should be passed"); - workflowHost.LocalRunspaceProvider.ReleaseRunspace(runspace); - break; - - case CommandRunOutOfProc: - case RunInProcNoRunspace: - { - // do nothing - } - break; - default: - Dbg.Assert(false, "Attempting to close runspace for a command type that is not supported"); - break; - } - } - - internal static void CloseRunspaceAndDisposeCommand(System.Management.Automation.PowerShell currentCommand, PSWorkflowHost WorkflowHost, PSActivityContext psActivityContext, int commandType) - { - Dbg.Assert(commandType != RunInProcNoRunspace, "Disposing runspaces should not occur for WMI"); - if (!currentCommand.IsRunspaceOwner && (currentCommand.Runspace.RunspaceStateInfo.State == RunspaceState.Opened || currentCommand.Runspace.RunspaceStateInfo.State == RunspaceState.Disconnected)) - CloseRunspace(currentCommand.Runspace, commandType, WorkflowHost, psActivityContext); - currentCommand.Dispose(); - } - - /// - /// Retrieves the stream and ubiquitous parameter information from the hosting application. - /// These must be passed in as "Streams" and "UbiquitousParameters", respectively. - /// - /// The metadata provided by the hosting application. - protected override void CacheMetadata(NativeActivityMetadata metadata) - { - base.CacheMetadata(metadata); - - metadata.AddImplementationVariable(this.bookmarking); - metadata.AddImplementationVariable(this.noPersistHandle); - metadata.AddImplementationChild(this.cancelTimer); - metadata.AddImplementationVariable(psRunningTimeoutDelayActivityInstanceVar); - metadata.AddImplementationChild(this.terminateActivity); - metadata.AddImplementationChild(this.suspendActivity); - metadata.AddDefaultExtensionProvider(() => new PSWorkflowInstanceExtension()); - - this.ParameterDefaults = new Variable>(); - metadata.AddImplementationVariable(this.ParameterDefaults); - - Tracer.WriteMessage(this.GetType().Name, "CacheMetadata", Guid.Empty, - "Adding PowerShell specific extensions to metadata, CommonParameters are {0} available.", - "not"); - - metadata.AddImplementationVariable(psActivityContextImplementationVariable); - } - - /// - /// The event fired when the PSActivity-derived activity has initialized its its instance - /// of System.Management.Automation.PowerShell, but has not yet invoked its action. This event - /// is for diagnostic, tracing, and testing purposes. - /// - internal static event ActivityCreatedEventHandler ActivityCreated; - - private static void OnActivityCreated(Object sender, ActivityCreatedEventArgs e) - { - if (ActivityCreated != null) - { - ActivityCreated(sender, e); - } - } - - internal static bool IsActivityInlineScript(Activity activity) - { - return String.Equals(activity.GetType().FullName, "Microsoft.PowerShell.Activities.InlineScript", StringComparison.OrdinalIgnoreCase); - } - - internal bool RunWithCustomRemoting(ActivityContext context) - { - if (typeof(PSRemotingActivity).IsAssignableFrom(GetType())) - { - PSRemotingActivity remotingActivity = (PSRemotingActivity) this; - if (remotingActivity.PSRemotingBehavior.Get(context) == RemotingBehavior.Custom) - return true; - } - - return false; - } - - #region Check In-Proc vs Out-of-Proc - - /// - /// Determine if this activity should be run in or out of process - /// when run locally/ - /// - /// The native activity context for this workflow instance - /// True if it should be run in process with the workflow engine. - protected bool GetRunInProc(ActivityContext context) - { - HostParameterDefaults defaults = context.GetExtension(); - if (this is PSGeneratedCIMActivity) - { - return true; - } - - // Look to see if there is a PSRunInProc variable in scope... - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (string.Equals(property.DisplayName, WorkflowPreferenceVariables.PSRunInProcessPreference, StringComparison.OrdinalIgnoreCase)) - { - object parentId = property.GetValue(context.DataContext); - if (parentId != null) - { - bool variableValue; - if (LanguagePrimitives.TryConvertTo(parentId, CultureInfo.InvariantCulture, out variableValue)) - { - return variableValue; - } - } - } - } - - PSActivityContext activityContext = psActivityContextImplementationVariable.Get(context); - PSWorkflowHost workflowHost = GetWorkflowHost(defaults); - - return workflowHost.PSActivityHostController.RunInActivityController(this); - } - - internal static PSWorkflowHost GetWorkflowHost(HostParameterDefaults defaults) - { - PSWorkflowHost _psWorkflowHost = null; - - if ((defaults != null) && (defaults.Runtime != null)) - { - PSWorkflowHost workflowHost = defaults.Runtime; - Interlocked.CompareExchange(ref _psWorkflowHost, workflowHost, null); - - if (_psWorkflowHost != workflowHost) - { - System.Diagnostics.Debug.Assert(false, "Workflow host has been set before the incoming value was processed"); - } - } - if (_psWorkflowHost == null) - { - _psWorkflowHost = DefaultWorkflowHost.Instance; - } - - return _psWorkflowHost; - } - - #endregion Check In-Proc vs Out-of-Proc - } - - - /// - /// The delegate invoked when an activity is created. - /// - /// The PSActivity instance being invoked. - /// The ActivityCreatedEventArgs associated with this invocation. - internal delegate void ActivityCreatedEventHandler(object sender, ActivityCreatedEventArgs e); - - /// - /// Holds the event arguments when a new PSActivity instance is created. - /// - internal class ActivityCreatedEventArgs : EventArgs - { - /// - /// Creates a new ActivityCreatedEventArgs instance. - /// - /// The instance of System.Management.Automation.PowerShell the activity has prepared. - internal ActivityCreatedEventArgs(System.Management.Automation.PowerShell instance) - { - PowerShellInstance = instance; - } - - /// - /// The instance of System.Management.Automation.PowerShell the activity has prepared. - /// - public System.Management.Automation.PowerShell PowerShellInstance - { - get; - set; - } - } - - /// - /// Holds an instance of System.Management.Automation.PowerShell, and the context it needs to run. - /// - public class ActivityImplementationContext - { - /// - /// The instance of System.Management.Automation.PowerShell the activity has prepared. - /// - public System.Management.Automation.PowerShell PowerShellInstance - { - get; - set; - } - - /// - /// Any context required by the command. - /// - public Object WorkflowContext - { - get; - set; - } - - /// - /// context id. - /// - internal int Id - { - get; - set; - } - - /// - /// DisconnectedRunspaceInstanceId - /// - internal Guid DisconnectedRunspaceInstanceId - { - get; - set; - } - - /// - /// DisconnectedRunspaceInstanceId - /// - internal bool EnableRemotingActivityAutoResume - { - get; - set; - } - - /// - /// The Input stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection Input - { - get; - set; - } - - /// - /// The collection to hold the results of the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection Result - { - get; - set; - } - - /// - /// The Error stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSError - { - get; - set; - } - - /// - /// The Progress stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSProgress - { - get; - set; - } - - /// - /// The Verbose stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSVerbose - { - get; - set; - } - - /// - /// The Debug stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSDebug - { - get; - set; - } - - /// - /// The Warning stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSWarning - { - get; - set; - } - - /// - /// The Information stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSInformation - { - get; - set; - } - - /// - /// The computer name to invoke this activity on. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", - "CA1819:PropertiesShouldNotReturnArrays", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public string[] PSComputerName - { - get; - set; - } - - /// - /// Defines the credential to use in the remote connection. - /// - public PSCredential PSCredential - { - get; - set; - } - - /// - /// Defines the remoting behavior to use when invoking this activity. - /// - public RemotingBehavior PSRemotingBehavior { get; set; } - - /// - /// Defines the number of retries that the activity will make to connect to a remote - /// machine when it encounters an error. The default is to not retry. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public uint? PSConnectionRetryCount { get; set; } - - /// - /// The port to use in a remote connection attempt. The default is: - /// HTTP: 5985, HTTPS: 5986. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public uint? PSPort { get; set; } - - /// - /// Determines whether to use SSL in the connection attempt. The default is false. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public bool? PSUseSsl { get; set; } - - /// - /// Determines whether to allow redirection by the remote computer. The default is false. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public bool? PSAllowRedirection { get; set; } - - /// - /// Defines the remote application name to connect to. The default is "wsman". - /// - public string PSApplicationName { get; set; } - - /// - /// Defines the remote configuration name to connect to. The default is "Microsoft.PowerShell". - /// - public string PSConfigurationName { get; set; } - - /// - /// Defines the fully-qualified remote URI to connect to. When specified, the PSComputerName, - /// PSApplicationName, PSConfigurationName, and PSPort are not used. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", - "CA1819:PropertiesShouldNotReturnArrays", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public string[] PSConnectionUri { get; set; } - - /// - /// Defines the authentication type to be used in the remote connection. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public AuthenticationMechanism? PSAuthentication { get; set; } - - /// - /// Defines the certificate thumbprint to be used in the remote connection. - /// - public string PSCertificateThumbprint { get; set; } - - /// - /// Defines any session options to be used in the remote connection. - /// - public System.Management.Automation.Remoting.PSSessionOption PSSessionOption { get; set; } - - - /// - /// Forces the activity to return non-serialized objects. Resulting objects - /// have functional methods and properties (as opposed to serialized versions - /// of them), but will not survive persistence when the Workflow crashes or is - /// persisted. - /// - public bool? PSDisableSerialization - { - get; - set; - } - - /// - /// Forces the activity to not call the persist functionality, which will be responsible for - /// persisting the workflow state onto the disk. - /// - public bool? PSPersist - { - get; - set; - } - - /// - /// Determines whether to append output to Result. - /// - public bool? AppendOutput - { - get; - set; - } - - /// - /// Determines whether to merge the error data to the output stream - /// - public bool? MergeErrorToOutput - { - get; - set; - } - - /// - /// Defines the maximum amount of time, in seconds, that this activity may run. - /// The default is unlimited. - /// - public uint? PSActionRunningTimeoutSec - { - get; - set; - } - - /// - /// Defines the delay, in seconds, between connection retry attempts. - /// The default is one second. - /// - public uint? PSConnectionRetryIntervalSec - { - get; - set; - } - - /// - /// Defines the number of retries that the activity will make when it encounters - /// an error during execution of its action. The default is to not retry. - /// - public uint? PSActionRetryCount - { - get; - set; - } - - /// - /// Defines the delay, in seconds, between action retry attempts. - /// The default is one second. - /// - public uint? PSActionRetryIntervalSec - { - get; - set; - } - - /// - /// Defines the PSProgressMessage. - /// - public string PSProgressMessage - { - get; - set; - } - - /// - /// The connection info to use for this command (may be null) - /// - public WSManConnectionInfo ConnectionInfo - { - get; - set; - } - - /// - /// This the list of module names (or paths) that are required to run this Activity successfully. - /// The default is null. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", - "CA1819:PropertiesShouldNotReturnArrays", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public string[] PSRequiredModules - { - get; - set; - } - - /// - /// The path that the workflow was imported from. - /// - public string PSWorkflowPath - { - get; - set; - } - - /// - /// Determines whether to emit verbose output of the activity. - /// - public bool? Verbose - { - get; - set; - } - - /// - /// Determines whether to emit debug output of the activity. - /// - public bool? Debug - { - get; - set; - } - - /// - /// Determines whether to emit whatif output of the activity. - /// - public bool? WhatIf - { - get; - set; - } - - /// - /// Determines how errors should be handled by the activity. - /// - public ActionPreference? ErrorAction - { - get; - set; - } - - /// - /// Determines how warnings should be handled by the activity. - /// - public ActionPreference? WarningAction - { - get; - set; - } - - /// - /// Determines how information messages should be handled by the activity. - /// - public ActionPreference? InformationAction - { - get; - set; - } - /// - /// Policy for activity host that will execute this activity - /// - public PSActivityEnvironment PSActivityEnvironment - { - get; - set; - } - - /// - /// Specifies the authentication level to be used with the WMI connection. Valid values are: - /// -1: Unchanged - /// 0: Default - /// 1: None (No authentication in performed.) - /// 2: Connect (Authentication is performed only when the client establishes a relationship with the application.) - /// 3: Call (Authentication is performed only at the beginning of each call when the application receives the request.) - /// 4: Packet (Authentication is performed on all the data that is received from the client.) - /// 5: PacketIntegrity (All the data that is transferred between the client and the application is authenticated and verified.) - /// 6: PacketPrivacy (The properties of the other authentication levels are used, and all the data is encrypted.) - /// - public AuthenticationLevel PSAuthenticationLevel { get; set; } - - /// - /// Specifies the impersonation level to use. Valid values are: - /// 0: Default (reads the local registry for the default impersonation level , which is usually set to "3: Impersonate".) - /// 1: Anonymous (Hides the credentials of the caller.) - /// 2: Identify (Allows objects to query the credentials of the caller.) - /// 3: Impersonate (Allows objects to use the credentials of the caller.) - /// 4: Delegate (Allows objects to permit other objects to use the credentials of the caller.) - /// - public ImpersonationLevel Impersonation { get; set; } - - /* - * Enables all the privileges of the current user before the command makes the WMI call. - */ - /// - /// Enables all the privileges of the current user before the command makes the WMI call. - /// - public bool EnableAllPrivileges { get; set; } - - /// - /// Specifies the authority to use to authenticate the WMI connection. You can specify - /// standard NTLM or Kerberos authentication. To use NTLM, set the authority setting - /// to ntlmdomain:"DomainName", where "DomainName" identifies a valid NTLM domain name. - /// To use Kerberos, specify kerberos:"DomainName>\ServerName". You cannot include - /// the authority setting when you connect to the local computer. - /// - public string Authority { get; set; } - - /// - /// When used with the Class parameter, this parameter specifies the WMI repository namespace - /// where the referenced WMI class is located. When used with the List parameter, it specifies - /// the namespace from which to gather WMI class information. - /// summary> - public string Namespace { get; set; } - - /// - /// Specifies the preferred locale for WMI objects. Specify the value of the Locale - /// parameter as an array in the MS_"LCID" format in the preferred order . - /// - public string Locale { get; set; } - - /// - /// CIM Sessions to use for this activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", - "CA1819:PropertiesShouldNotReturnArrays", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public CimSession[] CimSession { get; set; } - - - /// - /// Perform any cleanup activities needed by this activity implementation - /// - public virtual void CleanUp() - { - } - } - - class RetryCount - { - //internal int ConnectionAttempts { get; set; } - internal int ActionAttempts - { - get; - set; - } - } - - /// - /// Defining resuming extension. - /// - public class PSWorkflowInstanceExtension : IWorkflowInstanceExtension - { - private WorkflowInstanceProxy instance; - - /// - /// Get all additional extensions. - /// - /// Returns no extensions. - public IEnumerable GetAdditionalExtensions() - { - return null; - } - - /// - /// Set the instance of the workflow. - /// - /// The workflow instance proxy. - public void SetInstance(WorkflowInstanceProxy instance) - { - this.instance = instance; - } - - /// - /// Begin resuming book mark. - /// - /// The bookmark where it will be resumed. - /// The value which need to be passed to the bookmark. - /// The call back function when resuming the bookmark. - /// The state of the async call. - /// Returns the result of async call. - public IAsyncResult BeginResumeBookmark(Bookmark bookmark, object value, AsyncCallback callback, object state) - { - return instance.BeginResumeBookmark(bookmark, value, callback, state); - } - - /// - /// End resuming bookmark. - /// - /// The result of async all. - /// Returns the bookmark resumption result. - public BookmarkResumptionResult EndResumeBookmark(IAsyncResult asyncResult) - { - return instance.EndResumeBookmark(asyncResult); - } - } - - /// - /// Stores information about an activity argument - /// - public sealed class PSActivityArgumentInfo - { - /// - /// The name of the argument. - /// - public string Name { get; set; } - - /// - /// The actual argument. - /// - public Argument Value { get; set; } - } - - /// - /// Abstract base containing the common members and invocation code for the WMI cmdlets. - /// - public abstract class WmiActivity : PSActivity - { - /// - /// The computer name to invoke this activity on. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSComputerName - { - get; - set; - } - - /// - /// Defines the credential to use in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCredential - { - get; - set; - } - - /// - /// Specifies the authentication level to be used with the WMI connection. Valid values are: - /// -1: Unchanged - /// 0: Default - /// 1: None (No authentication in performed.) - /// 2: Connect (Authentication is performed only when the client establishes a relationship with the application.) - /// 3: Call (Authentication is performed only at the beginning of each call when the application receives the request.) - /// 4: Packet (Authentication is performed on all the data that is received from the client.) - /// 5: PacketIntegrity (All the data that is transferred between the client and the application is authenticated and verified.) - /// 6: PacketPrivacy (The properties of the other authentication levels are used, and all the data is encrypted.) - /// - [ConnectivityCategory] - [DefaultValue(null)] - public AuthenticationLevel PSAuthenticationLevel { get; set; } - - /// - /// Specifies the impersonation level to use. Valid values are: - /// 0: Default (reads the local registry for the default impersonation level , which is usually set to "3: Impersonate".) - /// 1: Anonymous (Hides the credentials of the caller.) - /// 2: Identify (Allows objects to query the credentials of the caller.) - /// 3: Impersonate (Allows objects to use the credentials of the caller.) - /// 4: Delegate (Allows objects to permit other objects to use the credentials of the caller.) - /// - [ConnectivityCategory] - [DefaultValue(null)] - public ImpersonationLevel Impersonation { get; set; } - - /* - * Enables all the privileges of the current user before the command makes the WMI call. - */ - /// - /// Enables all the privileges of the current user before the command makes the WMI call. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public bool EnableAllPrivileges { get; set; } - - /// - /// Specifies the authority to use to authenticate the WMI connection. You can specify - /// standard NTLM or Kerberos authentication. To use NTLM, set the authority setting - /// to ntlmdomain:"DomainName", where "DomainName" identifies a valid NTLM domain name. - /// To use Kerberos, specify kerberos:"DomainName>\ServerName". You cannot include - /// the authority setting when you connect to the local computer. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public string Authority { get; set; } - - /// - /// When used with the Class parameter, this parameter specifies the WMI repository namespace - /// where the referenced WMI class is located. When used with the List parameter, it specifies - /// the namespace from which to gather WMI class information. - /// summary> - [BehaviorCategory] - [DefaultValue(null)] - public InArgument Namespace { get; set; } - - /// - /// Specifies the preferred locale for WMI objects. Specify the value of the Locale - /// parameter as an array in the MS_"LCID" format in the preferred order . - /// - [BehaviorCategory] - [DefaultValue(null)] - public string Locale { get; set; } - - /// - /// Generic version of the function to handle value types - /// - /// The type of the intended argument - /// - /// - /// - protected T GetUbiquitousParameter(string parameterName, Dictionary parameterDefaults) - { - if (ParameterDefaults != null && parameterDefaults.ContainsKey(parameterName)) - return (T)parameterDefaults[parameterName]; - else - return default(T); - } - - /// - /// Sets to execute the command that was passed in. - /// - /// - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - protected System.Management.Automation.PowerShell GetWmiCommandCore(NativeActivityContext context, string name) - { - System.Management.Automation.PowerShell command; - command = System.Management.Automation.PowerShell.Create().AddCommand(name); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: WMI Command '{1}'.", - context.ActivityInstanceId, name)); - - - if (Impersonation != ImpersonationLevel.Default) - { - command.AddParameter("Impersonation", Impersonation); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Impersonation", Impersonation)); - - } - - Dictionary parameterDefaults = context.GetValue>(this.ParameterDefaults); - - if (PSAuthenticationLevel != AuthenticationLevel.Default) - { - command.AddParameter("Authentication", PSAuthenticationLevel); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Authentication", PSAuthenticationLevel)); - } - else if (GetUbiquitousParameter("PSAuthenticationLevel", parameterDefaults) != AuthenticationLevel.Default) - { - var authLevel = GetUbiquitousParameter("PSAuthenticationLevel", parameterDefaults); - command.AddParameter("Authentication", authLevel); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Setting parameter {1} to {2} from ubiquitous parameters.", - context.ActivityInstanceId, "AuthenticationLevel", authLevel)); - } - - if (Locale != null) - { - command.AddParameter("Locale", Locale); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Locale", Locale)); - - } - - if (EnableAllPrivileges) - { - command.AddParameter("EnableAllPrivileges", EnableAllPrivileges); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "EnableAllPrivileges", EnableAllPrivileges)); - } - - if (Authority != null) - { - command.AddParameter("Authority", Authority); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Setting parameter {1} to {2}.", context.ActivityInstanceId, "Authority", Authority)); - } - - if (Namespace.Get(context) != null) - { - command.AddParameter("Namespace", Namespace.Get(context)); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Namespace", Namespace.Get(context))); - } - - // WMI does it's own remoting so we need to handle the PSCredential/Credential parameter - // explicitly ourselves. - if (PSCredential.Get(context) != null) - { - command.AddParameter("Credential", PSCredential.Get(context)); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Credential", PSCredential.Get(context))); - } - - return command; - } - - /// - /// Perform necessary steps to prepare the WMI commands - /// - /// The activity context to use - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - protected override List GetImplementation(NativeActivityContext context) - { - List commands = new List(); - string[] computernames = PSComputerName.Get(context); - - // Configure the remote connectivity options - if (computernames == null || computernames.Length == 0) - { - computernames = new string[] { "localhost" }; - } - - foreach (string computername in computernames) - { - // Create the PowerShell instance, and add the command to it. - ActivityImplementationContext implementationContext = GetPowerShell(context); - System.Management.Automation.PowerShell invoker = implementationContext.PowerShellInstance; - - // Don't add the computer if it's empty or localhost... - if (!String.IsNullOrEmpty(computername) && !String.Equals(computername, "localhost", StringComparison.OrdinalIgnoreCase)) - { - invoker.AddParameter("ComputerName", computername); - } - - commands.Add( - new ActivityImplementationContext() { PowerShellInstance = invoker } - ); - } - - return commands; - } - } - - /// - /// Implementation of ICommandRuntime for running the WMI cmdlets in - /// workflow without PowerShell. - /// - internal class DirectExecutionActivitiesCommandRuntime : ICommandRuntime - { - - /// - /// Constructs an instance of the default ICommandRuntime object - /// that will write objects into the arraylist that was passed. - /// - public DirectExecutionActivitiesCommandRuntime(PSDataCollection output, ActivityImplementationContext implementationContext, Type cmdletType) - { - if (output == null) throw new ArgumentNullException("output"); - if (implementationContext == null) throw new ArgumentNullException("implementationContext"); - if (cmdletType == null) throw new ArgumentNullException("cmdletType"); - - _output = output; - _implementationContext = implementationContext; - _cmdletType = cmdletType; - } - - PSDataCollection _output; - ActivityImplementationContext _implementationContext; - Type _cmdletType; - - /// - /// THe error record stream - /// - public PSDataCollection Error { get; set; } - - /// - /// The progress record stream - /// - public PSDataCollection Progress { get; set; } - - /// - /// The verbose record stream - /// - public PSDataCollection Verbose { get; set; } - - /// - /// The warning record stream - /// - public PSDataCollection Warning { get; set; } - - /// - /// The debug output stream - /// - public PSDataCollection Debug { get; set; } - - /// - /// The information record stream - /// - public PSDataCollection Information { get; set; } - - /// - /// Return the instance of PSHost - null by default. - /// - public PSHost Host { get { return null; } } - - #region Write - /// - /// Implementation of WriteDebug - just discards the input. - /// - /// Text to write - public void WriteDebug(string text) - { - if (Debug == null) - return; - - if (text != null) - { - Debug.Add(new DebugRecord(text)); - } - } - - /// - /// Default implementation of WriteError - if the error record contains - /// an exception then that exception will be thrown. If not, then an - /// InvalidOperationException will be constructed and thrown. - /// - /// Error record instance to process - public void WriteError(ErrorRecord errorRecord) - { - if (Error == null) - return; - - ErrorRecord updatedErrorRecord = new ErrorRecord(errorRecord.Exception, errorRecord.FullyQualifiedErrorId + ',' + _cmdletType.FullName, errorRecord.CategoryInfo.Category, errorRecord.TargetObject); - - ActionPreference preference = (_implementationContext.ErrorAction == null) ? - ActionPreference.Continue : _implementationContext.ErrorAction.Value; - - switch (preference) - { - case ActionPreference.SilentlyContinue: - case ActionPreference.Ignore: - break; - case ActionPreference.Inquire: - case ActionPreference.Continue: - if (errorRecord != null) - { - Error.Add(updatedErrorRecord); - } - break; - case ActionPreference.Stop: - ThrowTerminatingError(updatedErrorRecord); - break; - } - } - - /// - /// Default implementation of WriteObject - adds the object to the arraylist - /// passed to the objects constructor. - /// - /// Object to write - public void WriteObject(object sendToPipeline) - { - _output.Add(PSObject.AsPSObject(sendToPipeline)); - } - - /// - /// Write objects to the output collection - /// - /// Object to write - /// If true, the collection is enumerated, otherwise - /// it's written as a scalar. - /// - public void WriteObject(object sendToPipeline, bool enumerateCollection) - { - if (enumerateCollection) - { - IEnumerator e = LanguagePrimitives.GetEnumerator(sendToPipeline); - if (e == null) - { - WriteObject(sendToPipeline); - } - else - { - while (e.MoveNext()) - { - WriteObject(e.Current); - } - } - } - else - { - WriteObject(sendToPipeline); - } - } - - /// - /// Write a progress record - /// - /// progress record to write. - public void WriteProgress(ProgressRecord progressRecord) - { - WriteProgress(1, progressRecord); - } - - /// - /// Write a progress record, ignore the id field - /// - /// Source ID to write for - /// record to write. - public void WriteProgress(Int64 sourceId, ProgressRecord progressRecord) - { - if (Progress == null) - return; - - if (progressRecord != null) - { - Progress.Add(progressRecord); - } - } - - /// - /// Write a verbose record - /// - /// Text to write. - public void WriteVerbose(string text) - { - if (_implementationContext.Verbose != true) - return; - - if (Verbose == null) - return; - - if (text != null) - { - Verbose.Add(new VerboseRecord(text)); - } - } - - /// - /// Write a warning record - /// - /// Text to write. - public void WriteWarning(string text) - { - if (_implementationContext.WarningAction != ActionPreference.Continue) - return; - - if (Warning == null) - return; - - if (text != null) - { - Warning.Add(new WarningRecord(text)); - } - } - - /// - /// Write a information record - /// - /// Record to write. - public void WriteInformation(InformationRecord record) - { - if (_implementationContext.InformationAction != ActionPreference.Continue) - return; - - if (Information == null) - return; - - if (record != null) - { - Information.Add(record); - } - } - - /// - /// Write command detail info to the eventlog. - /// - /// Text to write. - public void WriteCommandDetail(string text) - { - PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource(); - tracer.WriteMessage(text); - } - - #endregion Write - - #region Should - /// - /// Default implementation - always returns true. - /// - /// ignored - /// true - public bool ShouldProcess(string target) { return true; } - /// - /// Default implementation - always returns true. - /// - /// ignored - /// ignored - /// true - public bool ShouldProcess(string target, string action) { return true; } - /// - /// Default implementation - always returns true. - /// - /// ignored - /// ignored - /// ignored - /// true - public bool ShouldProcess(string verboseDescription, string verboseWarning, string caption) { return true; } - /// - /// Default implementation - always returns true. - /// - /// ignored - /// ignored - /// ignored - /// ignored - /// true - public bool ShouldProcess(string verboseDescription, string verboseWarning, string caption, out ShouldProcessReason shouldProcessReason) { shouldProcessReason = ShouldProcessReason.None; return true; } - /// - /// Default implementation - always returns true. - /// - /// ignored - /// ignored - /// true - public bool ShouldContinue(string query, string caption) { return true; } - /// - /// Default implementation - always returns true. - /// - /// ignored - /// ignored - /// ignored - /// ignored - /// true - public bool ShouldContinue(string query, string caption, ref bool yesToAll, ref bool noToAll) { return true; } - #endregion Should - - #region Transaction Support - /// - /// Returns true if a transaction is available and active. - /// - public bool TransactionAvailable() { return false; } - - /// - /// Gets an object that surfaces the current PowerShell transaction. - /// When this object is disposed, PowerShell resets the active transaction - /// - public PSTransactionContext CurrentPSTransaction - { - get - { - // We want to throw in this situation, and want to use a - // property because it mimics the C# using(TransactionScope ...) syntax - throw new InvalidOperationException(); - } - } - #endregion Transaction Support - - #region Misc - /// - /// Implementation of the dummy default ThrowTerminatingError API - it just - /// does what the base implementation does anyway - rethrow the exception - /// if it exists, otherwise throw an invalid operation exception. - /// - /// The error record to throw - public void ThrowTerminatingError(ErrorRecord errorRecord) - { - if (errorRecord.Exception != null) - { - throw errorRecord.Exception; - } - else - { - throw new System.InvalidOperationException(errorRecord.ToString()); - } - } - #endregion - } - - /// - /// Suspends the current workflow. - /// - internal class SuspendOnError : NativeActivity - { - /// - /// Returns true if the activity can induce an idle. - /// - protected override bool CanInduceIdle { get { return true; } } - - /// - /// Invokes the activity - /// - /// The activity context. - /// True if the given argument is set. - protected override void Execute(NativeActivityContext context) - { - string bookmarkname = PSActivity.PSSuspendBookmarkPrefix; - bookmarkname += Guid.NewGuid().ToString().Replace("-", "_"); - - context.CreateBookmark(bookmarkname, BookmarkResumed); - } - - private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value) - { - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityHostManager.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityHostManager.cs deleted file mode 100644 index 0ad5dadff20..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityHostManager.cs +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2010 Microsoft Corporation. All rights reserved - */ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Management.Automation; -using System.Threading; -using System.Diagnostics.CodeAnalysis; -using System.Activities; -using Microsoft.PowerShell.Workflow; -using System.Collections.Concurrent; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Class the describes an activity host arguments - /// - internal sealed class PSResumableActivityContext - { - /// - /// PSActivityHostArguments - /// - /// - internal PSResumableActivityContext(PowerShellStreams streams) - { - Streams = streams; - Error = null; - Failed = false; - SupportDisconnectedStreams = true; - } - - /// - /// Gets and sets Streams - /// - internal PowerShellStreams Streams { get; set; } - - /// - /// Gets Errors - /// - internal Exception Error { get; set; } - - /// - /// Get failed - /// - internal bool Failed { get; set; } - - internal bool SupportDisconnectedStreams { get; set; } - } - - /// - /// Class the describes an activity host policy - /// - public sealed class PSActivityEnvironment - { - private readonly Collection _modules = new Collection(); - private readonly Dictionary _variables = new Dictionary(StringComparer.OrdinalIgnoreCase); - - /// - /// Collection of modules that an activity is - /// dependent upon - /// - public Collection Modules - { - get { return _modules; } - } - - /// - /// Collection of variables that an activity is - /// dependent upon - /// - public Dictionary Variables - { - get { return _variables; } - } - } - - #region PSActivityHostController - - /// - /// Activity host manager interface. This interface can be - /// used to implement various activity hosts - /// - public abstract class PSActivityHostController - { - private PSWorkflowRuntime _runtime; - private readonly ConcurrentDictionary _inProcActivityLookup = new ConcurrentDictionary(); - - /// - /// Runtime should be provided for accessing the runtime activity mode - /// - protected PSActivityHostController(PSWorkflowRuntime runtime) - { - _runtime = runtime; - } - - /// - /// Identifies whether the specified activity should run in the activity - /// host or in-proc - /// - /// activity that needs to be verified - /// true, if the activity should run in the activity - /// host - /// false otherwise - [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] - public virtual bool RunInActivityController(Activity activity) - { - if (activity == null) - { - throw new ArgumentNullException("activity"); - } - - String name = activity.GetType().Name; - - if (_inProcActivityLookup.ContainsKey(name)) return _inProcActivityLookup[name]; - - - ActivityRunMode runMode = _runtime.Configuration.GetActivityRunMode(activity); - bool runInProc = runMode == ActivityRunMode.InProcess; - - return _inProcActivityLookup.GetOrAdd(name, runInProc); - } - } - - /// - /// This class will be used for disconnected execution where the - /// Job Id and bookmark will be used resume the execution of workflow - /// after the completion of activity controller work. - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public abstract class PSResumableActivityHostController : PSActivityHostController - { - /// - /// Default Constructor - /// - /// - protected PSResumableActivityHostController(PSWorkflowRuntime runtime) - : base(runtime) - { - - } - - /// - /// StartResumablePSCommand - /// - /// - /// - /// - /// - /// - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public virtual void StartResumablePSCommand(Guid jobInstanceId, - Bookmark bookmark, - System.Management.Automation.PowerShell command, - PowerShellStreams streams, - PSActivityEnvironment environment, - PSActivity activityInstance) - { - throw new NotImplementedException(); - } - - /// - /// StopResumablePSCommand - /// - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public virtual void StopAllResumablePSCommands(Guid jobInstanceId) - { - throw new NotImplementedException(); - } - - /// - /// This property identifies if the Activity controller is running in disconnected mode - /// or not. If it is running in disconnected mode then all the output and data streams will be - /// proxied as new objects. - /// - public virtual bool SupportDisconnectedPSStreams - { - get - { - return true; - } - } - } - - #endregion -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSCleanupActivity.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSCleanupActivity.cs deleted file mode 100644 index 62d5d003036..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSCleanupActivity.cs +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2011 Microsoft Corporation. All rights reserved - */ -using System; -using System.Activities; -using System.Threading; -using System.Management.Automation.Runspaces; -using System.ComponentModel; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Base activity for cleanup activities - /// - public abstract class PSCleanupActivity : PSRemotingActivity - { - /// - /// Creates and returns an empty powershell - /// - /// activity context - /// A new activity implementation context - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - ActivityImplementationContext implementationContext = new ActivityImplementationContext - { - PowerShellInstance = - System.Management.Automation.PowerShell. - Create() - }; - - return implementationContext; - } - - /// - /// Method that needs to be overridden to perform the actual - /// cleanup action - /// - /// RunCommandsArguments - /// callback to call when cleanup - /// is done - /// The signature forces this method to be internal - internal virtual void DoCleanup(RunCommandsArguments args, WaitCallback callback) - { - throw new NotImplementedException(); - } - } - - /// - /// Activity to cleanup all Runspaces (PSRP connections) to - /// a remote machine - /// - public class DisablePSWorkflowConnection : PSCleanupActivity - { - // Arguments - - /// - /// Provides access to the Authentication parameter. - /// - [RequiredArgument] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public InArgument TimeoutSec - { - get; - set; - } - private int timeout = DefaultCleanupWaitTimerIntervalMs; - private const int DefaultCleanupWaitTimerIntervalMs = 5*60*1000; - - /// - /// Set the display name of the activity - /// - public DisablePSWorkflowConnection() - { - DisplayName = "Disable-PSWorkflowConnection"; - } - - /// - /// Creates and returns an empty powershell - /// - /// activity context - /// A new activity implementation context - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - if (TimeoutSec.Expression != null) - { - timeout = TimeoutSec.Get(context); - } - - return base.GetPowerShell(context); - } - - /// - /// Method that needs to be overridden to perform the actual - /// cleanup action - /// - /// RunCommandsArguments - /// callback to call when cleanup - /// is done - /// The signature forces this method to be internal - internal override void DoCleanup(RunCommandsArguments args, WaitCallback callback) - { - PSWorkflowHost workflowHost = args.WorkflowHost; - WSManConnectionInfo connectionInfo = - args.ImplementationContext.PowerShellInstance.Runspace.ConnectionInfo as WSManConnectionInfo; - - args.CleanupTimeout = timeout; - - if (connectionInfo == null) - { - if (callback != null) - callback(args); - } - else - { - workflowHost.RemoteRunspaceProvider.RequestCleanup(connectionInfo, callback, args); - } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSRemotingActivitiesBase.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSRemotingActivitiesBase.cs deleted file mode 100644 index f789188c6c5..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSRemotingActivitiesBase.cs +++ /dev/null @@ -1,257 +0,0 @@ -using System; -using System.Activities; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Management.Automation; -using System.Management.Automation.Remoting; -using System.Management.Automation.Runspaces; -using System.Threading; -using System.Threading.Tasks; -using System.ComponentModel; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Base class for PowerShell-based workflow activities - /// - public abstract class PSRemotingActivity : PSActivity, IImplementsConnectionRetry - { - /// - /// The computer name to invoke this activity on. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSComputerName - { - get; - set; - } - - /// - /// Defines the credential to use in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCredential - { - get; - set; - } - - /// - /// Defines the remoting behavior to use when invoking this activity. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSRemotingBehavior { get; set; } - - /// - /// Defines the number of retries that the activity will make to connect to a remote - /// machine when it encounters an error. The default is to not retry. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSConnectionRetryCount { get; set; } - - /// - /// Defines the delay, in seconds, between connection retry attempts. - /// The default is one second. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSConnectionRetryIntervalSec { get; set; } - - /// - /// The port to use in a remote connection attempt. The default is: - /// HTTP: 5985, HTTPS: 5986. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSPort { get; set; } - - /// - /// Determines whether to use SSL in the connection attempt. The default is false. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSUseSsl { get; set; } - - /// - /// Determines whether to allow redirection by the remote computer. The default is false. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSAllowRedirection { get; set; } - - /// - /// Defines the remote application name to connect to. The default is "wsman". - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSApplicationName { get; set; } - - /// - /// Defines the remote configuration name to connect to. The default is "Microsoft.PowerShell". - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSConfigurationName { get; set; } - - /// - /// Defines the fully-qualified remote URI to connect to. When specified, the PSComputerName, - /// PSApplicationName, PSConfigurationName, and PSPort are not used. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSConnectionUri { get; set; } - - /// - /// Defines the authentication type to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSAuthentication { get; set; } - - /// - /// Defines the certificate thumbprint to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCertificateThumbprint { get; set; } - - /// - /// Defines any session options to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSSessionOption { get; set; } - - /// - /// Declares whether this command supports its own custom remoting. - /// Commands that support their own custom remoting should return TRUE - /// from this property, and use the PSComputerName parameter as required - /// when the 'PSRemotingBehavior' argument is set to 'Custom'. - /// - protected virtual bool SupportsCustomRemoting { get { return false; } } - - /// - /// Returns TRUE if the PSComputerName argument has been specified, and - /// contains at least one target. - /// - /// The workflow NativeActivityContext - /// - protected bool GetIsComputerNameSpecified(ActivityContext context) - { - return ((PSComputerName.Get(context) != null) && - (PSComputerName.Get(context).Length > 0)); - } - - // Prepare the commands - /// - /// Prepare commands that use PSRP for remoting... - /// - /// The activity context to use - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - protected override List GetImplementation(NativeActivityContext context) - { - string[] computernames = PSComputerName.Get(context); - string[] connectionUris = PSConnectionUri.Get(context); - PSSessionOption sessionOptions = PSSessionOption.Get(context); - List commands = new List(); - - // Configure the remote connectivity options - RemotingBehavior remotingBehavior = PSRemotingBehavior.Get(context); - if (PSRemotingBehavior.Expression == null) - { - remotingBehavior = RemotingBehavior.PowerShell; - } - - // If they've specified the 'Custom' remoting behavior, ensure the activity - // supports it. - if ((remotingBehavior == RemotingBehavior.Custom) && (!SupportsCustomRemoting)) - { - throw new ArgumentException(Resources.CustomRemotingNotSupported); - } - - if (PSCredential.Get(context) != null && PSAuthentication.Get(context) == AuthenticationMechanism.NegotiateWithImplicitCredential) - { - throw new ArgumentException(Resources.CredentialParameterCannotBeSpecifiedWithNegotiateWithImplicitAuthentication); - } - - // we need connection info to be populated even for the custom remoting case. - // This is because the ComputerName is picked up from the connection info field - if ((remotingBehavior == RemotingBehavior.PowerShell || (IsActivityInlineScript(this) && RunWithCustomRemoting(context))) && - (GetIsComputerNameSpecified(context) || (connectionUris != null && connectionUris.Length > 0))) - { - List connectionInfo = ActivityUtils.GetConnectionInfo( - computernames, - connectionUris, PSCertificateThumbprint.Get(context), PSConfigurationName.Get(context), - PSUseSsl.Get(context), PSPort.Get(context), PSApplicationName.Get(context), - PSCredential.Get(context), PSAuthentication.Get(context).GetValueOrDefault(AuthenticationMechanism.Default), - PSAllowRedirection.Get(context).GetValueOrDefault(false), - sessionOptions); - - foreach (WSManConnectionInfo connection in connectionInfo) - { - CreatePowerShellInstance(context, connection, commands); - } - } - // Configure the local invocation options - else - { - CreatePowerShellInstance(context, null, commands); - } - - return commands; - } - - /// - /// Creates Powershell instance and adds the command to it - /// - /// The activity context to use - /// The wsman connection to use - /// The list of commands - private void CreatePowerShellInstance(NativeActivityContext context, WSManConnectionInfo connection, - List commands) - { - // Create the PowerShell instance, and add the command to it. - ActivityImplementationContext implementationContext = GetPowerShell(context); -#if true - Runspace runspace; - if (connection != null) - { - implementationContext.ConnectionInfo = connection; - runspace = RunspaceFactory.CreateRunspace(connection); - implementationContext.PowerShellInstance.Runspace = runspace; - } - else - { - // if PSComputerName is "" or $null than connection is NULL - UpdateImplementationContextForLocalExecution(implementationContext, context); - } - -#endif - // Add it to the queue of commands to execute. - commands.Add(implementationContext); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSSelfRemotingActivitiesBase.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSSelfRemotingActivitiesBase.cs deleted file mode 100644 index d8d7aaae9b9..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSSelfRemotingActivitiesBase.cs +++ /dev/null @@ -1,854 +0,0 @@ -using System; -using System.Activities; -using System.Collections.Generic; -using System.Management.Automation; -using System.Management.Automation.Remoting; -using System.Management.Automation.Runspaces; -using System.Threading; -using System.ComponentModel; -using Microsoft.Management.Infrastructure; -using Microsoft.Management.Infrastructure.Options; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Base class for activities that can use WsMan directly to contact ta managed node. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CIM")] - public abstract class PSGeneratedCIMActivity : PSActivity, IImplementsConnectionRetry - { - /// - /// The computer name to invoke this activity on. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSComputerName - { - get; - set; - } - - /// - /// Defines the credential to use in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCredential - { - get; - set; - } - - /// - /// Defines the authentication type to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSAuthentication { get; set; } - - - /// - /// Defines the certificate thumbprint to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCertificateThumbprint { get; set; } - - /// - /// Defines the number of retries that the activity will make to connect to a remote - /// machine when it encounters an error. The default is to not retry. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSConnectionRetryCount { get; set; } - - /// - /// Defines the delay, in seconds, between connection retry attempts. - /// The default is one second. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSConnectionRetryIntervalSec { get; set; } - - /// - /// Defines the resource URI used by the CIM cmdlet. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument ResourceUri { get; set; } - - /// - /// The port to use in a remote connection attempt. The default is: - /// HTTP: 5985, HTTPS: 5986. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSPort { get; set; } - - /// - /// Determines whether to use SSL in the connection attempt. The default is false. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSUseSsl { get; set; } - - /// - /// Defines any session options to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSSessionOption { get; set; } - - /// - /// CIM Sessions to use for this activity. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument CimSession { get; set; } - - - /// - /// Contains the powershell text defining the mode for this command. - /// - protected abstract string ModuleDefinition { get; } - - /// - /// Returns TRUE if the PSComputerName argument has been specified, and - /// contains at least one target. - /// - /// The workflow NativeActivityContext - /// - protected bool GetIsComputerNameSpecified(ActivityContext context) - { - return ((PSComputerName.Get(context) != null) && - (PSComputerName.Get(context).Length > 0)); - } - - /// - /// Prepare commands that use CIM for remoting... - /// - /// The activity context to use - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - protected override List GetImplementation(NativeActivityContext context) - { - bool needRunspace = !typeof(GenericCimCmdletActivity).IsAssignableFrom(this.GetType()); - string[] computernames = PSComputerName.Get(context); - CimSession[] sessions = this.CimSession.Get(context); - Uri resourceUri = null; - - if (ResourceUri != null) - { - resourceUri = ResourceUri.Get(context); - } - - List commands = new List(); - - // Configure the remote connectivity options... - if (computernames != null && computernames.Length > 0) - { - WSManSessionOptions sessionOptions = new WSManSessionOptions(); - - // Set a timeout on the connection... - uint? timeout = PSActionRunningTimeoutSec.Get(context); - if (timeout.HasValue) - { - sessionOptions.Timeout = TimeSpan.FromSeconds((double)(timeout.Value)); - } - - // See if we should use SSL or not... - bool? useSsl = PSUseSsl.Get(context); - bool sessionOptionUseSsl = false; - - if (useSsl.HasValue) - { - sessionOptions.UseSsl = useSsl.Value; - sessionOptionUseSsl = useSsl.Value; - } - - // Set the port to use - uint? port = PSPort.Get(context); - uint sessionOptionPort = 0; - - if (port.HasValue) - { - sessionOptions.DestinationPort = port.Value; - sessionOptionPort = port.Value; - } - - // Map over options from PSSessionConfig to WSManSessionOptions - PSSessionOption pso = PSSessionOption.Get(context); - if (pso != null) - { - sessionOptions.NoEncryption = pso.NoEncryption; - sessionOptions.CertCACheck = pso.SkipCACheck; - sessionOptions.CertCNCheck = pso.SkipCNCheck; - sessionOptions.CertRevocationCheck = pso.SkipRevocationCheck; - - if (pso.UseUTF16) - sessionOptions.PacketEncoding = PacketEncoding.Utf16; - - if (pso.Culture != null) - sessionOptions.Culture = pso.Culture; - - if (pso.UICulture != null) - sessionOptions.UICulture = pso.UICulture; - - if (pso.ProxyCredential != null) - { - string[] parts = pso.ProxyCredential.UserName.Split('\\'); - string domain, userid; - if (parts.Length < 2) - { - domain = string.Empty; - userid = parts[0]; - } - else - { - domain = parts[0]; - userid = parts[1]; - } - - sessionOptions.AddProxyCredentials( - new CimCredential(ConvertPSAuthenticationMechanismToCimPasswordAuthenticationMechanism(pso.ProxyAuthentication), - domain, userid, pso.ProxyCredential.Password)); - } - - switch (pso.ProxyAccessType) - { - case ProxyAccessType.WinHttpConfig: - sessionOptions.ProxyType = ProxyType.WinHttp; - break; - case ProxyAccessType.AutoDetect: - sessionOptions.ProxyType = ProxyType.Auto; - break; - - case ProxyAccessType.IEConfig: - sessionOptions.ProxyType = ProxyType.InternetExplorer; - break; - } - } - - PSCredential pscreds = PSCredential.Get(context); - string certificateThumbprint = PSCertificateThumbprint.Get(context); - - if (pscreds != null && certificateThumbprint != null) - { - throw new ArgumentException(Resources.CredentialParameterCannotBeSpecifiedWithPSCertificateThumbPrint); - } - - PasswordAuthenticationMechanism passwordAuthenticationMechanism = PasswordAuthenticationMechanism.Default; - AuthenticationMechanism? authenticationMechanism = PSAuthentication.Get(context); - - if (authenticationMechanism.HasValue) - passwordAuthenticationMechanism = ConvertPSAuthenticationMechanismToCimPasswordAuthenticationMechanism(authenticationMechanism.Value); - - - if (certificateThumbprint != null) - { - sessionOptions.AddDestinationCredentials(new CimCredential(CertificateAuthenticationMechanism.Default, certificateThumbprint)); - } - - if (pscreds != null) - { - string[] parts = pscreds.UserName.Split('\\'); - string domain, userid; - if (parts.Length < 2) - { - domain = string.Empty; - userid = parts[0]; - } - else - { - domain = parts[0]; - userid = parts[1]; - } - - sessionOptions.AddDestinationCredentials(new CimCredential(passwordAuthenticationMechanism, domain, userid, pscreds.Password)); - } - - // Create the PowerShell instance, and add the script to it. - if (sessions != null && sessions.Length > 0) - { - foreach (CimSession session in sessions) - { - ActivityImplementationContext configuredCommand = GetPowerShell(context); - - CimActivityImplementationContext activityImplementationContext = - new CimActivityImplementationContext( - configuredCommand, - session.ComputerName, - pscreds, - certificateThumbprint, - authenticationMechanism, - sessionOptionUseSsl, - sessionOptionPort, - pso, - session, - sessionOptions, - ModuleDefinition, - resourceUri); - - commands.Add(activityImplementationContext); - //if (needRunspace) - // GetRunspaceForCimCmdlet(context, activityImplementationContext); - } - } - else if (this.PSCommandName.Equals("CimCmdlets\\New-CimSession", StringComparison.OrdinalIgnoreCase)) - { - // NewCimSession activity is a special one as it creates the required sessions based on number of computers specified in one go. - - ActivityImplementationContext baseContext = GetPowerShell(context); - - CimActivityImplementationContext activityImplementationContext = - new CimActivityImplementationContext(baseContext, - null, // ComputerName - pscreds, - certificateThumbprint, - authenticationMechanism, - sessionOptionUseSsl, - sessionOptionPort, - pso, - null, // session - sessionOptions, - ModuleDefinition, - resourceUri); - - commands.Add(activityImplementationContext); - } - else - { - foreach (string computer in computernames) - { - ActivityImplementationContext baseContext = GetPowerShell(context); - - CimActivityImplementationContext activityImplementationContext = - new CimActivityImplementationContext(baseContext, - computer, - pscreds, - certificateThumbprint, - authenticationMechanism, - sessionOptionUseSsl, - sessionOptionPort, - pso, - null, // session - sessionOptions, - ModuleDefinition, - resourceUri); - - commands.Add(activityImplementationContext); - } - } - } - // Configure the local invocation options - else - { - // Create the PowerShell instance, and add the script to it. - ActivityImplementationContext baseContext = GetPowerShell(context); - CimActivityImplementationContext activityImplementationContext = - new CimActivityImplementationContext(baseContext, - null, // ComputerName - null, // Credential - null, // CertificateThumbprint - AuthenticationMechanism.Default, - false, // UseSsl - 0, // Port - null, // PSSessionOption - null, // Session - null, // CimSessionOptions - ModuleDefinition, - resourceUri); - - commands.Add(activityImplementationContext); - } - - return commands; - } - - internal static PasswordAuthenticationMechanism ConvertPSAuthenticationMechanismToCimPasswordAuthenticationMechanism(AuthenticationMechanism psAuthenticationMechanism) - { - switch (psAuthenticationMechanism) - { - case AuthenticationMechanism.Basic: - return PasswordAuthenticationMechanism.Basic; - - case AuthenticationMechanism.Negotiate: - case AuthenticationMechanism.NegotiateWithImplicitCredential: - return PasswordAuthenticationMechanism.Negotiate; - - case AuthenticationMechanism.Credssp: - return PasswordAuthenticationMechanism.CredSsp; - - case AuthenticationMechanism.Digest: - return PasswordAuthenticationMechanism.Digest; - - case AuthenticationMechanism.Kerberos: - return PasswordAuthenticationMechanism.Kerberos; - - case AuthenticationMechanism.Default: - default: - return PasswordAuthenticationMechanism.Default; - } - } - } - - /// - /// Base class for the built-in generic CIM cmdlets. - /// - public abstract class GenericCimCmdletActivity : PSGeneratedCIMActivity - { - /// - /// For these cmdlets, there is no defining text. - /// - protected override string ModuleDefinition { get { return string.Empty; } } - - /// - /// The .NET type that implements the associated cmdlet - /// - public abstract Type TypeImplementingCmdlet { get; } - } - - - /// - /// Provides additional functionality for CIM activity implementations. - /// - public class CimActivityImplementationContext : ActivityImplementationContext - { - /// - /// Create an instance of the CIM activity implementation class - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public CimActivityImplementationContext( - ActivityImplementationContext activityImplementationContext, - string computerName, - PSCredential credential, - string certificateThumbprint, - AuthenticationMechanism? authenticationMechanism, - bool useSsl, - uint port, - PSSessionOption sessionOption, - CimSession session, - CimSessionOptions cimSessionOptions, - string moduleDefinition, - Uri resourceUri) - { - if (activityImplementationContext == null) - { - throw new ArgumentNullException("activityImplementationContext"); - } - this.PowerShellInstance = activityImplementationContext.PowerShellInstance; - ResourceUri = resourceUri; - ComputerName = computerName; - PSCredential = credential; - PSCertificateThumbprint = certificateThumbprint; - PSAuthentication = authenticationMechanism; - PSUseSsl = useSsl; - PSPort = port; - PSSessionOption = sessionOption; - Session = session; - SessionOptions = cimSessionOptions; - if (moduleDefinition != null) - { - // Creating a script block forces the string into the compiled script cache so we - // don't need to reparse it at execution time. Locking the static _moduleDefinition is not - // required since the operation is idempotent. - _moduleScriptBlock = ScriptBlock.Create(moduleDefinition); - _moduleDefinition = moduleDefinition; - } - } - - /// - /// Gets the scriptblock that implements this activity's command - /// - public ScriptBlock ModuleScriptBlock - { - get { return _moduleScriptBlock; } - } - private static ScriptBlock _moduleScriptBlock; - - /// - /// Defines the resource URI used by the CIM cmdlet. - /// - public Uri ResourceUri { get; set; } - - /// - /// The session specified for this activity - /// - public CimSession Session { get; set; } - - /// - /// The session options used to create the session for this activity... - /// - public CimSessionOptions SessionOptions { get; set; } - - /// - /// The name of the computer this activity targets - /// - public string ComputerName { get; set; } - - /// - /// Base64 encoded string defining this module... - /// - public string ModuleDefinition { get { return _moduleDefinition; } } - string _moduleDefinition; - - /// - /// Return the session to the session manager - /// - public override void CleanUp() - { - if (Session != null && !string.IsNullOrEmpty(ComputerName)) - { - CimConnectionManager.GetGlobalCimConnectionManager().ReleaseSession(ComputerName, Session); - Session = null; - ComputerName = null; - } - } - } - - - /// - /// Class to manage reuse of CIM connections. - /// - internal class CimConnectionManager - { - private readonly System.Timers.Timer _cleanupTimer = new System.Timers.Timer(); - private bool firstTime = true; - internal CimConnectionManager() - { - _cleanupTimer.Elapsed += HandleCleanupTimerElapsed; - _cleanupTimer.AutoReset = true; - _cleanupTimer.Interval = 20*1000; - _cleanupTimer.Start(); - } - - private void HandleCleanupTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) - { - if (firstTime) - { - // this is to enable a start delay of 20 seconds - firstTime = false; - return; - } - - lock (SyncRoot) - { - List computersToRemove = new List(); - foreach (var pair in availableSessions) - { - List sel = pair.Value; - if (sel.Count == 0) - { - computersToRemove.Add(pair.Key); - } - else - { - for (int i = sel.Count - 1; i >= 0; i--) - { - // If this connection in use, then skip it - if (sel[i].GetReferenceCount > 0) - continue; - - if (--sel[i].IterationsRemaining <= 0) - { - sel[i].Session.Close(); - sel.RemoveAt(i); - } - } - } - } - } - } - - // Number of scan cycles left until this session entry will be removed. - internal const int MaxIterations = 6; - - internal const int MaxCimSessionsUpperLimit = 500; - internal const int MaxCimSessionsLowerLimit = 1; - - Dictionary> availableSessions = new Dictionary>(); - - private class SessionEntry - { - public int IterationsRemaining = MaxIterations; - public CimSessionOptions SessionOptions; - public CimSession Session; - - public void AddReference() - { - Interlocked.Add(ref _numberOfUses, 1); - } - - public void RemoveReference() - { - Interlocked.Decrement(ref _numberOfUses); - } - int _numberOfUses; - - public int GetReferenceCount - { - get { return _numberOfUses; } - } - - public PSCredential Credential - { - get { return _credential; } - } - private PSCredential _credential; - - public bool UseSsl - { - get - { - return _useSsl; - } - } - private bool _useSsl; - - public uint Port - { - get - { - return _port; - } - } - private uint _port; - - - public PSSessionOption PSSessionOption - { - get - { - return _psSessionOption; - } - } - private PSSessionOption _psSessionOption; - - public string CertificateThumbprint - { - get - { - return _certificateThumbprint; - } - } - private string _certificateThumbprint; - - public AuthenticationMechanism AuthenticationMechanism - { - get - { - return _authenticationMechanism; - } - } - private AuthenticationMechanism _authenticationMechanism; - - public SessionEntry(string computerName, PSCredential credential, string certificateThumbprint, AuthenticationMechanism authenticationMechanism, CimSessionOptions sessionOptions, bool useSsl, uint port, PSSessionOption pssessionOption) - { - SessionOptions = sessionOptions; - _credential = credential; - _certificateThumbprint = certificateThumbprint; - _authenticationMechanism = authenticationMechanism; - _useSsl = useSsl; - _port = port; - _psSessionOption = pssessionOption; - Session = CimSession.Create(computerName, sessionOptions); - } - } - - object SyncRoot = new object(); - - /// - /// Get a CIM session for the target computer - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal CimSession GetSession(string computerName, PSCredential credential, string certificateThumbprint, AuthenticationMechanism authenticationMechanism, CimSessionOptions sessionOptions, bool useSsl, uint port, PSSessionOption pssessionOption) - { - System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(computerName), "ComputerName is null in GetSession. GetSession should not be called in this case."); - lock (SyncRoot) - { - SessionEntry newSessionEntry; - - if (availableSessions.ContainsKey(computerName)) - { - - List sel = availableSessions[computerName]; - if (sel.Count > 0) - { - for (int i = 0; i < sel.Count; i++) - { - SessionEntry se = sel[i]; - - // No session options specified or the object matches exactly... - if ((se.SessionOptions == null && sessionOptions == null) || CompareSessionOptions(se, sessionOptions, credential, certificateThumbprint, authenticationMechanism, useSsl, port, pssessionOption)) - { - // Up the number of references to this session object... - se.AddReference(); - return se.Session; - } - } - } - } - - - // Allocate a new session entry for this computer - - newSessionEntry = new SessionEntry(computerName, credential, certificateThumbprint, authenticationMechanism, sessionOptions, useSsl, port, pssessionOption); - newSessionEntry.IterationsRemaining = MaxIterations; - newSessionEntry.AddReference(); - if (! availableSessions.ContainsKey(computerName)) - { - availableSessions.Add(computerName, new List()); - } - - availableSessions[computerName].Add(newSessionEntry); - - // Return the session object - return newSessionEntry.Session; - } - } - - - - /// - /// Return a session to the session table. - /// - /// - /// - internal void ReleaseSession(string computerName, CimSession session) - { - System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(computerName), "ComputerName is null in ReleaseSession. ReleaseSession should not be called in this case."); - lock (SyncRoot) - { - if (availableSessions.ContainsKey(computerName)) - { - foreach (var se in availableSessions[computerName]) - { - if (se.Session == session) - { - se.RemoveReference(); - } - } - } - } - } - - private static bool CompareSessionOptions(SessionEntry sessionEntry, CimSessionOptions options2, PSCredential credential2, string certificateThumbprint, AuthenticationMechanism authenticationMechanism, bool useSsl, uint port, PSSessionOption pssessionOption) - { - if (!sessionEntry.SessionOptions.Timeout.Equals(options2.Timeout)) - return false; - - if (!string.Equals(sessionEntry.SessionOptions.Culture.ToString(), options2.Culture.ToString(), StringComparison.OrdinalIgnoreCase)) - return false; - - if (!string.Equals(sessionEntry.SessionOptions.UICulture.ToString(), options2.UICulture.ToString(), StringComparison.OrdinalIgnoreCase)) - return false; - - if (!string.Equals(sessionEntry.CertificateThumbprint, certificateThumbprint, StringComparison.OrdinalIgnoreCase)) - return false; - - if (sessionEntry.AuthenticationMechanism != authenticationMechanism) - return false; - - if (!Workflow.WorkflowUtils.CompareCredential(sessionEntry.Credential, credential2)) - return false; - - if (sessionEntry.UseSsl != useSsl) - return false; - - if (sessionEntry.Port != port) - return false; - - - // check PSSessionOption if present - if (pssessionOption == null ^ sessionEntry.PSSessionOption == null) - { - return false; - } - - if (pssessionOption != null && sessionEntry.PSSessionOption != null) - { - if (sessionEntry.PSSessionOption.ProxyAccessType != pssessionOption.ProxyAccessType) - return false; - - if (sessionEntry.PSSessionOption.ProxyAuthentication != pssessionOption.ProxyAuthentication) - return false; - - if (!Workflow.WorkflowUtils.CompareCredential(sessionEntry.PSSessionOption.ProxyCredential, pssessionOption.ProxyCredential)) - return false; - - if (sessionEntry.PSSessionOption.SkipCACheck != pssessionOption.SkipCACheck) - return false; - - if (sessionEntry.PSSessionOption.SkipCNCheck != pssessionOption.SkipCNCheck) - return false; - - if (sessionEntry.PSSessionOption.SkipRevocationCheck != pssessionOption.SkipRevocationCheck) - return false; - - if (sessionEntry.PSSessionOption.NoEncryption != pssessionOption.NoEncryption) - return false; - - if (sessionEntry.PSSessionOption.UseUTF16 != pssessionOption.UseUTF16) - return false; - } - - return true; - } - - /// - /// Gets the global instance of the CIM session manager - /// - /// - public static CimConnectionManager GetGlobalCimConnectionManager() - { - lock (gcmLock) - { - if (_globalConnectionManagerInstance == null) - { - _globalConnectionManagerInstance = new CimConnectionManager(); - } - return _globalConnectionManagerInstance; - } - } - static CimConnectionManager _globalConnectionManagerInstance; - static object gcmLock = new object(); - } -} - diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSWorkflowHost.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSWorkflowHost.cs deleted file mode 100644 index bc8da6838a8..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSWorkflowHost.cs +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (c) 2010 Microsoft Corporation. All rights reserved - */ -using System; -using System.Management.Automation.Runspaces; -using System.Reflection; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - - #region PSWorkflowHost - - /// - /// Interface that defines the PowerShell workflow host - /// Workflow host defines the set of services that are - /// made available to an activity - /// - public abstract class PSWorkflowHost - { - /// - /// The activity host manager to use for processing - /// activities - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSActivityHostController PSActivityHostController - { - get - { - throw new NotImplementedException(); - } - } - - /// - /// The provider to be used for obtaining runspaces - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual RunspaceProvider RemoteRunspaceProvider - { - get - { - throw new NotImplementedException(); - } - } - - /// - /// The provider used to obtain local in-proc - /// runspaces - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual RunspaceProvider LocalRunspaceProvider - { - get { throw new NotImplementedException(); } - } - - /// - /// The provider which will supply an unbounded number of - /// runspaces - to be used in PowerShell value - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual RunspaceProvider UnboundedLocalRunspaceProvider - { - get { throw new NotImplementedException(); } - } - } - - #endregion PSWorkflowHost - - - #region RunspaceProvider - - /// - /// Class that provides a runspace with the specified options - /// and constraints - /// - public abstract class RunspaceProvider - { - /// - /// Begin for obtaining a runspace for the specified ConnectionInfo - /// - /// connection info to be used for remote connections - /// number of times to retry - /// optional user defined callback - /// optional user specified state - /// time in milliseconds before the next retry has to be attempted - /// - /// async result - public virtual IAsyncResult BeginGetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval, AsyncCallback callback, object state) - { - throw new NotImplementedException(); - } - - /// - /// End for obtaining a runspace for the specified connection info - /// - /// async result to end on - /// - /// remote runspace to invoke commands on - public virtual Runspace EndGetRunspace(IAsyncResult asyncResult) - { - throw new NotImplementedException(); - } - - /// - /// Get runspace for the specified connection info to be - /// used for running commands - /// - /// connection info to use - /// retry count - /// retry interval in ms - /// remote runspace to use - public virtual Runspace GetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval) - { - throw new NotImplementedException(); - } - - /// - /// Release the runspace once the activity is finished using the same - /// - /// runspace to release - public virtual void ReleaseRunspace(Runspace runspace) - { - throw new NotImplementedException(); - } - - /// - /// Callback to indicate that this runspace been initiated with - /// a pipeline and can be disconnected - /// - /// runspace that needs to be marked as - /// ready for disconnect - public virtual void ReadyForDisconnect(Runspace runspace) - { - throw new NotImplementedException(); - } - - /// - /// Request a cleanup to the destination specified in the - /// connection info. This means no runspaces will be held - /// to the specified connection info. - /// - /// connection info to which - /// cleanup is desired - ///callback to invoke - /// caller specified state - public virtual void RequestCleanup(WSManConnectionInfo connectionInfo, WaitCallback callback, object state) - { - throw new NotImplementedException(); - } - - /// - /// Checks to see if the provider intentionally disconnected a runspace - /// or it went into disconnected state due to network issues - /// - /// runspace that needs to be checked - /// true - when intentionally disconnected - /// false - disconnected due to network issues - public virtual bool IsDisconnectedByRunspaceProvider(Runspace runspace) - { - throw new NotImplementedException(); - } - } - - #endregion RunspaceProvider - - - #region DefaultWorkflowHost - - /// - /// Default workflow host implementation - /// - internal class DefaultWorkflowHost : PSWorkflowHost - { - private readonly PSActivityHostController _activityHostController; - private readonly RunspaceProvider _runspaceProvider; - private static readonly DefaultWorkflowHost _instance = new DefaultWorkflowHost(); - private readonly RunspaceProvider _localRunspaceProvider; - - private DefaultWorkflowHost() - { - const string applicationPrivateData = @" - - - -"; - var runtime = PSWorkflowRuntime.Instance; - runtime.Configuration.Populate(applicationPrivateData, "Microsoft.PowerShell.Workflow"); - _activityHostController = runtime.PSActivityHostController; - _runspaceProvider = runtime.RemoteRunspaceProvider; - _localRunspaceProvider = runtime.LocalRunspaceProvider; - } - - /// - /// The activity host manager to use for processing - /// activities - /// - public override PSActivityHostController PSActivityHostController - { - get - { - return _activityHostController; - } - } - - /// - /// The provider to be used for obtaining runspaces - /// - public override RunspaceProvider RemoteRunspaceProvider - { - get { return _runspaceProvider; } - } - - /// - /// - /// - public override RunspaceProvider LocalRunspaceProvider - { - get - { - return _localRunspaceProvider; - } - } - - /// - /// return the singleton instance - /// - internal static DefaultWorkflowHost Instance - { - get { return _instance; } - } - - internal void ResetLocalRunspaceProvider() - { - MethodInfo methodInfo = _localRunspaceProvider.GetType().GetMethod("Reset", BindingFlags.NonPublic | BindingFlags.Instance); - methodInfo.Invoke(_localRunspaceProvider, new object[]{}); - } - } - - #endregion DefaultWorkflowHost - -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PowerShellWorkflowHost.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PowerShellWorkflowHost.cs deleted file mode 100644 index b3ebd1f475f..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PowerShellWorkflowHost.cs +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2010 Microsoft Corporation. All rights reserved - */ - -using Microsoft.PowerShell.Activities; -using System.Management.Automation.PerformanceData; -using System.Management.Automation.Tracing; -using System.Threading; -using System; -using System.Management.Automation; -using System.Globalization; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// PowerShell Workflow host runtime. - /// - public class PSWorkflowRuntime : PSWorkflowHost, IDisposable - { - private static PSWorkflowRuntime powerShellWorkflowHostInstance; - private static readonly object syncLock = new object(); - private RunspaceProvider _runspaceProvider; - private RunspaceProvider _localRunspaceProvider; - private RunspaceProvider _unboundedLocalRunspaceProvider; - - private static readonly Tracer _tracer = new Tracer(); - private static readonly PSPerfCountersMgr _psPerfCountersMgrInst = PSPerfCountersMgr.Instance; - private readonly PSWorkflowConfigurationProvider _configuration; - private readonly object _syncObject = new object(); - - private PSWorkflowJobManager jobManager; - private PSActivityHostController activityHostController; - - private bool _isDisposed; - - /// - /// Default constructor - /// - public PSWorkflowRuntime() - { - _configuration = new PSWorkflowConfigurationProvider(); - _configuration.Runtime = this; - PSCounterSetRegistrar registrar = - new PSCounterSetRegistrar( - PSWorkflowPerformanceCounterSetInfo.ProviderId, - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterSetInfo.CounterSetType, - PSWorkflowPerformanceCounterSetInfo.CounterInfoArray); - _psPerfCountersMgrInst.AddCounterSetInstance(registrar); - - // Enable caching module paths appdomain-wide. - System.Management.Automation.PSModuleInfo.UseAppDomainLevelModuleCache = true; - } - - /// - /// Constructs runtime based on configuration - /// - /// - public PSWorkflowRuntime(PSWorkflowConfigurationProvider configuration) - { - if (configuration == null) - throw new ArgumentNullException("configuration"); - - // Only allow FullLanguage or ConstraintLanguage or it can be null in that case system wide default will take an affect - PSLanguageMode? langMode = configuration.LanguageMode; - if (langMode != null && langMode.HasValue && (langMode.Value == PSLanguageMode.NoLanguage || langMode.Value == PSLanguageMode.RestrictedLanguage)) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.NotSupportedLanguageMode, langMode.Value.ToString())); - } - - _configuration = configuration; - _configuration.Runtime = this; - } - - /// - /// Dispose implementation. - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose implementation. - /// - /// - private void Dispose(bool disposing) - { - if (_isDisposed || !disposing) - return; - - lock (_syncObject) - { - if (_isDisposed) - return; - - // Suspend all running jobs then dispose all runspace providers - if (jobManager != null) - jobManager.Dispose(); - jobManager = null; - - var remoteRunspaceProvider = _runspaceProvider as ConnectionManager; - if (remoteRunspaceProvider != null) - remoteRunspaceProvider.Dispose(); - _runspaceProvider = null; - - var localRunspaceProvider = _localRunspaceProvider as LocalRunspaceProvider; - if (localRunspaceProvider != null) - localRunspaceProvider.Dispose(); - _localRunspaceProvider = null; - - var unboundedLocalRunspaceProvider = _localRunspaceProvider as LocalRunspaceProvider; - if (unboundedLocalRunspaceProvider != null) - unboundedLocalRunspaceProvider.Dispose(); - _unboundedLocalRunspaceProvider = null; - - activityHostController = null; - - _isDisposed = true; - } - } - - private void AssertNotDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException("PSWorkflowRuntime"); - } - } - - internal static PSWorkflowRuntime Instance - { - get - { - if (powerShellWorkflowHostInstance == null) - { - lock (syncLock) - { - if (powerShellWorkflowHostInstance == null) - { - powerShellWorkflowHostInstance = new PSWorkflowRuntime(); - } - } - } - return powerShellWorkflowHostInstance; - } - } - - internal Tracer Tracer - { - get - { - return _tracer; - } - } - - #region Test Helpers - - internal void SetWorkflowJobManager(PSWorkflowJobManager wfJobManager) - { - lock (syncLock) - { - jobManager = wfJobManager; - } - } - - #endregion Test Helpers - - /// - /// The runtime configuration. - /// - public virtual PSWorkflowConfigurationProvider Configuration - { - get - { - return _configuration; - } - } - - /// - /// JobManager - /// - public virtual PSWorkflowJobManager JobManager - { - get - { - if (jobManager != null) - return jobManager; - - lock (syncLock) - { - if (jobManager != null) - return jobManager; - - AssertNotDisposed(); - jobManager = new PSWorkflowJobManager(this, _configuration.MaxRunningWorkflows); - } - return jobManager; - } - } - - - /// - /// PSActivityHostController (PSWorkflowHost) - /// - public override PSActivityHostController PSActivityHostController - { - get - { - if (activityHostController != null) - return activityHostController; - - lock (syncLock) - { - if (activityHostController != null) - return activityHostController; - - AssertNotDisposed(); - activityHostController = _configuration.CreatePSActivityHostController(); - } - return activityHostController; - } - } - - /// - /// RemoteRunspaceProvider (PSWorkflowHost) - /// - public override RunspaceProvider RemoteRunspaceProvider - { - get - { - if (_runspaceProvider == null) - { - lock (_syncObject) - { - if (_runspaceProvider == null) - { - AssertNotDisposed(); - _runspaceProvider = _configuration.CreateRemoteRunspaceProvider(); - } - } - } - return _runspaceProvider; - } - } - - /// - /// LocalRunspaceProvider (PSWorkflowHost) - /// - public override RunspaceProvider LocalRunspaceProvider - { - get - { - if (_localRunspaceProvider == null) - { - lock (_syncObject) - { - if (_localRunspaceProvider == null) - { - AssertNotDisposed(); - _localRunspaceProvider = _configuration.CreateLocalRunspaceProvider(false); - } - } - } - - return _localRunspaceProvider; - } - } - - /// - /// The provider which will supply an unbounded number of - /// runspaces - to be used in PowerShell value - /// - public override RunspaceProvider UnboundedLocalRunspaceProvider - { - get - { - if (_unboundedLocalRunspaceProvider == null) - { - lock(_syncObject) - { - if (_unboundedLocalRunspaceProvider == null) - { - AssertNotDisposed(); - _unboundedLocalRunspaceProvider = _configuration.CreateLocalRunspaceProvider(true); - } - } - } - - return _unboundedLocalRunspaceProvider; - } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/RuntimeBase.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/RuntimeBase.cs deleted file mode 100644 index 8bfe23839a4..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/RuntimeBase.cs +++ /dev/null @@ -1,834 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.Activities; -using System.Activities.Validation; -using System.Runtime.DurableInstancing; -using System.Collections.Generic; -using System.Management.Automation; -using System.Collections.ObjectModel; -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using Microsoft.PowerShell.Activities; -using System.Diagnostics; -using System.Reflection; -using System.Globalization; -using System.Runtime.Serialization; -using System.Activities.Persistence; -using System.Activities.Hosting; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// WorkflowStoreComponent - /// - [Flags] - public enum WorkflowStoreComponents - { - /// - /// Streams - /// - Streams = 1, - - /// - /// Metadata - /// - Metadata = 2, - - /// - /// Definition - /// - Definition = 4, - - /// - /// Timers - /// - Timer = 8, - - /// - /// JobState - /// - JobState = 16, - - /// - /// TerminatingError - /// - TerminatingError = 32, - - /// - /// ActivityState, like remote activity execution state, etc. - /// - ActivityState = 64, - } - - /// - /// ActivityRunMode - /// - public enum ActivityRunMode - { - /// - /// InProcess - /// - InProcess = 0, - - /// - /// OutOfProcess - /// - OutOfProcess = 1, - } - - /// - /// WorkflowInstanceStore - /// - public abstract class PSWorkflowInstanceStore - { - /// - /// PSWorkflowInstanceStore - /// - /// - protected PSWorkflowInstanceStore(PSWorkflowInstance workflowInstance) - { - if (workflowInstance == null) - throw new ArgumentNullException("workflowInstance"); - - PSWorkflowInstance = workflowInstance; - } - - /// - /// PSWorkflowInstance - /// - public PSWorkflowInstance PSWorkflowInstance - { - get; - private set; - } - - /// - /// CreatePersistenceIOParticipant - /// - /// - public abstract PersistenceIOParticipant CreatePersistenceIOParticipant(); - - /// - /// CreateInstanceStore - /// - /// - public abstract InstanceStore CreateInstanceStore(); - - #region Save Methods - - /// - /// Save - /// - /// - public void Save(WorkflowStoreComponents components) - { - this.Save(components, null); - } - - internal void Save(WorkflowStoreComponents components, Dictionary WorkflowContext) - { - Collection componentsToSave = new Collection(); - - if ((components & WorkflowStoreComponents.JobState) == WorkflowStoreComponents.JobState) - { - componentsToSave.Add(PSWorkflowInstance.State); - } - - if (WorkflowContext != null) - { - componentsToSave.Add(WorkflowContext); - } - - if (((components & WorkflowStoreComponents.Definition) == WorkflowStoreComponents.Definition) && - (PSWorkflowInstance.PSWorkflowDefinition != null)) - { - componentsToSave.Add(PSWorkflowInstance.PSWorkflowDefinition); - } - - if (((components & WorkflowStoreComponents.TerminatingError) == WorkflowStoreComponents.TerminatingError) && - (PSWorkflowInstance.Error != null)) - { - if (!WorkflowJobSourceAdapter.GetInstance().IsShutdownInProgress) - { - if (PSWorkflowInstance.Error.GetType() != typeof(RemoteException)) - { - componentsToSave.Add(PSWorkflowInstance.Error); - } - } - } - - if (((components & WorkflowStoreComponents.Metadata) == WorkflowStoreComponents.Metadata) && - (PSWorkflowInstance.PSWorkflowContext != null)) - { - componentsToSave.Add(PSWorkflowInstance.PSWorkflowContext); - } - - if (((components & WorkflowStoreComponents.Streams) == WorkflowStoreComponents.Streams) && - (PSWorkflowInstance.Streams != null)) - { - componentsToSave.Add(PSWorkflowInstance.Streams); - } - - if (((components & WorkflowStoreComponents.ActivityState) == WorkflowStoreComponents.ActivityState) && - (PSWorkflowInstance.RemoteActivityState != null)) - { - componentsToSave.Add(PSWorkflowInstance.RemoteActivityState); - } - - if (((components & WorkflowStoreComponents.Timer) == WorkflowStoreComponents.Timer) && - PSWorkflowInstance.Timer != null) - { - componentsToSave.Add(PSWorkflowInstance.Timer); - } - - DoSave(componentsToSave); - } - - /// - /// DoSave - /// - /// - protected abstract void DoSave(IEnumerable components); - - #endregion Save - - #region Load Methods - - /// - /// Load - /// - /// - public void Load(WorkflowStoreComponents components) - { - Collection componentsToLoad = new Collection(); - - if ((components & WorkflowStoreComponents.JobState) == WorkflowStoreComponents.JobState) - { - componentsToLoad.Add(typeof(JobState)); - PSWorkflowInstance.JobStateRetrieved = false; - } - - if ((components & WorkflowStoreComponents.Definition) == WorkflowStoreComponents.Definition) - { - componentsToLoad.Add(typeof(PSWorkflowDefinition)); - PSWorkflowInstance.PSWorkflowDefinition = null; - } - - if ((components & WorkflowStoreComponents.TerminatingError) == WorkflowStoreComponents.TerminatingError) - { - componentsToLoad.Add(typeof(Exception)); - PSWorkflowInstance.Error = null; - } - - if ((components & WorkflowStoreComponents.Metadata) == WorkflowStoreComponents.Metadata) - { - componentsToLoad.Add(typeof(PSWorkflowContext)); - PSWorkflowInstance.PSWorkflowContext = null; - } - - if ((components & WorkflowStoreComponents.Streams) == WorkflowStoreComponents.Streams) - { - componentsToLoad.Add(typeof(PowerShellStreams)); - PSWorkflowInstance.Streams = null; - } - - if ((components & WorkflowStoreComponents.ActivityState) == WorkflowStoreComponents.ActivityState) - { - componentsToLoad.Add(typeof(PSWorkflowRemoteActivityState)); - PSWorkflowInstance.RemoteActivityState = null; - } - - if ((components & WorkflowStoreComponents.Timer) == WorkflowStoreComponents.Timer) - { - componentsToLoad.Add(typeof(PSWorkflowTimer)); - PSWorkflowInstance.Timer = null; - } - - IEnumerable loadedComponents = DoLoad(componentsToLoad); - - foreach (object loadedComponent in loadedComponents) - { - Type componentType = loadedComponent.GetType(); - - if (componentType == typeof(JobState)) - { - PSWorkflowInstance.State = (JobState)loadedComponent; - PSWorkflowInstance.JobStateRetrieved = true; - } - else if (componentType == typeof(PSWorkflowDefinition)) - { - PSWorkflowInstance.PSWorkflowDefinition = (PSWorkflowDefinition)loadedComponent; - } - else if (loadedComponent is Exception) - { - PSWorkflowInstance.Error = (Exception)loadedComponent; - } - else if (componentType == typeof(PSWorkflowContext)) - { - PSWorkflowInstance.PSWorkflowContext = (PSWorkflowContext)loadedComponent; - } - else if (componentType == typeof(PowerShellStreams)) - { - PSWorkflowInstance.Streams = (PowerShellStreams)loadedComponent; - } - else if (componentType == typeof(PSWorkflowTimer)) - { - PSWorkflowInstance.Timer = (PSWorkflowTimer)loadedComponent; - } - else if (componentType == typeof(PSWorkflowRemoteActivityState)) - { - PSWorkflowInstance.RemoteActivityState = (PSWorkflowRemoteActivityState)loadedComponent; - } - } - } - - internal Dictionary LoadWorkflowContext() - { - Dictionary workflowContext = null; - - Collection componentsToLoad = new Collection(); - componentsToLoad.Add(typeof(Dictionary)); - - IEnumerable loadedComponents = DoLoad(componentsToLoad); - - foreach (object loadedComponent in loadedComponents) - { - Type componentType = loadedComponent.GetType(); - - if (componentType == typeof(Dictionary)) - { - workflowContext = (Dictionary)loadedComponent; - } - } - - return workflowContext; - } - - /// - /// DoLoad - /// - /// - protected abstract IEnumerable DoLoad(IEnumerable componentTypes); - - #endregion Load Methods - - #region Delete Methods - - /// - /// Delete - /// - public void Delete() - { - DoDelete(); - } - - /// - /// DoDelete - /// - protected abstract void DoDelete(); - - #endregion Delete Methods - } - - - /// - /// WorkflowInstance - /// - public abstract class PSWorkflowInstance : IDisposable - { - #region Private Members - - /// - /// _syncLock - /// - private object _syncLock = new object(); - - #endregion Private Members - - #region Protected Members - - /// - /// _disposed - /// - protected bool Disposed - { - set; - get; - } - - /// - /// Synchronization object available to derived classes. - /// - protected object SyncLock - { - get - { - return _syncLock; - } - } - - - /// - /// DoStopInstance - /// - protected virtual void DoStopInstance() - { - throw new NotImplementedException(); - } - - /// - /// DoAbortInstance - /// - /// Reason for aborting workflow. - protected virtual void DoAbortInstance(string reason) - { - throw new NotImplementedException(); - } - - /// - /// DoTerminateInstance - /// - /// Reason message for termination - protected virtual void DoTerminateInstance(string reason) - { - throw new NotImplementedException(); - } - - /// - /// DoTerminateInstance - /// - /// Reason message for termination - /// Suppress error for termination - protected virtual void DoTerminateInstance(string reason, bool suppressError) - { - throw new NotImplementedException(); - } - - /// - /// DoResumeInstance - /// - protected virtual void DoResumeInstance(string label) - { - throw new NotImplementedException(); - } - - /// - /// DoSuspendInstance - /// - /// - protected virtual void DoSuspendInstance(bool notStarted) - { - throw new NotImplementedException(); - } - - /// - /// DoExecuteInstance - /// - protected virtual void DoExecuteInstance() - { - throw new NotImplementedException(); - } - - /// - /// DoResumeBookmark - /// - /// - /// - protected virtual void DoResumeBookmark(Bookmark bookmark, object state) - { - throw new NotImplementedException(); - } - - /// - /// Loads the xaml to create an executable activity. - /// - protected virtual void DoCreateInstance() - { - throw new NotImplementedException(); - } - - /// - /// Remove - /// - protected virtual void DoRemoveInstance() - { - throw new NotImplementedException(); - } - - - /// - /// DoPersistInstance - /// - protected virtual void DoPersistInstance() - { - throw new NotImplementedException(); - } - - /// - /// DoGetPersistableIdleAction - /// - /// - /// - /// - protected virtual PSPersistableIdleAction DoGetPersistableIdleAction(ReadOnlyCollection bookmarks, bool externalSuspendRequest) - { - throw new NotImplementedException(); - } - - /// - /// Dispose - /// - protected virtual void Dispose(bool disposing) - { - if (!disposing || Disposed) - return; - - lock (SyncLock) - { - if (Disposed) - return; - - Disposed = true; - - this.OnCompleted = null; - this.OnFaulted = null; - this.OnStopped = null; - this.OnAborted = null; - this.OnSuspended = null; - this.OnIdle = null; - this.OnPersistableIdleAction = null; - this.OnUnloaded = null; - } - } - #endregion Protected Members - - #region Internal Members - internal PSWorkflowRuntime Runtime { get; set; } - - internal void CreateInstance() { this.DoCreateInstance(); } - internal void ExecuteInstance() { this.DoExecuteInstance(); } - internal void SuspendInstance(bool notStarted) { this.DoSuspendInstance(notStarted); } - internal void RemoveInstance() { this.DoRemoveInstance(); } - internal void ResumeInstance(string label) { this.DoResumeInstance(label); } - internal void ResumeBookmark(Bookmark bookmark, object state) { this.DoResumeBookmark(bookmark, state); } - internal void StopInstance() { this.DoStopInstance(); } - internal void AbortInstance(string reason) { this.DoAbortInstance(reason); } - internal void TerminateInstance(string reason, bool suppressError) { this.DoTerminateInstance(reason, suppressError); } - internal void PersistInstance() { this.DoPersistInstance(); } - internal PSPersistableIdleAction GetPersistableIdleAction(ReadOnlyCollection bookmarks, bool externalSuspendRequest) { return this.DoGetPersistableIdleAction(bookmarks, externalSuspendRequest); } - - /// - /// Gets the Guid of workflow instance. - /// - internal virtual Guid Id - { - get { return this.InstanceId.Guid; } - } - - /// - /// JobStateRetrieved - /// - internal bool JobStateRetrieved { get; set; } - - internal bool ForceDisableStartOrEndPersistence { get; set; } - - /// - /// CheckForTerminalAction - /// - internal virtual void CheckForTerminalAction() - { - throw new NotImplementedException(); - } - - /// - /// Load instance for resuming the workflow - /// - internal virtual void DoLoadInstanceForReactivation() - { - throw new NotImplementedException(); - } - - /// - /// PerformTaskAtTerminalState - /// - internal virtual void PerformTaskAtTerminalState() - { - throw new NotImplementedException(); - } - - /// - /// On completed handler. - /// - internal Action OnCompletedDelegate - { - set { this.OnCompleted = value; } - } - - /// - /// On faulted handler. - /// - internal Action OnFaultedDelegate - { - set { this.OnFaulted = value; } - } - - /// - /// On stopped handler. - /// - internal Action OnStoppedDelegate - { - set { this.OnStopped = value; } - } - - /// - /// On aborted handler. - /// - internal Action OnAbortedDelegate - { - set { this.OnAborted = value; } - } - - /// - /// On suspended handler. - /// - internal Action OnSuspendedDelegate - { - set { this.OnSuspended = value; } - } - - /// - /// On idle handler. - /// - internal Action, object> OnIdleDelegate - { - set { this.OnIdle = value; } - } - - - /// - /// On persistable idle action handler. - /// - internal Func, bool, object, PSPersistableIdleAction> OnPersistableIdleActionDelegate - { - set { this.OnPersistableIdleAction = value; } - } - - /// - /// On unloaded handler. - /// - internal Action OnUnloadedDelegate - { - set { this.OnUnloaded = value; } - } - - /// - /// Validate label - /// - /// - internal virtual void ValidateIfLabelExists(string label) - { - throw new NotImplementedException(); - } - - internal virtual bool SaveStreamsIfNecessary() - { - return false; - } - - #endregion Internal Members - - - #region protected delegate members - - /// - /// On completed handler. - /// - protected Action OnCompleted { get; private set; } - - /// - /// On faulted handler. - /// - protected Action OnFaulted { get; private set; } - - /// - /// On stopped handler. - /// - protected Action OnStopped { get; private set; } - - /// - /// On aborted handler. - /// - protected Action OnAborted { get; private set; } - - /// - /// On suspended handler. - /// - protected Action OnSuspended { get; private set; } - - /// - /// On idle handler. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "????")] - protected Action, object> OnIdle { get; private set; } - - /// - /// On persistable idle action handler. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "????")] - protected Func, bool, object, PSPersistableIdleAction> OnPersistableIdleAction { get; private set; } - - /// - /// On unloaded handler. - /// - protected Action OnUnloaded { get; private set; } - - #endregion protected delegate members - - - #region Public Members - - /// - /// PSWorkflowJob - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowJob PSWorkflowJob - { - get { throw new NotImplementedException();} - protected internal set { throw new NotImplementedException(); } - } - - /// - /// Gets the Guid of workflow instance. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowId InstanceId - { - get { throw new NotImplementedException(); } - } - - /// - /// Gets the workflow job creation context. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual Dictionary CreationContext - { - get { throw new NotImplementedException(); } - } - - /// - /// State - /// - public virtual JobState State - { - get; - set; - } - - /// - /// InstanceStore - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowInstanceStore InstanceStore - { - get{ throw new NotImplementedException(); } - } - - /// - /// Gets the definition of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowDefinition PSWorkflowDefinition - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - /// - /// Gets the streams of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PowerShellStreams Streams - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - /// - /// Gets/Sets the RemoteActivityState of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowRemoteActivityState RemoteActivityState - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - /// - /// Gets the streams of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Error")] - public virtual Exception Error - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - /// - /// Gets the timers of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowTimer Timer - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - /// - /// Gets the metadatas of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowContext PSWorkflowContext - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - /// - /// Gets the workflow debugger. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - internal virtual PSWorkflowDebugger PSWorkflowDebugger - { - get { throw new NotImplementedException(); } - } - - /// - /// Dispose - /// - public void Dispose() - { - if (Disposed) - return; - - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose the streams to save memory - /// - public virtual void DisposeStreams() - { - - } - - #endregion Public Members - - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/TimeBasedCache.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/TimeBasedCache.cs deleted file mode 100644 index 620e286b99e..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/TimeBasedCache.cs +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2011 Microsoft Corporation. All rights reserved - */ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.ObjectModel; -using System.Timers; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// Bounded cache which will clear items when not used for a while - /// - /// type of item to use - internal class TimeBasedCache : IEnumerable, IDisposable - { - private readonly int _timeoutInSeconds; - private readonly object _timerServicingSyncObject = new object(); - private readonly Timer _validationTimer; - private int _timerFired; - private const int TimerFired = 1; - private const int TimerReset = 0; - private readonly ConcurrentDictionary> _cache = new ConcurrentDictionary>(); - - /// - /// - /// - internal ConcurrentDictionary> Cache - { - get { return _cache; } - } - - /// - /// The consumer of this class should hold a lock - /// on this object when servicing requests and adding - /// to this cache - /// - internal object TimerServicingSyncObject - { - get { return _timerServicingSyncObject; } - } - - internal TimeBasedCache(int timeoutInSeconds) - { - _timeoutInSeconds = timeoutInSeconds; - _validationTimer = new Timer - { - AutoReset = true, - Interval = _timeoutInSeconds*1000, - Enabled = false - }; - _validationTimer.Elapsed += ValidationTimerElapsed; - _validationTimer.Start(); - } - - private void ValidationTimerElapsed(object sender, ElapsedEventArgs e) - { - // ensure that the servicing thread is done before proceeding - if (_timerFired == TimerFired) return; - - lock(TimerServicingSyncObject) - { - if (_timerFired == TimerFired) return; - _timerFired = TimerFired; - Collection> toRemove = new Collection>(); - - foreach(Item item in _cache.Values) - { - if (item.Idle) - toRemove.Add(item); - else if (!item.Busy) - item.Idle = true; - } - - foreach(Item item in toRemove) - { - Item removedItem; - _cache.TryRemove(item.InstanceId, out removedItem); - IDisposable disposable = removedItem.Value as IDisposable; - if (disposable == null) continue; - - //dispose and force early GC - disposable.Dispose(); - disposable = null; - removedItem = null; - } - - _timerFired = TimerReset; - } - } - - /// - /// - /// - internal void Add(Item item) - { - item.Busy = true; - item.Idle = false; - _cache.TryAdd(item.InstanceId, item); - } - - /// - /// - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// - /// - /// - protected void Dispose(bool disposing) - { - if (disposing) - { - _validationTimer.Dispose(); - } - } - - internal class CacheEnumerator : IEnumerator - { - private readonly ConcurrentDictionary> _cache; - private readonly IEnumerator _dictionaryEnumerator; - private Item _currentItem; - - internal CacheEnumerator(ConcurrentDictionary> cache) - { - _cache = cache; - _dictionaryEnumerator = _cache.Keys.GetEnumerator(); - } - - /// - /// Advances the enumerator to the next element of the collection. - /// - /// - /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. - /// - /// The collection was modified after the enumerator was created. 2 - public bool MoveNext() - { - if (_dictionaryEnumerator.MoveNext()) - { - Guid key = (Guid)_dictionaryEnumerator.Current; - _currentItem = default(Item); - _cache.TryGetValue(key, out _currentItem); - - if (_currentItem != null) - return true; - } - - return false; - } - - /// - /// Sets the enumerator to its initial position, which is before the first element in the collection. - /// - /// The collection was modified after the enumerator was created. 2 - public void Reset() - { - _dictionaryEnumerator.Reset(); - } - - /// - /// Gets the current element in the collection. - /// - /// - /// The current element in the collection. - /// - /// The enumerator is positioned before the first element of the collection or after the last element.2 - public object Current - { - get { return _currentItem; } - } - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - /// 2 - public IEnumerator GetEnumerator() - { - return new CacheEnumerator(_cache); - } - } - - /// - /// one item that will be used in the cache - /// - /// type of item to use - internal class Item - { - private readonly Guid _instanceId; - private readonly T _value; - internal bool Busy { get; set; } - internal bool Idle { get; set; } - internal T Value - { - get { return _value; } - } - internal Guid InstanceId - { - get { return _instanceId; } - } - internal Item(T value, Guid instanceId) - { - _value = value; - _instanceId = instanceId; - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDebugger.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDebugger.cs deleted file mode 100644 index 3f5d8e51b78..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDebugger.cs +++ /dev/null @@ -1,1592 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Activities; -using System.Management.Automation; -using System.Management.Automation.Tracing; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Language; -using System.Management.Automation.Host; -using System.Globalization; -using System.Diagnostics.CodeAnalysis; -using System.Activities.Tracking; -using Microsoft.PowerShell.Activities; -using Microsoft.PowerShell.Commands; -using System.Collections.ObjectModel; -using System.Activities.Hosting; -using System.Xml; -using System.Linq; -using System.Management.Automation.Security; -using Dbg = System.Diagnostics.Debug; - -namespace Microsoft.PowerShell.Workflow -{ - #region PSWorkflowDebuggerMode - - /// - /// PSWorkflowDebuggerMode - /// - internal enum WorkflowDebugMode - { - /// - /// Debugger is off. - /// - Off = 0, - - /// - /// Debugger stops at breakpoints. - /// - StopAtBreakpoint, - - /// - /// Debugger stops at each Activity within a workflow function, - /// and will step over nested workflow functions. - /// - StepToNextActivity, - - /// - /// Debugger steps into nested workflow functions. - /// - StepIntoFunction, - - /// - /// Debugger steps out of nested workflow functions. - /// - StepOutOfFunction - } - - #endregion - - #region DebuggerActivityPosition - - internal sealed class ActivityPosition - { - #region Members - - private Tuple _activityPosition; - - #endregion - - #region Constructors - - private ActivityPosition() { } - - /// - /// Constructor - /// - /// - /// - /// - internal ActivityPosition( - string name, - int line, - int col) - { - _activityPosition = new Tuple( - (string.IsNullOrEmpty(name)) ? string.Empty : name, - line, - col); - } - - #endregion - - #region Properties - - /// - /// Workflow function name - /// - internal string Name - { - get { return _activityPosition.Item1; } - } - - /// - /// Script line position - /// - internal int Line - { - get { return _activityPosition.Item2; } - } - - /// - /// Script column position - /// - internal int Column - { - get { return _activityPosition.Item3; } - } - - #endregion - } - - #endregion - - /// - /// Workflow Debugger - /// - internal sealed class PSWorkflowDebugger : Debugger, IDisposable - { - #region Members - - private Debugger _parent; - private Dictionary _lineBreakPoints; - private Dictionary _variableBreakPoints; - private Dictionary _commandBreakpoints; - private Dictionary _initalParentBreakpoints; - private Dictionary _variables; - private Dictionary _wfMetaVariables; - private PSWorkflowInstance _wfInstance; - private WorkflowDebugMode _mode; - private string _scriptName; - private string _script; - private string[] _scriptLines; - private string _xamlDefinition; - private string _outerFnXamlDefinition; - private SortedSet _validWFLineNumbers; - private DebuggerStopEventArgs _debuggerStopEventArgs; - private Stack _nestedCallStack; - private IEqualityComparer _invocationComparer; - private int _breakFrame; - private Dictionary _funcToSourceMap; - private Dictionary> _funcToValidLineNumbersMap; - - private Runspace _runspace; - private System.Management.Automation.PowerShell _psDebuggerCommand; - private IAsyncResult _psAsyncResult; - private PSHost _host; - private PathInfo _parentPath; - - private const string DefaultWFPrompt = "PS>> "; - private const string WFDebugPrompt = "[WFDBG:{0}]: "; - private const string DebugContextName = "PSDebugContext"; - - #endregion - - #region Constructors - - private PSWorkflowDebugger() - { } - - /// - /// Constructor - /// - /// PSWorkflowInstance - internal PSWorkflowDebugger(PSWorkflowInstance wfInstance) - { - if (wfInstance == null) - { - throw new PSArgumentNullException("wfInstance"); - } - - _wfInstance = wfInstance; - _lineBreakPoints = new Dictionary(); - _variableBreakPoints = new Dictionary(); - _commandBreakpoints = new Dictionary(); - _initalParentBreakpoints = new Dictionary(); - _variables = new Dictionary(); - _wfMetaVariables = new Dictionary(); - _nestedCallStack = new Stack(); - _invocationComparer = new InvocationInfoComparer(); - _mode = WorkflowDebugMode.Off; - _funcToSourceMap = new Dictionary(); - _funcToValidLineNumbersMap = new Dictionary>(); - } - - #endregion - - #region Debugger Overrides - - /// - /// ProcessCommand - /// - /// PSCommand - /// - /// DebuggerCommandResults - public override DebuggerCommandResults ProcessCommand(PSCommand command, PSDataCollection output) - { - if (command == null) - { - throw new PSArgumentNullException("command"); - } - - if (output == null) - { - throw new PSArgumentNullException("output"); - } - - if (!DebuggerStopped) - { - throw new PSInvalidOperationException(Resources.ProcessDebugCommandNotInDebugStopMode); - } - - // First give debugger chance to handle command. - DebuggerCommandResults results = InternalProcessCommand(command, output); - if (results != null) { return results; } - - // Check to see if command modifies Workflow variables, which is not allowed. - ErrorRecord varChangedError; - if (CheckForWorkflowVariableChange(command, output, out varChangedError)) - { - Dbg.Assert(varChangedError != null, "Error record cannot be null."); - - // Command attempted to modify a workflow variable. - // Write error message and return. - PSCommand errorCommand = new PSCommand(); - errorCommand.AddCommand("Write-Error").AddParameter("ErrorRecord", varChangedError); - - if ((_host != null) && - !(_host.GetType().FullName.Equals("System.Management.Automation.Remoting.ServerRemoteHost", StringComparison.OrdinalIgnoreCase))) - { - errorCommand.AddCommand("Out-Default"); - } - - return RunPowerShellCommand(errorCommand, null, output, false); - } - - bool addToHistory = CheckAddToHistory(command); - - return RunPowerShellCommand(command, null, output, addToHistory); - } - - /// - /// StopProcessCommand - /// - public override void StopProcessCommand() - { - System.Management.Automation.PowerShell ps = _psDebuggerCommand; - if (ps != null) - { - ps.BeginStop(null, null); - } - } - - /// - /// SetDebuggerAction - /// - /// DebuggerResumeAction to set - public override void SetDebuggerAction(DebuggerResumeAction resumeAction) - { - switch (resumeAction) - { - case DebuggerResumeAction.Continue: - _mode = WorkflowDebugMode.StopAtBreakpoint; - break; - - case DebuggerResumeAction.StepOut: - _mode = WorkflowDebugMode.StepOutOfFunction; - break; - - case DebuggerResumeAction.StepOver: - _mode = WorkflowDebugMode.StepToNextActivity; - break; - - case DebuggerResumeAction.StepInto: - _mode = WorkflowDebugMode.StepIntoFunction; - break; - - case DebuggerResumeAction.Stop: - _mode = WorkflowDebugMode.Off; - break; - - default: - _mode = WorkflowDebugMode.StopAtBreakpoint; - break; - } - } - - /// - /// DebuggerStopEventArgs - /// - /// DebuggerStopEventArgs - public override DebuggerStopEventArgs GetDebuggerStopArgs() - { - return _debuggerStopEventArgs; - } - - /// - /// Sets the parent debugger and breakpoints. - /// - /// Parent debugger - /// List of breakpoints - /// Debugger mode - /// host - /// Current path - public override void SetParent( - Debugger parent, - IEnumerable breakPoints, - DebuggerResumeAction? mode, - PSHost host, - PathInfo path) - { - if (parent == null) { throw new PSArgumentNullException("parent"); } - - _parent = parent; - _host = host; - _parentPath = path; - - // Add breakpoints for this workflow. - if (breakPoints != null) - { - _lineBreakPoints.Clear(); - _variableBreakPoints.Clear(); - _commandBreakpoints.Clear(); - foreach (var bp in breakPoints) - { - // Add breakpoints to local collections. - AddBreakpoint(bp); - } - } - - if (mode != null) - { - SetDebuggerAction(mode.Value); - } - } - - /// - /// Sets the parent debugger, breakpoints and function source map. - /// - /// Parent debugger - /// List of breakpoints - /// Debugger mode - /// host - /// Current path - /// Function to source map - public override void SetParent( - Debugger parent, - IEnumerable breakPoints, - DebuggerResumeAction? mode, - PSHost host, - PathInfo path, - Dictionary functionSourceMap) - { - if (functionSourceMap == null) { throw new PSArgumentNullException("functionSourceMap"); } - - // Set parent info using original API. - SetParent(parent, breakPoints, mode, host, path); - - // Add WF function to source information. - foreach (var item in functionSourceMap) - { - if (!_funcToSourceMap.ContainsKey(item.Key)) - { - _funcToSourceMap.Add(item.Key, item.Value); - } - } - } - - /// - /// Returns IEnumerable of CallStackFrame objects. - /// - /// - public override IEnumerable GetCallStack() - { - Debugger parentDebugger = _parent; - if (parentDebugger != null) - { - // First return WF call stack items. - foreach (var invocationInfo in _nestedCallStack) - { - yield return new CallStackFrame(invocationInfo); - } - - // Then return parent script call stack items. - foreach (var frame in parentDebugger.GetCallStack()) - { - yield return frame; - } - } - } - - /// - /// Sets debugger stepping mode. - /// - /// True if stepping is to be enabled - public override void SetDebuggerStepMode(bool enabled) - { - if (enabled) - { - SetDebuggerAction(DebuggerResumeAction.StepInto); - } - else - { - SetDebuggerAction(DebuggerResumeAction.Continue); - } - } - - #endregion - - #region IDisposable - - /// - /// Dispose - /// - public void Dispose() - { - if (_runspace != null) - { - _runspace.Debugger.BreakpointUpdated -= HandleRunspaceBreakpointUpdated; - _runspace.Dispose(); - _runspace = null; - } - - _parent = null; - } - - #endregion - - #region Internal Methods - - /// - /// DebuggerCheck - /// - /// Defines activity execution position in XAML definition - internal void DebuggerCheck( - ActivityPosition activityPosition) - { - string funcName = activityPosition.Name; - - // - // PSWorkflowInstance.PSWorkflowDefinition may be null when debugger - // object is created. Lazily check and create Xaml related info when - // needed. - // - if (!CheckXamlInfo()) - { - return; - } - - // - // PSWorkflowInstance.PSWorkflowJob may be null when debugger - // object is created. Lazily check and create script info when - // needed. - // - if (!CheckWorkflowJob(funcName)) - { - return; - } - - // Update script information with current WF function. - UpdateScriptInfo(funcName); - - // Update call stack with current WF function. - if (!UpdateCallStack(activityPosition)) - { - return; - } - - // Let debugger update script and callstack information but - // if debugger is off or not subscribed-to then return here. - if (!IsDebuggerStopEventSubscribed() || - (_mode == WorkflowDebugMode.Off)) - { - return; - } - - SetWorkflowVariablesToRunspace(); - - DebuggerStopEventArgs args = EvaluateForStop(activityPosition); - if (args != null) - { - SetWorkflowDebuggerContextVariable(args); - - _debuggerStopEventArgs = new DebuggerStopEventArgs( - args.InvocationInfo, - new Collection(args.Breakpoints), - args.ResumeAction); - - try - { - // Blocking call. - RaiseDebuggerStopEvent(args); - } - finally - { - _debuggerStopEventArgs = null; - } - - // Update debugger mode based on resumeAction. - SetDebuggerAction(args.ResumeAction); - - if (args.ResumeAction == DebuggerResumeAction.Stop) - { - _nestedCallStack.Clear(); - - if (_wfInstance.PSWorkflowJob != null) - { - // Stop and remove job from job manager. - _wfInstance.PSWorkflowJob.StopJobAsync(true, - Resources.JobStoppedByDebugger, true); - } - } - } - - RemoveWorkflowVariablesFromRunspace(); - } - - /// - /// UpdateVariables - /// - /// - internal void UpdateVariables(IDictionary data) - { - if (!IsDebuggerStopEventSubscribed() || - (_mode == WorkflowDebugMode.Off)) - { - return; - } - - this._variables = new Dictionary(data); - } - - #endregion - - #region Private Methods - - private bool UpdateCallStack( - ActivityPosition activityPosition) - { - var invocationInfo = CreateInvocationInfo(activityPosition); - if (invocationInfo == null) { return false; } - if (_nestedCallStack.Contains(invocationInfo, _invocationComparer)) - { - // Unwind stack to this position and update invocation info. - // We can do this because workflows does not allow recursion calls - // and each workflow function name on the stack must be unique. - while ((_nestedCallStack.Count > 0) && - (_invocationComparer.Equals(_nestedCallStack.Peek(), invocationInfo) == false)) - { - _nestedCallStack.Pop(); - } - - Dbg.Assert(_nestedCallStack.Count > 0, "Should have unwound stack to known location."); - - // Replace invocationInfo item on stack. - _nestedCallStack.Pop(); - _nestedCallStack.Push(invocationInfo); - } - else - { - // Push invocationInfo item on top of stack. - _nestedCallStack.Push(invocationInfo); - } - - return true; - } - - private DebuggerStopEventArgs EvaluateForStop( - ActivityPosition activityPosition) - { - WorkflowDebugMode mode = _mode; - Breakpoint bp = null; - DebuggerResumeAction resumeAction = DebuggerResumeAction.Continue; - bool doStop = false; - - switch (mode) - { - case WorkflowDebugMode.StopAtBreakpoint: - resumeAction = DebuggerResumeAction.Continue; - bp = FindBreakpointHit(activityPosition); - doStop = (bp != null); - break; - - case WorkflowDebugMode.StepToNextActivity: - resumeAction = DebuggerResumeAction.StepOver; - doStop = (_nestedCallStack.Count <= _breakFrame); - break; - - case WorkflowDebugMode.StepIntoFunction: - resumeAction = DebuggerResumeAction.StepInto; - doStop = true; - break; - - case WorkflowDebugMode.StepOutOfFunction: - resumeAction = DebuggerResumeAction.StepOut; - doStop = (_nestedCallStack.Count < _breakFrame); - break; - } - - if (!doStop && - (mode == WorkflowDebugMode.StepOutOfFunction || - mode == WorkflowDebugMode.StepToNextActivity)) - { - // If we are in step over/out mode check break point, which - // overrides over/out action. - bp = FindBreakpointHit(activityPosition); - doStop = (bp != null); - } - - if (doStop) - { - // Update current break frame value. - _breakFrame = _nestedCallStack.Count; - Dbg.Assert(_breakFrame > 0, "Must always have a non zero call stack count at debugger stop."); - - return new DebuggerStopEventArgs( - CreateInvocationInfo(activityPosition), - (bp != null) ? new Collection() { bp } : new Collection(), - resumeAction); - } - - return null; - } - - private Breakpoint FindBreakpointHit( - ActivityPosition activityPosition) - { - // Line breakpoint. - foreach (var bp in _lineBreakPoints.Values) - { - LineBreakpoint lineBp = bp as LineBreakpoint; - if (CheckLineBreakpoint(lineBp, activityPosition.Line)) - { - return lineBp; - } - } - - // Command breakpoint. - // Variable breakpoint. - - return null; - } - - private bool CheckLineBreakpoint( - LineBreakpoint bp, - int activityLine) - { - if (bp == null || !bp.Enabled) { return false; } - - // Check for exact line match. - bool isGoodBreakpoint = (bp.Line == activityLine); - - if (!isGoodBreakpoint && (_validWFLineNumbers != null) && - ((_validWFLineNumbers.Count > 0) && (!_validWFLineNumbers.Contains(bp.Line)))) - { - // If requested breakpoint is not valid, check for a close line match. - // Currently this means a breakpoint within +/1 one script line. - isGoodBreakpoint = (Math.Abs(bp.Line - activityLine) == 1) ? true : false; - } - - if (!isGoodBreakpoint) - { - return isGoodBreakpoint; - } - - // Check to see if this is a valid line breakpoint with an Action - // script block and if so evaluate the script block. - if (bp.Action != null) - { - ErrorRecord errorRecord; - isGoodBreakpoint = CheckBreakpointAction(bp, out errorRecord); - - if (!isGoodBreakpoint && (errorRecord != null) && - (_host != null) && (_host.UI != null)) - { - _host.UI.WriteErrorLine(errorRecord.Exception.Message); - } - } - - if (!isGoodBreakpoint) - { - return isGoodBreakpoint; - } - - // Validate breakpoint with current source file. - if ((bp.Script != null) && - (_scriptName != null)) - { - isGoodBreakpoint = bp.Script.Equals(_scriptName, StringComparison.OrdinalIgnoreCase); - } - else - { - isGoodBreakpoint = false; - } - - return isGoodBreakpoint; - } - - private bool CheckBreakpointAction(LineBreakpoint bp, out ErrorRecord errorRecord) - { - errorRecord = null; - bool validBreakpoint = false; - - try - { - PSCommand cmd = new PSCommand(); - cmd.AddScript(bp.Action.ToString()); - if (CheckForWorkflowVariableChange(cmd, null, out errorRecord)) { return false; } - RunPowerShellCommand(cmd, null, null, false, true); - } - catch (BreakException) - { - validBreakpoint = true; - } - catch (Exception e) - { - // Don't throw exceptions on Participant tracker thread. - CheckForSevereException(e); - } - - return validBreakpoint; - } - - private void GetMetaDataAndCommonParameters( - Dictionary wfMetaData, - Dictionary commonParameters) - { - if (wfMetaData != null) - { - foreach (var item in wfMetaData) - { - _wfMetaVariables.Add(PSWorkflowApplicationInstance.TranslateMetaDataName(item.Key), item.Value); - } - } - - if (commonParameters != null) - { - foreach (var item in commonParameters) - { - if (!_wfMetaVariables.ContainsKey(item.Key)) - { - _wfMetaVariables.Add(item.Key, item.Value); - } - } - } - } - - private InvocationInfo CreateInvocationInfo( - ActivityPosition activityPosition) - { - string cmdName = activityPosition.Name; - int line = activityPosition.Line; - int col = activityPosition.Column; - - Dbg.Assert(!string.IsNullOrEmpty(_scriptName), "Script name should never be null during a debugger stop."); - - string script; - string scriptLine; - int endCol; - - if (string.IsNullOrEmpty(_script) || - (_scriptLines == null) || - (line < 1) || - (line > _scriptLines.Length)) - { - // Create a minimal InvocationInfo object without source information so that - // the user can get something (function name, line, column) on this workflow sequence point. - script = string.Empty; - scriptLine = string.Empty; - endCol = col; - } - else - { - // Otherwise provide full source information. - script = _script; - scriptLine = _scriptLines[line - 1]; - endCol = scriptLine.Length + 1; - } - - ScriptPosition scriptStartPosition = new ScriptPosition(_scriptName, line, col, scriptLine, script); - ScriptPosition scriptEndPosition = new ScriptPosition(_scriptName, line, endCol, scriptLine, script); - - InvocationInfo invocationInfo = InvocationInfo.Create( - new WorkflowInfo( - cmdName, - script, - ScriptBlock.Create(script), - (_xamlDefinition != null) ? _xamlDefinition : _outerFnXamlDefinition, - null), - new ScriptExtent( - scriptStartPosition, - scriptEndPosition) - ); - - // For remoting invocationInfo.DisplayScriptPosition does not serialize line string needed - // for display. Therefore set it to null so that ScriptPosition will be used which *does* - // pass the Line string. - invocationInfo.DisplayScriptPosition = null; - - return invocationInfo; - } - - private void AddBreakpoint(Breakpoint bp) - { - // Keep track of all script breakpoints from the parent so - // that *-PSBreakpoint commands can properly reflect this. - if (!_initalParentBreakpoints.ContainsKey(bp.Id)) - { - _initalParentBreakpoints.Add(bp.Id, bp); - } - - // Line breakpoints - LineBreakpoint lbp = bp as LineBreakpoint; - if (lbp != null) - { - if (!_lineBreakPoints.ContainsKey(lbp.Id)) { _lineBreakPoints.Add(lbp.Id, lbp); } - } - else - { - // Variable breakpoints - VariableBreakpoint vbp = bp as VariableBreakpoint; - if (vbp != null) - { - if (!_variableBreakPoints.ContainsKey(vbp.Id)) { _variableBreakPoints.Add(vbp.Id, vbp); } - } - else - { - // Command breakpoints - CommandBreakpoint cbp = bp as CommandBreakpoint; - if (cbp != null) - { - if (!_commandBreakpoints.ContainsKey(cbp.Id)) { _commandBreakpoints.Add(cbp.Id, cbp); } - } - } - } - } - - private void RemoveBreakpoint(int id) - { - _initalParentBreakpoints.Remove(id); - - if (!_lineBreakPoints.Remove(id)) - { - if (!_variableBreakPoints.Remove(id)) - { - _commandBreakpoints.Remove(id); - } - } - } - - /// - /// Lazily create Xaml related info. - /// - /// True if Xaml info is available. - private bool CheckXamlInfo() - { - if (!string.IsNullOrEmpty(_outerFnXamlDefinition)) - { - return true; - } - - if (_wfInstance.PSWorkflowDefinition != null && - _wfInstance.PSWorkflowDefinition.WorkflowXaml != null) - { - _outerFnXamlDefinition = _wfInstance.PSWorkflowDefinition.WorkflowXaml; - - // It is possible for the workflow definition to contain null/empty Xaml, - // such as when a workflow job is created from Xaml and not from PS script. - // In this case the debugger remains inactive. - return !string.IsNullOrEmpty(_outerFnXamlDefinition); - } - - return false; - } - - /// - /// Lazily create the job meta data variables. - /// - /// - private bool CheckWorkflowJob(string funcName) - { - if (_wfMetaVariables.Count > 0) - { - return true; - } - - if (_wfInstance.PSWorkflowJob != null && - _wfInstance.PSWorkflowJob.PSWorkflowJobDefinition != null) - { - // Look for script file and source info and if needed read script file. - // Update the function name-to-script map. - string scriptFile = _wfInstance.PSWorkflowJob.PSWorkflowJobDefinition.WorkflowScriptFile; - string script = _wfInstance.PSWorkflowJob.PSWorkflowJobDefinition.WorkflowFullScript; - if (string.IsNullOrEmpty(script) && !string.IsNullOrEmpty(scriptFile)) - { - script = ReadScriptFromFile(scriptFile); - } - - if (!string.IsNullOrEmpty(script)) - { - UpdateSourceMap( - _funcToSourceMap, - funcName, - script, - scriptFile, - _outerFnXamlDefinition); - } - - // Create job meta data. - GetMetaDataAndCommonParameters( - _wfInstance.PSWorkflowJob.JobMetadata, - _wfInstance.PSWorkflowJob.PSWorkflowCommonParameters); - - return true; - } - - return false; - } - - private void UpdateSourceMap( - Dictionary funcToSourceMap, - string funcName, - string script, - string scriptFile, - string xamlDefinition) - { - if (funcToSourceMap == null) { throw new PSArgumentNullException("funcToSourceMap"); } - if (funcName == null) { throw new PSArgumentNullException("funcName"); } - if (script == null) { throw new PSArgumentNullException("script"); } - - // Outer function name is always added. - DebugSource debugSource = new DebugSource(script, scriptFile, xamlDefinition); - funcToSourceMap.Add(funcName, debugSource); - - // Get list of workflow functions in the script AST and - // add reference to the debug source. - ParseScriptForMap(funcToSourceMap, script, debugSource); - } - - private void ParseScriptForMap( - Dictionary funcToSourceMap, - string script, - DebugSource debugSource) - { - ScriptBlock sb = ScriptBlock.Create(script); - ScriptBlockAst sbAst = sb.Ast as ScriptBlockAst; - if (sbAst != null) - { - if (sbAst.BeginBlock != null) - { - AddWorkflowFunctionToMap(sbAst.BeginBlock.Statements, funcToSourceMap, debugSource); - } - - if (sbAst.ProcessBlock != null) - { - AddWorkflowFunctionToMap(sbAst.ProcessBlock.Statements, funcToSourceMap, debugSource); - } - - if (sbAst.EndBlock != null) - { - AddWorkflowFunctionToMap(sbAst.EndBlock.Statements, funcToSourceMap, debugSource); - } - } - } - - private void AddWorkflowFunctionToMap( - ReadOnlyCollection statements, - Dictionary funcToSourceMap, - DebugSource debugSource) - { - if (statements == null) { return; } - - foreach (var statementAst in statements) - { - FunctionDefinitionAst fAst = statementAst as FunctionDefinitionAst; - if ((fAst != null) && (fAst.IsWorkflow)) - { - string fName = fAst.Name; - - if (funcToSourceMap.ContainsKey(fName)) - { - // Update existing source info. - var existingInfo = funcToSourceMap[fName]; - funcToSourceMap.Remove(fName); - - debugSource = new DebugSource( - debugSource.Script, // Update script - debugSource.ScriptFile, // Update script filename - existingInfo.XamlDefinition); // Keep XamlDefinition - } - - funcToSourceMap.Add(fName, debugSource); - - // Call this recursively for all statements in the workflow function to pick - // up any WF functions defined inside this WF function. - if (fAst.Body.BeginBlock != null) - { - AddWorkflowFunctionToMap(fAst.Body.BeginBlock.Statements, funcToSourceMap, debugSource); - } - - if (fAst.Body.ProcessBlock != null) - { - AddWorkflowFunctionToMap(fAst.Body.ProcessBlock.Statements, funcToSourceMap, debugSource); - } - - if (fAst.Body.EndBlock != null) - { - AddWorkflowFunctionToMap(fAst.Body.EndBlock.Statements, funcToSourceMap, debugSource); - } - } - } - } - - /// - /// UpdateScriptInfo - /// - private void UpdateScriptInfo(string funcName) - { - if (funcName.Equals(_scriptName, StringComparison.OrdinalIgnoreCase)) { return; } - - DebugSource debugSource; - if (!_funcToSourceMap.TryGetValue(funcName, out debugSource)) - { - // Minimal script info, just the Workflow function name. - _scriptName = funcName; - _script = null; - _scriptLines = null; - _xamlDefinition = null; - } - else - { - // Otherwise get full source script info. - _script = debugSource.Script; - _scriptName = !string.IsNullOrEmpty(debugSource.ScriptFile) ? debugSource.ScriptFile : funcName; - _scriptLines = _script.Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); - - _xamlDefinition = debugSource.XamlDefinition; - - if (!_funcToValidLineNumbersMap.TryGetValue(funcName, out _validWFLineNumbers)) - { - _validWFLineNumbers = CreateValidScriptLineSet(_xamlDefinition); - _funcToValidLineNumbersMap.Add(funcName, _validWFLineNumbers); - } - } - - // Reset parent debugger to show new source, if any. - Debugger parentDebugger = _parent; - if (parentDebugger != null) - { - parentDebugger.ResetCommandProcessorSource(); - } - } - - private SortedSet CreateValidScriptLineSet(string xamlDefinition) - { - if (xamlDefinition == null) { return null; } - - SortedSet validWFLineNumbers = new SortedSet(); - - try - { - XmlDocument doc = (XmlDocument)LanguagePrimitives.ConvertTo(xamlDefinition, typeof(XmlDocument), CultureInfo.InvariantCulture); - string nsName = "ns1"; - string ns = "clr-namespace:Microsoft.PowerShell.Activities;assembly=Microsoft.PowerShell.Activities"; - string query = string.Format(CultureInfo.InvariantCulture, @"//{0}:PowerShellValue/@Expression", nsName); - XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable); - nsMgr.AddNamespace(nsName, ns); - - // Search for all Xaml PowerShellValue/Expression node attributes. - XmlNodeList nodes = doc.SelectNodes(query, nsMgr); - foreach (XmlAttribute exprItem in nodes) - { - if (exprItem == null) { continue; } - string exprValue = exprItem.InnerText; - - // Look for line/col expression attribute symbol pattern and retrieve symbols. - if (System.Text.RegularExpressions.Regex.IsMatch(exprValue, @"^'\d+:\d+:\S+'$")) - { - string[] symbols = exprValue.Trim('\'').Split(':'); - - int nLine = -1; - try - { - nLine = Convert.ToInt32(symbols[0], CultureInfo.InvariantCulture); - } - catch (FormatException) - { } - catch (OverflowException) - { } - - // Add line number to list. - if (nLine > 0 && - !validWFLineNumbers.Contains(nLine)) - { - validWFLineNumbers.Add(nLine); - } - } - } - } - catch (ArgumentNullException) { } - catch (PSInvalidCastException) { } - catch (System.Xml.XPath.XPathException) { } - - return validWFLineNumbers; - } - - private static string ReadScriptFromFile(string scriptSource) - { - // If script content string is a file path then read the script. - try - { - return System.IO.File.ReadAllText(scriptSource); - } - catch (ArgumentException) { } - catch (System.IO.IOException) { } - catch (UnauthorizedAccessException) { } - catch (NotSupportedException) { } - catch (System.Security.SecurityException) { } - - return null; - } - - private DebuggerCommandResults InternalProcessCommand(PSCommand command, PSDataCollection output) - { - string cmd = command.Commands[0].CommandText.Trim(); - - if (cmd.Equals("prompt", StringComparison.OrdinalIgnoreCase)) - { - // WF prompt. - return HandlePromptCommand(command, output); - } - - if (cmd.Equals("exit", StringComparison.OrdinalIgnoreCase)) - { - // Ignore. - return new DebuggerCommandResults(DebuggerResumeAction.Continue, true); - } - - if (cmd.Equals("k", StringComparison.OrdinalIgnoreCase) || - cmd.Equals("Get-PSCallStack", StringComparison.OrdinalIgnoreCase)) - { - // WF call stack. - return HandleCallStack(output); - } - - return null; - } - - private DebuggerCommandResults HandlePromptCommand(PSCommand command, PSDataCollection output) - { - // Create WF Debug prompt. - string wfDebugPrompt; - string location = null; - object locationItem; - if (_wfMetaVariables.TryGetValue("PSComputerName", out locationItem)) - { - location = locationItem as string; - } - - wfDebugPrompt = string.Format(CultureInfo.InvariantCulture, - WFDebugPrompt, - !string.IsNullOrEmpty(location) ? location : string.Empty); - - // Run prompt command to get default prompt from runspace. - string defaultPrompt = null; - PSDataCollection promptOutput = new PSDataCollection(); - RunPowerShellCommand(command, null, promptOutput, false); - if (promptOutput.Count == 1) - { - defaultPrompt = promptOutput[0].BaseObject as string; - } - - if (defaultPrompt != null) - { - output.Add(wfDebugPrompt + defaultPrompt.TrimEnd() + "> "); - } - else - { - output.Add(wfDebugPrompt + DefaultWFPrompt); - } - - return new DebuggerCommandResults(null, true); - } - - private DebuggerCommandResults HandleCallStack(PSDataCollection output) - { - PSDataCollection callStack = GetCallStack().ToArray(); - if (callStack.Count > 0) - { - if ((_host != null) && (_host.UI != null) && - !(_host.GetType().FullName).Equals("System.Management.Automation.Remoting.ServerRemoteHost", - StringComparison.OrdinalIgnoreCase)) - { - // Use Out-Default to stream and format call stack data. - PSCommand cmd = new PSCommand(); - cmd.AddCommand("Out-Default"); - - RunPowerShellCommand(cmd, callStack, output, false); - } - else - { - // Otherwise add directly to output. This is needed in the WF remote case because - // host output is blocked on the client during debugger stop. - // Use Out-String so that it gets formatted correctly. - PSCommand cmd = new PSCommand(); - cmd.AddCommand("Out-String").AddParameter("Stream", true); - - RunPowerShellCommand(cmd, callStack, output, false); - } - } - - return new DebuggerCommandResults(null, true); - } - - private DebuggerCommandResults RunPowerShellCommand( - PSCommand command, - PSDataCollection input, - PSDataCollection output, - bool addToHistory, - bool allowBreakException = false) - { - // Wait for any current command to finish. - IAsyncResult async = _psAsyncResult; - if (async != null) - { - try - { - async.AsyncWaitHandle.WaitOne(); - } - catch (ObjectDisposedException) { } - } - - // Lazily create a runspace to run PS commands on. - if (_runspace == null || (_runspace.RunspaceStateInfo.State != RunspaceState.Opened)) - { - if (_runspace != null) { _runspace.Dispose(); } - - // Enforce the system lockdown policy if one is defined. - InitialSessionState iss = InitialSessionState.CreateDefault2(); - if (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) - { - iss.LanguageMode = PSLanguageMode.ConstrainedLanguage; - } - - _runspace = (_host != null) ? - RunspaceFactory.CreateRunspace(_host, iss) : RunspaceFactory.CreateRunspace(iss); - - _runspace.Open(); - - SetPath(_runspace, _parentPath); - - CreateHelperFunctionsAndWorkflowMetaDataOnRunspace(_runspace); - - // Set breakpoints on runspace to support *-PSBreakpoint cmdlets. - _runspace.Debugger.SetBreakpoints(_initalParentBreakpoints.Values); - _runspace.Debugger.BreakpointUpdated += HandleRunspaceBreakpointUpdated; - } - - try - { - using (_psDebuggerCommand = System.Management.Automation.PowerShell.Create()) - { - // Runspace debugger must be disabled while running commands. - _runspace.Debugger.SetDebugMode(DebugModes.None); - _psDebuggerCommand.Runspace = _runspace; - _psDebuggerCommand.Commands = command; - foreach (var cmd in _psDebuggerCommand.Commands.Commands) - { - cmd.MergeMyResults(PipelineResultTypes.All, PipelineResultTypes.Output); - } - - // Default settings. - PSInvocationSettings settings = new PSInvocationSettings(); - settings.ExposeFlowControlExceptions = allowBreakException; - settings.AddToHistory = addToHistory; - - PSDataCollection collectOutput = (output != null) ? output : new PSDataCollection(); - - // Allow any exceptions to propagate. - _psAsyncResult = _psDebuggerCommand.BeginInvoke(input, collectOutput, settings, null, null); - _psDebuggerCommand.EndInvoke(_psAsyncResult); - } - } - finally - { - _psDebuggerCommand = null; - _psAsyncResult = null; - - // Restore workflow debugging. - _runspace.Debugger.SetDebugMode(DebugModes.LocalScript); - } - - return new DebuggerCommandResults(null, false); - } - - private void HandleRunspaceBreakpointUpdated(object sender, BreakpointUpdatedEventArgs e) - { - // Update local workflow breakpoints. - switch (e.UpdateType) - { - case BreakpointUpdateType.Set: - AddBreakpoint(e.Breakpoint); - break; - - case BreakpointUpdateType.Removed: - RemoveBreakpoint(e.Breakpoint.Id); - break; - } - - // Forward update to parent. - RaiseBreakpointUpdatedEvent(e); - } - - private void SetPath(Runspace runspace, PathInfo path) - { - if (path == null || - string.IsNullOrEmpty(path.Path)) - { - return; - } - - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - ps.Runspace = runspace; - ps.AddCommand("Set-Location").AddParameter("Path", path.Path); - ps.Invoke(); - } - } - - private void CreateHelperFunctionsAndWorkflowMetaDataOnRunspace(Runspace runspace) - { - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - ps.Runspace = runspace; - - // Add workflow specific debugger functions. - foreach (var functionScript in System.Management.Automation.Internal.DebuggerUtils.GetWorkflowDebuggerFunctions()) - { - ps.Commands.Clear(); - ps.AddScript(functionScript); - ps.Invoke(); - } - - // Add meta workflow variables to runspace. - ps.Commands.Clear(); - ps.AddCommand("Set-DebuggerVariable").AddParameter("Variables", _wfMetaVariables); - ps.Invoke(); - - // Add private *this* debugger variable. - ps.Commands.Clear(); - ps.AddCommand("Set-Variable").AddParameter("Name", "PSWorkflowDebugger").AddParameter("Value", this).AddParameter("Visibility", "Private"); - ps.Invoke(); - } - } - - private void SetWorkflowVariablesToRunspace() - { - // Set workflow variables. - try - { - // Set workflow variables from activity context. - PSCommand cmd = new PSCommand(); - cmd.AddCommand("Set-DebuggerVariable").AddParameter("Variables", _variables); - RunPowerShellCommand(cmd, null, null, false); - } - catch (Exception e) - { - // Don't throw exceptions on Participant tracker thread. - CheckForSevereException(e); - } - } - - private void SetWorkflowDebuggerContextVariable(DebuggerStopEventArgs args) - { - // Add context variable to variables collection. - PSDebugContext debugContext = new PSDebugContext(args.InvocationInfo, new List(args.Breakpoints)); - if (_variables.ContainsKey(DebugContextName)) - { - _variables[DebugContextName] = debugContext; - } - else - { - _variables.Add(DebugContextName, debugContext); - } - - // Set workflow debugger context variable to runspace. - try - { - PSCommand cmd = new PSCommand(); - cmd.AddCommand("Set-Variable") - .AddParameter("Name", DebugContextName) - .AddParameter("Value", debugContext); - RunPowerShellCommand(cmd, null, null, false); - } - catch (Exception e) - { - // Don't throw exceptions on Participant tracker thread. - CheckForSevereException(e); - } - } - - private void RemoveWorkflowVariablesFromRunspace() - { - // Remove workflow variables. - try - { - PSCommand cmd = new PSCommand(); - cmd.AddCommand("Remove-DebuggerVariable").AddParameter("Name", _variables.Keys); - RunPowerShellCommand(cmd, null, null, false); - } - catch (Exception e) - { - // Don't throw exceptions on Participant tracker thread. - CheckForSevereException(e); - } - } - - /// - /// Helper method to check if user attempted to modify a workflow variable which - /// isn't supported. Write error message to host. - /// - private bool CheckForWorkflowVariableChange( - PSCommand command, - PSDataCollection output, - out ErrorRecord errorRecord) - { - errorRecord = null; - string variableName = string.Empty; - - // Searcher to detect a variable assignment. - // This is based on searcher from WorkflowJobConverter but is modified. - Func assignmentSearcher = (ast) => - { - AssignmentStatementAst assignment = ast as AssignmentStatementAst; - UnaryExpressionAst unaryExpression = ast as UnaryExpressionAst; - - if ((assignment == null) && (unaryExpression == null)) - { - return false; - } - - // Check if this is a unary assignment - if (unaryExpression != null) - { - VariableExpressionAst referenceVariable = unaryExpression.Child as VariableExpressionAst; - if (referenceVariable != null && String.Equals(referenceVariable.VariablePath.UserPath, variableName, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - // Check if this is a regular assignment - if (assignment == null) { return false; } - - // Capture $x = 10 - VariableExpressionAst variableExpression = assignment.Left as VariableExpressionAst; - string detectedVariableName = null; - - if (variableExpression == null) - { - // Capture [int] $x = 10 - ConvertExpressionAst convertExpression = assignment.Left as ConvertExpressionAst; - if (convertExpression != null) - { - variableExpression = convertExpression.Child as VariableExpressionAst; - } - } - - if (variableExpression != null) - { - detectedVariableName = variableExpression.VariablePath.UserPath; - } - else - { - return false; - } - - // See if it's the variable we're looking for - // Variable = - string workingVariableName = detectedVariableName; - - // Allow the "WORKFLOW:" scope qualifier in nested scopes - if (workingVariableName.StartsWith("WORKFLOW:", StringComparison.OrdinalIgnoreCase)) - { - workingVariableName = workingVariableName.Remove(0, "WORKFLOW:".Length); - } - - return String.Equals(workingVariableName, variableName, StringComparison.OrdinalIgnoreCase); - }; - - Dictionary allWFVariables = new Dictionary(_wfMetaVariables); - foreach (var wfVar in _variables) - { - if (!allWFVariables.ContainsKey(wfVar.Key)) - { - allWFVariables.Add(wfVar.Key, wfVar.Value); - } - } - - foreach (var cmd in command.Commands) - { - // Create scriptblock/AST for each command. - ScriptBlock sb; - try - { - sb = ScriptBlock.Create(cmd.CommandText); - } - catch (ParseException) - { - // Ignore parse exception here and let normal command - // processing handle potential incomplete parse condition. - return false; - } - - // Check assignments to known workflow variables. - foreach (var workflowVariable in allWFVariables) - { - variableName = workflowVariable.Key; - - Ast result = sb.Ast.Find(assignmentSearcher, searchNestedScriptBlocks: true); - if (result != null) - { - string msg = string.Format(System.Threading.Thread.CurrentThread.CurrentCulture, - Resources.DebuggerCannotModifyWFVars, variableName); - - errorRecord = new ErrorRecord( - new PSInvalidOperationException(msg), - "PSWorkflowDebuggerCannotModifyWorkflowVariables", - ErrorCategory.InvalidOperation, - this); - - return true; - } - } - } - - return false; - } - - private bool CheckAddToHistory(PSCommand command) - { - if (command.Commands.Count == 0) { return false; } - - return System.Management.Automation.Internal.DebuggerUtils.ShouldAddCommandToHistory( - command.Commands[0].CommandText); - } - - /// - /// Helper method to check for and rethrow severe exceptions. - /// - /// Exception to check - private static void CheckForSevereException(Exception e) - { - if (e is AccessViolationException || e is StackOverflowException) - { - throw e; - } - } - - #endregion - - #region Private Classes - - private class InvocationInfoComparer : IEqualityComparer - { - bool IEqualityComparer.Equals(InvocationInfo obj1, InvocationInfo obj2) - { - return obj1.MyCommand.Name.Equals(obj2.MyCommand.Name, StringComparison.OrdinalIgnoreCase); - } - - int IEqualityComparer.GetHashCode(InvocationInfo info) - { - return info.GetHashCode(); - } - } - - #endregion - - } // PSWorkflowDebugger -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDefinition.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDefinition.cs deleted file mode 100644 index 36e5ed1fb54..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDefinition.cs +++ /dev/null @@ -1,91 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Management.Automation; - using System.Activities; - - /// - /// Define all the metadatas. - /// - public sealed class PSWorkflowDefinition - { - private Activity workflow; - private string workflowXaml; - private string runtimeAssemblyPath; - private Dictionary requiredAssemblies; - - /// - /// Workflow instance constructor. - /// - /// The workflow definition, represented in object-graph. - /// The workflow xaml definition. - /// The path to the runtime assembly. - /// Required assemblies paths - public PSWorkflowDefinition( - Activity workflow, - string workflowXaml, - string runtimeAssemblyPath, - Dictionary requiredAssemblies): this(workflow, workflowXaml, runtimeAssemblyPath) - { - this.requiredAssemblies = requiredAssemblies; - } - - /// - /// Workflow instance constructor. - /// - /// The workflow definition, represented in object-graph. - /// The workflow xaml definition. - /// The path to the runtime assembly. - public PSWorkflowDefinition( - Activity workflow, - string workflowXaml, - string runtimeAssemblyPath) - { - this.workflow = workflow; - this.workflowXaml = workflowXaml; - this.runtimeAssemblyPath = runtimeAssemblyPath; - } - - /// - /// Gets sets the workflow. - /// - public Activity Workflow - { - get { return this.workflow; } - set { this.workflow = value; } - } - - /// - /// Gets sets the workflow xaml. - /// - public string WorkflowXaml - { - get { return this.workflowXaml; } - set { this.workflowXaml = value; } - } - - /// - /// Gets sets the runtime assembly path. - /// - public string RuntimeAssemblyPath - { - get { return this.runtimeAssemblyPath; } - set { this.runtimeAssemblyPath = value; } - } - - /// - /// Gets sets the required assemblies. - /// - public Dictionary RequiredAssemblies - { - get { return this.requiredAssemblies; } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowInstance.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowInstance.cs deleted file mode 100644 index 4198665363c..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowInstance.cs +++ /dev/null @@ -1,2613 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Diagnostics; -using System.Management.Automation.Runspaces; - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Collections.Generic; - using System.Activities; - using System.Management.Automation; - using System.Management.Automation.Tracing; - using System.Globalization; - using System.Diagnostics.CodeAnalysis; - using System.Activities.Tracking; - using System.IO; - using System.Reflection; - using Microsoft.PowerShell.Activities; - using Microsoft.PowerShell.Commands; - using System.Timers; - using System.Collections.ObjectModel; - using System.Activities.Hosting; - using Dbg = System.Diagnostics.Debug; - using System.Security.Principal; - using System.Management.Automation.Language; - using System.Runtime.Serialization; - using System.Linq; - - /// - /// Specifies the action that occurs when a workflow becomes idle when persistence is allowed. - /// - public enum PSPersistableIdleAction - { - /// - /// No or null action is defined so fall back to default. - /// - NotDefined = 0, - - /// - /// Specifies that no action is taken. - /// - None = 1, - - /// - /// Specifies that the System.Activities.WorkflowApplication should persist the workflow. - /// - Persist = 2, - - /// - /// Specifies that the System.Activities.WorkflowApplication should persist and unload the workflow. - /// The job will remain in running state because async operation (out of proc or remote operation) is in progress. - /// The System.Activities.WorkflowApplication will be loaded when async operation gets completed. - /// - Unload = 3, - - /// - /// Specifies that the System.Activities.WorkflowApplication should persist and unload the workflow and Job is marked as suspended. - /// - Suspend = 4, - - } - - /// - /// This class encapsulate the guid to avoid the confusion be job instance id and workflow instance id. - /// - public sealed class PSWorkflowId - { - private Guid _guid; - - /// - /// Default constructor - /// - public PSWorkflowId() - { - _guid = new Guid(); - } - - /// - /// Constructor which takes guid. - /// - /// - public PSWorkflowId(Guid value) - { - _guid = value; - } - - /// - /// NewWorkflowGuid - /// - /// - public static PSWorkflowId NewWorkflowGuid() - { - return new PSWorkflowId(Guid.NewGuid()); - } - - /// - /// Gets Guid - /// - public Guid Guid - { - get - { - return _guid; - } - } - } - - /// - /// WorkflowOnHandledErrorAction - /// - public enum WorkflowUnhandledErrorAction - { - /// - /// Suspend - /// - Suspend = 0, - - /// - /// Stop - /// - Stop = 1, - - /// - /// Terminate - /// - Terminate = 2, - }; - - /// - /// Possible workflow instance creation modes. - /// - internal enum WorkflowInstanceCreationMode - { - /// - /// Workflow instance created normally. - /// - Normal = 0, - - /// - /// Workflow instance created normally. - /// - AfterCrashOrShutdown = 1, - }; - - /// - /// PSWorkflowRemoteActivityState - /// - public sealed class PSWorkflowRemoteActivityState - { - // the remote runspace instance ids collection - private Dictionary>> _remoteRunspaceIdCollection; - private readonly PSWorkflowInstanceStore _store; - private bool _internalUnloaded = false; - // sync object is required to synchronize the access to _remoteRunspaceIdCollection from multiple parallel tasks execution - private object _syncObject = new object(); - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// PSWorkflowRemoteActivityState constructor - /// - /// - internal PSWorkflowRemoteActivityState(PSWorkflowInstanceStore store) - { - _store = store; - _remoteRunspaceIdCollection = new Dictionary>>(); - } - - /// - /// Creates a PSWorkflowRemoteActivityState for a workflow instance based on deserialized object - /// - /// - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the PowerShell Workflow extensibility.")] - public PSWorkflowRemoteActivityState(PSWorkflowInstanceStore store, Dictionary>> deserializedRemoteActivityState) - { - _store = store; - - if (deserializedRemoteActivityState == null) - throw new ArgumentNullException("deserializedRemoteActivityState"); - - _remoteRunspaceIdCollection = deserializedRemoteActivityState; - } - - /// - /// Retrieves RemoteActivityState object to be serialized - /// - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the PowerShell Workflow extensibility.")] - public Dictionary>> GetSerializedData() - { - lock (_syncObject) - { - // Get the copy of current _remoteRunspaceIdCollection for serialization - return new Dictionary>>(_remoteRunspaceIdCollection); - } - } - - /// - /// InternalUnloaded property - /// - internal bool InternalUnloaded - { - get - { - lock (_syncObject) - { - return _internalUnloaded; - } - } - set - { - lock (_syncObject) - { - _internalUnloaded = value; - } - } - } - - /// - /// GetRemoteActivityRunspaceEntry - /// - /// - /// - /// task entry object - internal object GetRemoteActivityRunspaceEntry(string activityId, int taskId) - { - object taskRunspaceId = null; - - lock (_syncObject) - { - Dictionary> runspaceIdCollection; - if (_remoteRunspaceIdCollection.TryGetValue(activityId, out runspaceIdCollection)) - { - if (runspaceIdCollection != null) - { - Tuple taskTuple; - runspaceIdCollection.TryGetValue(taskId, out taskTuple); - - if (taskTuple != null) - taskRunspaceId = taskTuple.Item1; - } - } - } - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "RemoteActivityState: runspace entry for taskid: {0}, taskEntry: {1}", taskId, taskRunspaceId)); - return taskRunspaceId; - } - - /// - /// SetRemoteActivityRunspaceEntry - /// - /// - /// - /// task state can be "NotStarted", Guid, or "Completed" - /// computer name of the task - internal void SetRemoteActivityRunspaceEntry(string activityId, int taskId, object taskState, string computerName) - { - lock (_syncObject) - { - Dictionary> activityTasksRunspaceIdCollection; - _remoteRunspaceIdCollection.TryGetValue(activityId, out activityTasksRunspaceIdCollection); - - if (activityTasksRunspaceIdCollection == null) - { - activityTasksRunspaceIdCollection = new Dictionary>(); - _remoteRunspaceIdCollection.Add(activityId, activityTasksRunspaceIdCollection); - } - else - { - // computerName will be passed as null during runspace instance id assignment and task completion - // So retrieving it from task entry. - if (computerName == null) - { - Tuple taskTuple; - activityTasksRunspaceIdCollection.TryGetValue(taskId, out taskTuple); - if (taskTuple != null) - { - computerName = taskTuple.Item2; - } - } - - // Remove the taskid entry if exists - activityTasksRunspaceIdCollection.Remove(taskId); - } - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "RemoteActivityState.SetRemoteActivityRunspaceEntry: runspace entry for taskid: {0}, taskState: {1}, computerName: {2}", taskId, taskState, computerName)); - - activityTasksRunspaceIdCollection.Add(taskId, new Tuple(taskState, computerName)); - } - - if (InternalUnloaded) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "RemoteActivityState.SetRemoteActivityRunspaceEntry: persisting the Streams and RemoteActivityState")); - - // Streams and ActivityState needs to be persisted when workflow application is already unloaded internally - // Streams will have the results from task completion - _store.Save(WorkflowStoreComponents.ActivityState | WorkflowStoreComponents.Streams); - } - } - - /// - /// RemoteActivityResumeRequired - /// - /// - /// - /// - internal bool RemoteActivityResumeRequired(PSActivity activity, bool duringResumeBookmark) - { - bool activityResumeRequired = false; - if (activity is PSRemotingActivity) - { - lock (_syncObject) - { - if (_remoteRunspaceIdCollection.ContainsKey(activity.Id)) - { - // if all commands are completed remove the activity instance id's entry from remoteRunspaceCollection - // otherwise restart the activity execution to reconnect to managed nodes. - Dictionary> commandRunspaceIdCollection; - _remoteRunspaceIdCollection.TryGetValue(activity.Id, out commandRunspaceIdCollection); - if (commandRunspaceIdCollection != null) - { - if (commandRunspaceIdCollection.Any(o => o.Value.Item1 is Guid)) - { - activityResumeRequired = true; - } - else if (commandRunspaceIdCollection.Any(o => (o.Value.Item1 is string && o.Value.Item1.ToString().Equals("notstarted")))) - { - activityResumeRequired = true; - } - } - - // This method is first time called from OnResumeBookmark, if no resume required it's entry - // needs to be removed fromt the collection as activity execution has finished. - // When an activity entry is removed, remoteActivityState will be persisted - // as part of whole workflow application persistence at the end of activity completion - if (activityResumeRequired == false) - _remoteRunspaceIdCollection.Remove(activity.Id); - } - else if (duringResumeBookmark) - { - // Activity needs to be restarted/resumed if the process got crashed/terminated just after activity is bookmarked and - // in this case there will be no entry for this activity id in _remoteRunspaceIdCollection - activityResumeRequired = true; - } - } - } - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "RemoteActivityState.RemoteActivityResumeRequired returning activityResumeRequired: {0}", activityResumeRequired)); - return activityResumeRequired; - } - } - - /// - /// Collects all the information related to a workflow instance. - /// - internal class PSWorkflowApplicationInstance : PSWorkflowInstance - { - #region Private Members - - /// - /// Tracer initialization. - /// - private readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// Workflow Application executes the workflow and it is part of CLR 4.0. - /// - private WorkflowApplication workflowApplication; - - /// - /// The Guid, which represents the unique instance of a workflow. - /// - private Guid id; - - /// - /// Workflow definition. - /// - private PSWorkflowDefinition _definition; - - /// - /// Workflow output, which represent all streams from the workflow. - /// - private PowerShellStreams _streams; - - /// - /// Workflow error exception, which represent the terminating error workflow. - /// - private Exception _errorException; - - /// - /// the remote runspace instance ids collection - /// - private PSWorkflowRemoteActivityState _remoteActivityState; - - /// - /// Workflow metadatas. - /// - private PSWorkflowContext _metadatas; - - /// - /// Workflow debugger. - /// - private PSWorkflowDebugger _debugger; - - /// - /// Workflow timers. - /// - private PSWorkflowTimer _timers; - - /// - /// The workflow creation mode. - /// - private WorkflowInstanceCreationMode creationMode; - - private Dictionary asyncExecutionCollection; - - private bool PersistAfterNextPSActivity; - private bool suspendAtNextCheckpoint = false; - - - private readonly PSWorkflowInstanceStore _stores; - private static readonly Tracer _structuredTracer = new Tracer(); - private PSWorkflowJob _job; - - private bool errorExceptionLoadCalled = false; - - private bool _suppressTerminateError; - - private void HandleWorkflowApplicationCompleted(WorkflowApplicationCompletedEventArgs e) - { - if (Disposed) - return; - - _structuredTracer.Correlate(); - - if (e.CompletionState == ActivityInstanceState.Closed) - { - if (System.Threading.Interlocked.CompareExchange(ref terminalStateHandled, Handled, NotHandled) == Handled) - return; - - try - { - Tracer.WriteMessage("Workflow Application is completed and is in closed state."); - - Tracer.WriteMessage("Flatting out the PSDataCollection returned outputs."); - - foreach (KeyValuePair outvariable in e.Outputs) - { - if (outvariable.Value != null) - { - if (outvariable.Value is PSDataCollection) - { - PSDataCollection outCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(outCollection != null, "outCollection should not be NULL"); - - foreach (PSObject obj in outCollection) - { - Streams.OutputStream.Add(obj); - } - outCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection errorCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(errorCollection != null, "errorCollection should not be NULL"); - - foreach (ErrorRecord obj in errorCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - errorCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection warningCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(warningCollection != null, "warningCollection should not be NULL"); - - foreach (WarningRecord obj in warningCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - warningCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection progressCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(progressCollection != null, "progressCollection should not be NULL"); - - foreach (ProgressRecord obj in progressCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - progressCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection verboseCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(verboseCollection != null, "verboseCollection should not be NULL"); - - foreach (VerboseRecord obj in verboseCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - verboseCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection debugCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(debugCollection != null, "debugCollection should not be NULL"); - - foreach (DebugRecord obj in debugCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - debugCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection informationCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(informationCollection != null, "informationCollection should not be NULL"); - - foreach (InformationRecord obj in informationCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - informationCollection.Clear(); - } - else - { - // pass the object as is to the output stream - this.Streams.OutputStream.Add(PSObject.AsPSObject(outvariable.Value)); - } - } - } - } - catch (Exception exception) - { - Dbg.Assert(false, "An exception is raised after a workflow completed successfully. This is most likely a case of exception eating, check the exception details"); - Tracer.TraceException(exception); - } - - State = JobState.Completed; - this.PerformTaskAtTerminalState(); - - // do all cleanups here to save memory - PerformCleanupAtTerminalState(); - - if (this.OnCompleted != null) - this.OnCompleted(this); - } - - if (e.CompletionState == ActivityInstanceState.Faulted) - { - HandleWorkflowApplicationFaultedState(e.TerminationException); - } - - if (e.CompletionState == ActivityInstanceState.Canceled) - { - this.HandleWorkflowApplicationCanceled(); - } - } - - private void HandleWorkflowApplicationCanceled() - { - if (System.Threading.Interlocked.CompareExchange(ref terminalStateHandled, Handled, NotHandled) == Handled) - return; - - Tracer.WriteMessage("Workflow Application is completed in Canceled state."); - - State = JobState.Stopped; - this.PerformTaskAtTerminalState(); - - // do all cleanups here to save memory - PerformCleanupAtTerminalState(); - - if (this.OnStopped != null) - this.OnStopped(this); - } - - private ErrorRecord GetInnerErrorRecord(Exception exception) - { - IContainsErrorRecord nestedException = exception as IContainsErrorRecord; - if (nestedException == null) return null; - return nestedException.ErrorRecord; - } - - private void HandleWorkflowApplicationAborted(WorkflowApplicationAbortedEventArgs e) - { - if (Disposed) - return; - - _structuredTracer.Correlate(); - Tracer.WriteMessage("Workflow Application is completed in Aborted state."); - - // if the suspend in progress and there is some error it result in the aborted event - // explicit faulting the workflow. - if (this.callSuspendDelegate) - { - this.callSuspendDelegate = false; - HandleWorkflowApplicationFaultedState(e.Reason); - return; - } - - - // if there is an exception in the canceling the workflow the WF application throws abort event instead on canceled event. - if (_job.JobStateInfo.State == JobState.Stopping) - { - HandleWorkflowApplicationCanceled(); - return; - } - - this.Error = e.Reason; - - // do all cleanups here to save memory - PerformCleanupAtTerminalState(); - - if (this.OnAborted != null) - this.OnAborted(e.Reason, this); - } - - private UnhandledExceptionAction HandleWorkflowApplicationUnhandledException(WorkflowApplicationUnhandledExceptionEventArgs e) - { - if (Disposed) - return UnhandledExceptionAction.Terminate; - - _structuredTracer.Correlate(); - Tracer.WriteMessage("Workflow Application is completed in Unhandled exception state."); - - if (PSWorkflowContext.PSWorkflowCommonParameters.ContainsKey(Constants.PSWorkflowErrorAction) && PSWorkflowContext.PSWorkflowCommonParameters[Constants.PSWorkflowErrorAction] != null) - { - WorkflowUnhandledErrorAction action = (WorkflowUnhandledErrorAction)PSWorkflowContext.PSWorkflowCommonParameters[Constants.PSWorkflowErrorAction]; - - switch (action) - { - case WorkflowUnhandledErrorAction.Stop: - return UnhandledExceptionAction.Cancel; - - case WorkflowUnhandledErrorAction.Suspend: - return UnhandledExceptionAction.Abort; - - case WorkflowUnhandledErrorAction.Terminate: - return UnhandledExceptionAction.Terminate; - } - } - - return UnhandledExceptionAction.Terminate; - } - - private void HandlePersistence(object state) - { - if (Disposed) - return; - - // this lock will ensure the synchronization between the persistence and the force suspend (abort) method. - // if persistence is going on then it will hold the execution of force suspend - lock (ReactivateSync) - { - if (Disposed) - return; - - // if force suspend or abort has already been called IsTerminalStateAction will be true and then - // there is no need to do the persistence. - if (IsTerminalStateAction) - return; - - - if (_job.JobStateInfo.State == JobState.Running && this.workflowApplication != null) - { - try - { - Tracer.WriteMessage("PSWorkflowApplicationInstance", "HandlePersistence", id, "Persisting the workflow."); - this.workflowApplication.Persist(); - - } - catch (Exception e) - { - Tracer.TraceException(e); - SafelyHandleFaultedState(e); - return; - } - - try - { - foreach (BookmarkInfo b in this.workflowApplication.GetBookmarks()) - { - if (b.BookmarkName.Contains(PSActivity.PSPersistBookmarkPrefix)) - this.workflowApplication.ResumeBookmark(b.BookmarkName, null); - } - } - catch (Exception e) - { - // this may occur in the race condition if there are many persistable workflow and then you tried to shutdown them. - Tracer.WriteMessage("PSWorkflowApplicationInstance", "HandlePersistence", id, "There has been exception while persisting the workflow in the background thread."); - Tracer.TraceException(e); - } - } - } - } - - private void SafelyHandleFaultedState(Exception exception) - { - try - { - HandleWorkflowApplicationFaultedState(exception); - } - catch (Exception e) - { - // this may occur in the race condition if there is a persistable workflow which is getting stopped and at the same persist is called. - Tracer.WriteMessage("PSWorkflowApplicationInstance", "SafelyHandleFaultedState", id, "There has been exception while marking the workflow in faulted state in the background thread."); - Tracer.TraceException(e); - } - } - - private bool callSuspendDelegate = false; - - private PersistableIdleAction HandleWorkflowApplicationPersistableIdle(WorkflowApplicationIdleEventArgs e) - { - if (Disposed) - return PersistableIdleAction.None; - - _structuredTracer.Correlate(); - - PSPersistableIdleAction action = PSPersistableIdleAction.NotDefined; - - if (this.OnPersistableIdleAction != null) - action = this.OnPersistableIdleAction(e.Bookmarks, this.suspendAtNextCheckpoint, this); - - if(action == PSPersistableIdleAction.NotDefined) // fall back default handler - action = _job.GetPersistableIdleAction(e.Bookmarks, this.suspendAtNextCheckpoint); - - switch (action) - { - case PSPersistableIdleAction.None: - return PersistableIdleAction.None; - - case PSPersistableIdleAction.Persist: - System.Threading.ThreadPool.QueueUserWorkItem(this.HandlePersistence); - return PersistableIdleAction.None; - - case PSPersistableIdleAction.Unload: - if (Runtime.Configuration.PSWorkflowApplicationPersistUnloadTimeoutSec <= 0) - { - this.StartPersistUnloadWithZeroSeconds(); - return PersistableIdleAction.Unload; - } - else - { - this.StartPersistUnloadTimer(Runtime.Configuration.PSWorkflowApplicationPersistUnloadTimeoutSec); - return PersistableIdleAction.None; - } - - case PSPersistableIdleAction.Suspend: - this.callSuspendDelegate = true; - return PersistableIdleAction.Unload; - } - - return PersistableIdleAction.None; - } - - private void HandleWorkflowApplicationIdle(WorkflowApplicationIdleEventArgs e) - { - if (Disposed) - return; - - _structuredTracer.Correlate(); - Tracer.WriteMessage("Workflow Application is idle."); - - // there might be a possibility that stop job is being called by wfApp is Idle handling it properly so all Async execution . - if (_job.JobStateInfo.State == JobState.Stopping) - { - this.StopBookMarkedWorkflow(); - return; - } - - if (this.OnIdle != null) - this.OnIdle(e.Bookmarks, this); - } - - private void HandleWorkflowApplicationUnloaded(WorkflowApplicationEventArgs e) - { - if (Disposed) - return; - - _structuredTracer.Correlate(); - Tracer.WriteMessage("Workflow Application is unloaded."); - - if (this.callSuspendDelegate) - { - this.callSuspendDelegate = false; - // suspend logic - if (this.OnSuspended != null) - { - this.OnSuspended(this); - } - } - - if (this.OnUnloaded != null) - this.OnUnloaded(this); - } - - private void SubscribeWorkflowApplicationEvents() - { - this.workflowApplication.Completed += HandleWorkflowApplicationCompleted; - this.workflowApplication.Aborted += HandleWorkflowApplicationAborted; - this.workflowApplication.OnUnhandledException += HandleWorkflowApplicationUnhandledException; - this.workflowApplication.PersistableIdle += HandleWorkflowApplicationPersistableIdle; - this.workflowApplication.Idle += HandleWorkflowApplicationIdle; - this.workflowApplication.Unloaded += HandleWorkflowApplicationUnloaded; - } - - private void DisposeWorkflowApplication() - { - if (this.workflowApplication != null) - { - this.workflowApplication.Completed -= HandleWorkflowApplicationCompleted; - this.workflowApplication.Aborted -= HandleWorkflowApplicationAborted; - this.workflowApplication.OnUnhandledException -= HandleWorkflowApplicationUnhandledException; - this.workflowApplication.PersistableIdle -= HandleWorkflowApplicationPersistableIdle; - this.workflowApplication.Idle -= HandleWorkflowApplicationIdle; - this.workflowApplication.Unloaded -= HandleWorkflowApplicationUnloaded; - this.workflowApplication = null; - } - } - - private void OnSuspendUnloadComplete(IAsyncResult asyncResult) - { - if (Disposed) return; - - try - { - WorkflowApplication wfa = this.workflowApplication; - if (wfa != null) - { - wfa.EndUnload(asyncResult); - } - } - catch (Exception e) - { - Tracer.WriteMessage("PSWorkflowInstance", "DoSuspendInstance", id, "Not able to unload workflow application in a given timeout."); - Tracer.TraceException(e); - - HandleWorkflowApplicationFaultedState(e); - - return; - } - - this.ConfigureTimerOnUnload(); - this.DisposeWorkflowApplication(); - - if (this.OnSuspended != null) - this.OnSuspended(this); - } - - private int terminalStateHandled = NotHandled; - private const int NotHandled = 0; - private const int Handled = 1; - private void HandleWorkflowApplicationFaultedState(Exception e) - { - if (System.Threading.Interlocked.CompareExchange(ref terminalStateHandled, Handled, NotHandled) == Handled) - return; - - Tracer.WriteMessage("Workflow Application is completed in Faulted state."); - - // there might be possible of race condition here in case of Winrm shutdown. if the activity is - // executing on loop back winrm process so there might be possibility that the winrm shuts down the - // activity process fist and causing the workflow to fail in the M3P process. - // in order to avoid those situations we will ignore the remote exception if the shutdown is in progress - if (WorkflowJobSourceAdapter.GetInstance().IsShutdownInProgress) - { - if (e.GetType() == typeof(RemoteException)) - { - Tracer.WriteMessage("PSWorkflowApplicationInstance", "HandleWorkflowApplicationFaultedState", id, "Since we are in shuting down mode so ignoring the remote exception"); - Tracer.TraceException(e); - - // no need to proceed any further - return; - } - } - - // before we start handling the faulted state we need to cancel - // all running activities. Activities may be running if there is a - // parallel statement involved - StopAllAsyncExecutions(); - - State = JobState.Failed; - Exception reason = null; - - if (!_suppressTerminateError) - { - reason = e; - - ErrorRecord failureErrorRecord = GetInnerErrorRecord(reason); - if (failureErrorRecord != null) - { - reason = failureErrorRecord.Exception; - - if (PSWorkflowJob.SynchronousExecution && Streams.ErrorStream.IsOpen) - { - // If we're running synchronously from the commandline, we want to see the error - // record from the workflow without the Receive-Job decoration. Receive-Job will - // decorate the reason, so do not send it as the reason. - // Note that if the error collection is closed, we do not want to lose this data, so - // pass the exception despite the bad decoration. - Streams.ErrorStream.Add(failureErrorRecord); - reason = null; - } - } - else - { - // No error record, a raw exception was thrown. See if we have location - // information that we can wrap it in. - HostSettingCommandMetadata commandMetadata = null; - - if (_paramDefaults != null) - commandMetadata = _paramDefaults.HostCommandMetadata; - - if (commandMetadata != null) - { - ScriptPosition scriptStart = new ScriptPosition( - commandMetadata.CommandName, - commandMetadata.StartLineNumber, - commandMetadata.StartColumnNumber, - null); - ScriptPosition scriptEnd = new ScriptPosition( - commandMetadata.CommandName, - commandMetadata.EndLineNumber, - commandMetadata.EndColumnNumber, - null); - ScriptExtent extent = new ScriptExtent(scriptStart, scriptEnd); - - reason = new JobFailedException(reason, extent); - } - } - } - - this.Error = reason; - - this.PerformTaskAtTerminalState(); - - // do all cleanups here to save memory - PerformCleanupAtTerminalState(); - - if (this.OnFaulted != null) - this.OnFaulted(reason, this); - } - - private void CheckDisposed() - { - if (Disposed) - { - Debug.Assert(Disposed, "PSWorkflowInstance has been disposed " + Id); - } - } - - private void PerformCleanupAtTerminalState() - { - DisposeWorkflowApplication(); - ConfigureTimerOnUnload(); - if (_streams != null) - UnregisterHandlersForDataAdding(_streams); - } - - #endregion Private Members - - #region Internal Members - - /// - /// Workflow instance constructor. - /// - /// - /// The workflow definition. - /// The metadata which includes parameters etc. - /// This is input coming from pipeline, which is added to the input stream. - /// - internal PSWorkflowApplicationInstance( - PSWorkflowRuntime runtime, - PSWorkflowDefinition definition, - PSWorkflowContext metadata, - PSDataCollection pipelineInput, - PSWorkflowJob job) - { - Tracer.WriteMessage("Creating Workflow instance."); - InitializePSWorkflowApplicationInstance(runtime); - this._definition = definition; - this._metadatas = metadata; - this._streams = new PowerShellStreams(pipelineInput); - RegisterHandlersForDataAdding(_streams); - this._timers = new PSWorkflowTimer(this); - this.creationMode = WorkflowInstanceCreationMode.Normal; - - _job = job; - _stores = Runtime.Configuration.CreatePSWorkflowInstanceStore(this); - - this._remoteActivityState = new PSWorkflowRemoteActivityState(_stores); - } - - private void InitializePSWorkflowApplicationInstance(PSWorkflowRuntime runtime) - { - if (runtime == null) - throw new ArgumentNullException("runtime"); - - this.PersistAfterNextPSActivity = false; - this.suspendAtNextCheckpoint = false; - Runtime = runtime; - - this.asyncExecutionCollection = new Dictionary(); - this.ForceDisableStartOrEndPersistence = false; - - if (Runtime.Configuration.PSWorkflowApplicationPersistUnloadTimeoutSec > 0) - { - PersistUnloadTimer = new Timer(Convert.ToDouble(Runtime.Configuration.PSWorkflowApplicationPersistUnloadTimeoutSec * 1000)); - PersistUnloadTimer.Elapsed += new ElapsedEventHandler(PersistUnloadTimer_Elapsed); - PersistUnloadTimer.AutoReset = false; - } - - this._debugger = new PSWorkflowDebugger(this); - } - - private void RegisterHandlersForDataAdding(PowerShellStreams streams) - { - streams.OutputStream.DataAdding += HandleOutputDataAdding; - streams.ErrorStream.DataAdding += HandleErrorDataAdding; - streams.DebugStream.DataAdding += HandleInformationalDataAdding; - streams.VerboseStream.DataAdding += HandleInformationalDataAdding; - streams.WarningStream.DataAdding += HandleInformationalDataAdding; - streams.ProgressStream.DataAdding += HandleProgressDataAdding; - streams.InformationStream.DataAdding += HandleInformationDataAdding; - } - - private void UnregisterHandlersForDataAdding(PowerShellStreams streams) - { - if (streams.OutputStream != null) - streams.OutputStream.DataAdding -= HandleOutputDataAdding; - if (streams.ErrorStream != null) - streams.ErrorStream.DataAdding -= HandleErrorDataAdding; - if (streams.DebugStream != null) - streams.DebugStream.DataAdding -= HandleInformationalDataAdding; - if (streams.VerboseStream != null) - streams.VerboseStream.DataAdding -= HandleInformationalDataAdding; - if (streams.WarningStream != null) - streams.WarningStream.DataAdding -= HandleInformationalDataAdding; - if (streams.ProgressStream != null) - streams.ProgressStream.DataAdding -= HandleProgressDataAdding; - if (streams.InformationStream != null) - streams.InformationStream.DataAdding -= HandleInformationDataAdding; - } - - private const string LocalHost = "localhost"; - private void HandleOutputDataAdding(object sender, DataAddingEventArgs e) - { - PSObject psObject = (PSObject) e.ItemAdded; - - if (psObject == null) return; - - PSActivity.AddIdentifierInfoToOutput(psObject, JobInstanceId, LocalHost); - } - - private void HandleErrorDataAdding(object sender, DataAddingEventArgs e) - { - ErrorRecord errorRecord = (ErrorRecord)e.ItemAdded; - if (errorRecord == null) return; - - PSActivity.AddIdentifierInfoToErrorRecord(errorRecord, LocalHost, JobInstanceId); - } - - private void HandleProgressDataAdding(object sender, DataAddingEventArgs e) - { - ProgressRecord progressRecord = (ProgressRecord)e.ItemAdded; - if (progressRecord == null) return; - - progressRecord.CurrentOperation = PSActivity.AddIdentifierInfoToString(JobInstanceId, LocalHost, - progressRecord.CurrentOperation); - } - - private void HandleInformationDataAdding(object sender, DataAddingEventArgs e) - { - InformationRecord informationRecord = (InformationRecord)e.ItemAdded; - if (informationRecord == null) return; - - informationRecord.Source = PSActivity.AddIdentifierInfoToString(JobInstanceId, LocalHost, - informationRecord.Source); - } - - private void HandleInformationalDataAdding(object sender, DataAddingEventArgs e) - { - InformationalRecord informationalRecord = (InformationalRecord)e.ItemAdded; - if (informationalRecord == null) return; - - informationalRecord.Message = PSActivity.AddIdentifierInfoToString(JobInstanceId, LocalHost, - informationalRecord.Message); - } - - private Guid JobInstanceId - { - get - { - return _job != null ? _job.InstanceId : Guid.Empty; - } - } - - /// - /// Workflow instance constructor for shutdown or crashed workflows. - /// - /// - /// - internal PSWorkflowApplicationInstance(PSWorkflowRuntime runtime, PSWorkflowId instanceId) - { - Tracer.WriteMessage("Creating Workflow instance after crash and shutdown workflow."); - InitializePSWorkflowApplicationInstance(runtime); - this._definition = null; - this._metadatas = null; - this._streams = null; - this._timers = null; - this.id = instanceId.Guid; - this.creationMode = WorkflowInstanceCreationMode.AfterCrashOrShutdown; - - _stores = Runtime.Configuration.CreatePSWorkflowInstanceStore(this); - this._remoteActivityState = null; - } - - /// - /// Gets the Guid of workflow instance. - /// - internal override Guid Id - { - get { return this.id; } - } - - - /// - /// Load instance for resuming the workflow - /// - internal override void DoLoadInstanceForReactivation() - { - CheckDisposed(); - - try - { - lock (SyncLock) - { - Tracer.WriteMessage("Loading for Workflow resumption."); - - if (this.PSWorkflowDefinition.Workflow == null) - { - ArgumentException exception = new ArgumentException(Resources.NoWorkflowProvided); - Tracer.TraceException(exception); - throw exception; - } - - this.workflowApplication = new WorkflowApplication(this.PSWorkflowDefinition.Workflow); - this.wfAppNeverLoaded = false; - SubscribeWorkflowApplicationEvents(); - this.ConfigureAllExtensions(); - - // loading the workflow context from the store - this.workflowApplication.Load(this.id); - - SetInternalUnloaded(false); - - Tracer.WriteMessage("Workflow is loaded for reactivation, Guid = " + this.id.ToString("D", CultureInfo.CurrentCulture)); - } - } - catch(Exception e) - { - Tracer.WriteMessage("PSWorkflowApplicationInstance", "DoLoadInstanceForReactivation", id, "There has been an exception while loading the workflow state from persistence store."); - Tracer.TraceException(e); - var invalidOpException = new InvalidOperationException(Resources.WorkflowInstanceIncompletelyPersisted, e); - HandleWorkflowApplicationFaultedState(invalidOpException); - throw invalidOpException; - } - } - - /// - /// PerformTaskAtTerminalState - /// - internal override void PerformTaskAtTerminalState() - { - - // Cleanup - if (this.PSWorkflowDefinition != null && this.PSWorkflowDefinition.Workflow != null) - { - this.PSWorkflowDefinition.Workflow = null; - } - - - if (this.CheckForStartOrEndPersistence()) - { - try - { - //Serializing Data to disk - _stores.Save(WorkflowStoreComponents.Metadata - | WorkflowStoreComponents.Streams - | WorkflowStoreComponents.TerminatingError - | WorkflowStoreComponents.Timer - | WorkflowStoreComponents.JobState - | WorkflowStoreComponents.ActivityState - ); - } - catch (Exception e) - { - Tracer.WriteMessage("Serialization exception occurred while saving workflow to persistence store"); - Tracer.TraceException(e); - // Trace details - if (Streams.ErrorStream != null && Streams.ErrorStream.IsOpen) - { - Streams.ErrorStream.Add(new ErrorRecord(e, "Workflow_Serialization_Error", ErrorCategory.ParserError, null)); - } - else - { - Tracer.WriteMessage("Error stream is not in Open state"); - } - } - } - - Streams.CloseAll(); - - // Stopping the timers - if (Timer != null) - { - Timer.StopTimer(WorkflowTimerType.RunningTimer); - } - - _structuredTracer.EndWorkflowExecution(_job.InstanceId); - } - - /// - /// Save streams if they are not already persisted - /// - /// false, if persistence was attempted and there - /// was an error - /// true - otherwise - internal override bool SaveStreamsIfNecessary() - { - // if there was start and end persistence, there is - // no need to persist again, simply return - if (CheckForStartOrEndPersistence()) - return true; - - try - { - _stores.Save(WorkflowStoreComponents.Streams); - } - catch (Exception e) - { - // it is fine to eat an exception here - // the user explicitly disabled persist and - // we are doing a best effort here - // any failure means we will simply return - // the caller will handle this scenario - Tracer.TraceException(e); - return false; - } - return true; - } - - #endregion Internal Members - - #region Public Members - - /// - /// PSWorkflowJob - /// - public override PSWorkflowJob PSWorkflowJob - { - get { return _job; } - - protected internal set - { - _job = value; - } - } - - /// - /// Gets the Guid of workflow instance. - /// - public override PSWorkflowId InstanceId - { - get { return new PSWorkflowId(this.id); } - } - - /// - /// Gets the Creation Context of workflow job. - /// - public override Dictionary CreationContext - { - get - { - Dictionary creationContext = null; - - object jobCreationContext = null; - if (this.PSWorkflowContext.JobMetadata.TryGetValue(Constants.WorkflowJobCreationContext, out jobCreationContext)) - { - creationContext = jobCreationContext as Dictionary; - } - - return creationContext; - } - } - - /// - /// InstanceStore - /// - public override PSWorkflowInstanceStore InstanceStore - { - get - { - return _stores; - } - } - - /// - /// Gets the definition of workflow. - /// - public override PSWorkflowDefinition PSWorkflowDefinition - { - get - { - if (this._definition == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - lock (SyncLock) - { - if (this._definition == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - try - { - _stores.Load(WorkflowStoreComponents.Definition); - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the workflow definition"); - - Tracer.TraceException(e); - - this._definition = new PSWorkflowDefinition(null, string.Empty, string.Empty); - - - } - } - } - } - - return this._definition; - } - - set - { - CheckDisposed(); - this._definition = value; - } - } - - /// - /// Gets the streams of workflow. - /// - public override PowerShellStreams Streams - { - get - { - if (this._streams == null && - (_streamsDisposed || this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown)) - { - lock (SyncLock) - { - if (this._streams == null && - (_streamsDisposed || this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown)) - { - try - { - _stores.Load(WorkflowStoreComponents.Streams); - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the workflow streams"); - - Tracer.TraceException(e); - - this._streams = new PowerShellStreams(null); - Tracer.WriteMessage("Marking the job to the faulted state."); - } - RegisterHandlersForDataAdding(_streams); - } - } - } - return this._streams; - } - - set - { - CheckDisposed(); - if (_streams == value) - return; - - if (_streams != null) - { - UnregisterHandlersForDataAdding(_streams); - _streams.Dispose(); - } - this._streams = value; - RegisterHandlersForDataAdding(_streams); - } - } - - /// - /// Gets the remote runspace instance ids collection. - /// - public override PSWorkflowRemoteActivityState RemoteActivityState - { - get - { - if (this._remoteActivityState == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - lock (SyncLock) - { - if (this._remoteActivityState == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - try - { - _stores.Load(WorkflowStoreComponents.ActivityState); - - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the RemoteActivityState"); - - Tracer.TraceException(e); - - this.RemoteActivityState = new PSWorkflowRemoteActivityState(_stores); - - Tracer.WriteMessage("Marking the job to the faulted state."); - } - } - } - } - - return this._remoteActivityState; - } - - set - { - CheckDisposed(); - this._remoteActivityState = value; - } - } - - /// - /// Gets the streams of workflow. - /// - public override Exception Error - { - get - { - if (this._errorException == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown && errorExceptionLoadCalled == false) - { - lock (SyncLock) - { - if (this._errorException == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown && errorExceptionLoadCalled == false) - { - try - { - errorExceptionLoadCalled = true; - _stores.Load(WorkflowStoreComponents.TerminatingError); - - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the workflow terminating error"); - - Tracer.TraceException(e); - - this._errorException = null; - - Tracer.WriteMessage("Marking the job to the faulted state."); - } - } - } - } - - return this._errorException; - } - - set - { - CheckDisposed(); - this._errorException = value; - } - } - - /// - /// Gets the timers of workflow. - /// - public override PSWorkflowTimer Timer - { - get - { - if (this._timers == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - lock (SyncLock) - { - if (this._timers == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - try - { - _stores.Load(WorkflowStoreComponents.Timer); - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the workflow timer"); - - Tracer.TraceException(e); - - this._timers = null; - - Tracer.WriteMessage("Marking the job to the faulted state."); - } - } - } - } - - return this._timers; - } - - set - { - CheckDisposed(); - this._timers = value; - } - } - - /// - /// Gets the metadatas of workflow. - /// - public override PSWorkflowContext PSWorkflowContext - { - get - { - if (this._metadatas == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - lock (SyncLock) - { - if (this._metadatas == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - try - { - _stores.Load(WorkflowStoreComponents.Metadata); - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the workflow metadata"); - - Tracer.TraceException(e); - - this._metadatas = new PSWorkflowContext(); - - } - } - } - } - - return this._metadatas; - } - - set - { - CheckDisposed(); - this._metadatas = value; - } - - } - - /// - /// PSWorkflowDebugger - /// - internal override PSWorkflowDebugger PSWorkflowDebugger - { - get { return this._debugger; } - } - - /// - /// Dispose the streams to save memory - /// - public override void DisposeStreams() - { - if (_streams == null) return; - lock (SyncLock) - { - if (_streams == null) return; - _streamsDisposed = true; - UnregisterHandlersForDataAdding(_streams); - _streams.Dispose(); - _streams = null; - } - } - - private bool _streamsDisposed; - - #endregion Public Members - - #region Protected override members - - /// - /// DoStopInstance - /// - protected override void DoStopInstance() - { - // This lock required to wait for any inprogress workflow reactivation - lock (ReactivateSync) - { - // this flag ensures that the workflow application will not be loaded again since we are stopping the workflow instance - IsTerminalStateAction = true; - // this flag ensures that we don't unload the wfApplication instance since we are stopping the workflow. - PersistIdleTimerInProgressOrTriggered = false; - } - - if (this.workflowApplication != null) - { - this.workflowApplication.Cancel(); - return; - } - else - { - this.StopBookMarkedWorkflow(); - } - } - - /// - /// DoAbortInstance - /// - /// Reason for aborting workflow. - protected override void DoAbortInstance(string reason) - { - if (Disposed) - return; - - this.workflowApplication.Abort(reason); - } - - /// - /// DoTerminateInstance - /// - /// Reason message for termination - protected override void DoTerminateInstance(string reason) - { - // TimeoutException happens when workflowapplication can not be terminated in 30 sec. This happens on CTL VMs. - // if that timeout exception happens force stop job hangs on Finished event. - // Specifying timeout value to max value - this.workflowApplication.Terminate(reason, TimeSpan.MaxValue); - } - - /// - /// DoTerminateInstance - /// - /// Reason message for termination - /// Suppress error for termination - protected override void DoTerminateInstance(string reason, bool suppressError) - { - _suppressTerminateError = suppressError; - DoTerminateInstance(reason); - } - - /// - /// DoResumeInstance - /// - protected override void DoResumeInstance(string label) - { - Tracer.WriteMessage("Trying to resume workflow"); - - this.IsTerminalStateAction = false; - this.suspendAtNextCheckpoint = false; - - string prefix = string.IsNullOrEmpty(label) ? PSActivity.PSBookmarkPrefix : PSActivity.PSSuspendBookmarkPrefix + label; - - ReadOnlyCollection bookmarkInfos = this.workflowApplication.GetBookmarks(); - if (bookmarkInfos.Count > 0) - { - foreach (BookmarkInfo bookmarkInfo in bookmarkInfos) - { - if (bookmarkInfo.BookmarkName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - Bookmark bookmark = new Bookmark(bookmarkInfo.BookmarkName); - this.workflowApplication.ResumeBookmark(bookmark, ActivityOnResumeAction.Restart); - } - } - } - else - { - this.workflowApplication.Run(); - } - - this.StartTimerOnResume(); - Tracer.WriteMessage("Workflow resumed"); - } - - /// - /// DoSuspendInstance - /// - /// - protected override void DoSuspendInstance(bool notStarted) - { - if (notStarted && this.CheckForStartOrEndPersistence()) - { - this.workflowApplication.BeginUnload(OnSuspendUnloadComplete, null); - } - else - { - this.suspendAtNextCheckpoint = true; - } - } - - /// - /// DoExecuteInstance - /// - protected override void DoExecuteInstance() - { - if (Disposed) - return; - - Tracer.WriteMessage("Starting workflow execution"); - _structuredTracer.BeginWorkflowExecution(_job.InstanceId); - - try - { - this.workflowApplication.Run(); - } - catch (Exception e) - { - HandleWorkflowApplicationFaultedState(e); - return; - } - - this.StartTimers(); - Tracer.WriteMessage("Workflow application started execution"); - } - - /// - /// DoResumeBookmark - /// - /// - /// - protected override void DoResumeBookmark(Bookmark bookmark, object state) - { - if (bookmark == null) - throw new ArgumentNullException("bookmark"); - - ReactivateWorkflowInternal(bookmark, state, true); - } - - /// - /// Loads the xaml to create an executable activity. - /// - protected override void DoCreateInstance() - { - CheckDisposed(); - _structuredTracer.LoadingWorkflowForExecution(this.id); - lock (SyncLock) - { - Tracer.WriteMessage("Loading Workflow"); - - if (this.PSWorkflowDefinition.Workflow == null) - { - ArgumentException exception = new ArgumentException(Resources.NoWorkflowProvided); - Tracer.TraceException(exception); - throw exception; - } - - this.workflowApplication = this.PSWorkflowContext.WorkflowParameters == null - ? new WorkflowApplication(this.PSWorkflowDefinition.Workflow) - : new WorkflowApplication(this.PSWorkflowDefinition.Workflow, this.PSWorkflowContext.WorkflowParameters); - this.wfAppNeverLoaded = false; - - this.id = this.workflowApplication.Id; - SubscribeWorkflowApplicationEvents(); - - this.ConfigureAllExtensions(); - this.SetupTimers(); - - this.PersistBeforeExecution(); - - SetInternalUnloaded(false); - - Tracer.WriteMessage("Workflow is loaded, Guid = " + this.id.ToString("D", CultureInfo.CurrentCulture)); - } - _structuredTracer.WorkflowLoadedForExecution(this.id); - } - - /// - /// Remove - /// - protected override void DoRemoveInstance() - { - CheckDisposed(); - _stores.Delete(); - } - - /// - /// DoPersistInstance - /// - protected override void DoPersistInstance() - { - if (_job.JobStateInfo.State == JobState.Completed || _job.JobStateInfo.State == JobState.Stopped || _job.JobStateInfo.State == JobState.Failed) - { - // when job is reached to a terminal state so we just need to persist the required information - _stores.Save(WorkflowStoreComponents.Metadata - | WorkflowStoreComponents.Streams - | WorkflowStoreComponents.TerminatingError - | WorkflowStoreComponents.JobState - ); - } - else - { - // if job has not reached the terminal state then the persistence will happen after the completion of currently running activity. - this.PersistAfterNextPSActivity = true; - } - } - - /// - /// DoGetPersistableIdleAction - /// - /// - /// - /// - protected override PSPersistableIdleAction DoGetPersistableIdleAction(ReadOnlyCollection bookmarks, bool externalSuspendRequest) - { - if (bookmarks.Count == 0) - return PSPersistableIdleAction.None; - - Collection bks = new Collection(); - foreach (BookmarkInfo bk in bookmarks) - bks.Add(bk); - - // Check if this is a suspend request - if (VerifyRequest(bks, PSActivity.PSSuspendBookmarkPrefix)) - return PSPersistableIdleAction.Suspend; - - // Check if this is a persist request - if (VerifyRequest(bks, PSActivity.PSPersistBookmarkPrefix)) - { - if (externalSuspendRequest) - return PSPersistableIdleAction.Suspend; - - return PSPersistableIdleAction.Persist; - } - - if (bks.Count > 0) - return PSPersistableIdleAction.Unload; - - return PSPersistableIdleAction.None; - } - - // This will return true when all bookmarks are of type same prefix. (like suspend or persist) - // We may have branches (i.e.: in a parallel statement) where bookmarks are - // being used for other things. - private bool VerifyRequest(Collection bookmarks, string prefix) - { - if ((bookmarks == null) || (bookmarks.Count <= 0)) - return false; - - Collection toRemove = new Collection(); - bool rc = true; - foreach (BookmarkInfo bookmark in bookmarks) - { - if (!bookmark.BookmarkName.Contains(prefix)) - { - rc = false; - } - else - { - toRemove.Add(bookmark); - } - } - - foreach (BookmarkInfo r in toRemove) - { - bookmarks.Remove(r); - } - - return rc; - } - - - /// - /// Dispose - /// - protected override void Dispose(bool disposing) - { - if (!disposing || Disposed) - return; - - lock (SyncLock) - { - if (Disposed) - return; - - Disposed = true; - - lock (ReactivateSync) - { - this.IsTerminalStateAction = true; - this.PersistIdleTimerInProgressOrTriggered = false; - SetInternalUnloaded(false); - } - - this.ConfigureTimerOnUnload(); - - // saving the workflow application handle into the temporary variable - // then unregistering the workflow application handle - // temporary variable will be used to call abort if workflow is in running state - WorkflowApplication wf = this.workflowApplication; - DisposeWorkflowApplication(); - - if (this._job.JobStateInfo.State == JobState.Running && wf != null) - { - try - { - wf.Abort("Disposing the job"); - } - catch (Exception) - { - // We are not re-throwing any exception during Dispose - } - } - - if (_paramDefaults != null) - { - _paramDefaults.Dispose(); - _paramDefaults = null; - } - - if (_streams != null) - { - UnregisterHandlersForDataAdding(_streams); - _streams.Dispose(); - } - - if (_timers != null) - _timers.Dispose(); - - if (_debugger != null) - _debugger.Dispose(); - - DisposePersistUnloadTimer(); - - base.Dispose(disposing); - } - } - - #endregion Protected override members - - #region Private Members - - private void SetupTimers() - { - if (Timer != null && PSWorkflowContext.PSWorkflowCommonParameters != null) - { - // Running time - if (PSWorkflowContext.PSWorkflowCommonParameters.ContainsKey(Constants.PSRunningTime)) - { - int timeInSeconds = Convert.ToInt32(PSWorkflowContext.PSWorkflowCommonParameters[Constants.PSRunningTime], CultureInfo.CurrentCulture); - - if (timeInSeconds > 0) - { - Timer.SetupTimer(WorkflowTimerType.RunningTimer, TimeSpan.FromSeconds(timeInSeconds)); - } - } - - // Elapsed time - if (PSWorkflowContext.PSWorkflowCommonParameters.ContainsKey(Constants.PSElapsedTime)) - { - int timeInSeconds = Convert.ToInt32(PSWorkflowContext.PSWorkflowCommonParameters[Constants.PSElapsedTime], CultureInfo.CurrentCulture); - - if (timeInSeconds > 0) - { - Timer.SetupTimer(WorkflowTimerType.ElapsedTimer, TimeSpan.FromSeconds(timeInSeconds)); - } - } - - // Persist Interval - bool psPersistValue = true; - if (PSWorkflowContext.PSWorkflowCommonParameters.ContainsKey(Constants.Persist)) - { - psPersistValue = Convert.ToBoolean(PSWorkflowContext.PSWorkflowCommonParameters[Constants.Persist], CultureInfo.CurrentCulture); - } - } - } - - private HostParameterDefaults _paramDefaults; - private void ConfigureAllExtensions() - { - // declaring instance store - this.workflowApplication.InstanceStore = _stores.CreateInstanceStore(); - - var IOParticipant = _stores.CreatePersistenceIOParticipant(); - if (IOParticipant != null) - this.workflowApplication.Extensions.Add(IOParticipant); - - // adding the tracking participants - this.workflowApplication.Extensions.Add(this.GetTrackingParticipant()); - - // adding the custom extensions - IEnumerable extensions = this.Runtime.Configuration.CreateWorkflowExtensions(); - if (extensions != null) - { - foreach (object extension in extensions) - { - this.workflowApplication.Extensions.Add(extension); - } - } - - // adding the custom extension creation functions - IEnumerable> extensionFunctions = this.Runtime.Configuration.CreateWorkflowExtensionCreationFunctions(); - if (extensionFunctions != null) - { - foreach(Func extensionFunc in extensionFunctions) - { - this.workflowApplication.Extensions.Add(extensionFunc); - } - } - - _paramDefaults = new HostParameterDefaults(); - - if (this.PSWorkflowContext.PSWorkflowCommonParameters != null) - { - foreach (KeyValuePair param in this.PSWorkflowContext.PSWorkflowCommonParameters) - { - if (param.Key != Constants.PSRunningTime && param.Key != Constants.PSElapsedTime) - { - _paramDefaults.Parameters.Add(param.Key, param.Value); - } - } - } - - if (this.PSWorkflowContext.PrivateMetadata != null) - { - _paramDefaults.Parameters[Constants.PrivateMetadata] = this.PSWorkflowContext.PrivateMetadata; - } - - // Job related parameters - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataName)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataName)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataName]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataInstanceId)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataInstanceId)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataInstanceId]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataSessionId)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataSessionId)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataSessionId]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataCommand)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataCommand)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataCommand]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentName)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataParentName)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataParentName]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentInstanceId)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataParentInstanceId)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataParentInstanceId]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentSessionId)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataParentSessionId)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataParentSessionId]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentCommand)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataParentCommand)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataParentCommand]; - } - - _paramDefaults.Parameters["WorkflowInstanceId"] = this.InstanceId; - - _paramDefaults.Parameters["Input"] = this.Streams.InputStream; - _paramDefaults.Parameters["Result"] = this.Streams.OutputStream; - _paramDefaults.Parameters["PSError"] = this.Streams.ErrorStream; - _paramDefaults.Parameters["PSWarning"] = this.Streams.WarningStream; - _paramDefaults.Parameters["PSProgress"] = this.Streams.ProgressStream; - _paramDefaults.Parameters["PSVerbose"] = this.Streams.VerboseStream; - _paramDefaults.Parameters["PSDebug"] = this.Streams.DebugStream; - _paramDefaults.Parameters["PSInformation"] = this.Streams.InformationStream; - - // Assign PSActivityHost here - _paramDefaults.Runtime = Runtime; - - _paramDefaults.JobInstanceId = _job.InstanceId; - - // Assign PSHostPersistDelegate here - Func hostDelegate = this.CheckForPersistenceAfterPSActivity; - _paramDefaults.HostPersistenceDelegate = hostDelegate; - - Action activateDelegate = this.ReactivateWorkflow; - _paramDefaults.ActivateDelegate = activateDelegate; - - _paramDefaults.AsyncExecutionCollection = this.asyncExecutionCollection; - _paramDefaults.RemoteActivityState = this.RemoteActivityState; - - System.Activities.Hosting.SymbolResolver resolver = new System.Activities.Hosting.SymbolResolver(); - resolver.Add("ParameterDefaults", _paramDefaults); - - this.workflowApplication.Extensions.Add(resolver); - this.workflowApplication.Extensions.Add(_paramDefaults); - } - - - private void PersistBeforeExecution() - { - if (this.CheckForStartOrEndPersistence()) - { - try - { - // For robustness -// _structuredTracer.PersistingWorkflow(this.id, (_runtime.Configuration as PSWorkflowConfigurationProvider).InstanceStorePath); - this.workflowApplication.Persist(); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - } - - private void StartTimers() - { - if (Timer != null) - { - Timer.StartTimer(WorkflowTimerType.RunningTimer); - Timer.StartTimer(WorkflowTimerType.ElapsedTimer); - } - } - - private void ConfigureTimerOnUnload() - { - if (Timer != null) - { - Timer.StopTimer(WorkflowTimerType.RunningTimer); - } - } - - private void StartTimerOnResume() - { - - if (Timer != null) - { - Timer.StartTimer(WorkflowTimerType.RunningTimer); - } - } - - /// - /// Construct the workflow tracking participant. - /// - /// Returns the workflow tracking participant. - private PSWorkflowTrackingParticipant GetTrackingParticipant() - { - const String all = "*"; - PSWorkflowTrackingParticipant participant = new PSWorkflowTrackingParticipant(this._debugger) - { - // Create a tracking profile to subscribe for tracking records - // In this sample the profile subscribes for CustomTrackingRecords, - // workflow instance records and activity state records - TrackingProfile = new TrackingProfile() - { - Name = "WorkflowTrackingProfile", - Queries = - { - new CustomTrackingQuery() - { - Name = all, - ActivityName = all - }, - new WorkflowInstanceQuery() - { - // Limit workflow instance tracking records for started and completed workflow states - States = { - WorkflowInstanceStates.Started, - WorkflowInstanceStates.Completed, - WorkflowInstanceStates.Persisted, - WorkflowInstanceStates.UnhandledException - }, - }, - new ActivityStateQuery() - { - // Subscribe for track records from all activities for all states - ActivityName = all, - States = { all }, - - // Extract workflow variables and arguments as a part of the activity tracking record - // VariableName = "*" allows for extraction of all variables in the scope - // of the activity - Variables = - { - { all } - }, - - Arguments = - { - { all } - } - } - } - } - }; - - return participant; - } - - private bool CheckForStartOrEndPersistence() - { - // This is for the unit testing - // Inside the unit test the force disable attribute can be set to true. - if (ForceDisableStartOrEndPersistence == true) - return false; - - if (this.PSWorkflowContext != null && this.PSWorkflowContext.PSWorkflowCommonParameters != null && this.PSWorkflowContext.PSWorkflowCommonParameters.ContainsKey(Constants.Persist)) - { - bool? value = this.PSWorkflowContext.PSWorkflowCommonParameters[Constants.Persist] as bool?; - if (value != null && value == false) - return false; - } - - return true; - } - - private bool CheckForPersistenceAfterPSActivity() - { - bool value = this.PersistAfterNextPSActivity; - this.PersistAfterNextPSActivity = false; - - return value; - } - - #endregion Private Members - - #region Persist and Reactivation - - internal bool InternalUnloaded; - - private void SetInternalUnloaded(bool value) - { - InternalUnloaded = value; - - if (this.RemoteActivityState != null) - this.RemoteActivityState.InternalUnloaded = value; - } - - private bool wfAppNeverLoaded = true; - - private bool PersistIdleTimerInProgressOrTriggered = false; - private bool IsTerminalStateAction = false; - - private Timer PersistUnloadTimer; - private object ReactivateSync = new object(); - - private void ReactivateWorkflow(object state) - { - Debug.Assert(state != null, "State not passed correctly to ReactivateWorkflow"); - Bookmark bookmark = state as Bookmark; - Debug.Assert(bookmark != null, "Bookmark not passed correctly to ReactivateWorkflow"); - - ReactivateWorkflowInternal(bookmark, null, false); - } - - private void ReactivateWorkflowInternal(Bookmark bookmark, object state, bool validateBookmark) - { - if (Disposed || IsTerminalStateAction) - return; - - CheckAndLoadInstanceForReactivation(); - - Debug.Assert(this.workflowApplication != null, "PSWorkflowApplicationInstance is not initialized properly"); - if (this.workflowApplication == null) - return; - - if (validateBookmark && this.CheckIfBookmarkExistInCollection(bookmark.Name, this.workflowApplication.GetBookmarks()) == false) - { - Tracer.WriteMessage("PSWorkflowInstance", "ReactivateWorkflowInternal", this.id,"Invalid bookmark: '{0}'.", bookmark.Name); - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidBookmark,bookmark.Name)); - } - - try - { - this.workflowApplication.ResumeBookmark(bookmark, state); - } - catch (Exception e) - { - // there should not be any exception if any then writing to the logs - Tracer.TraceException(e); - HandleWorkflowApplicationFaultedState(e); - } - } - - private void CheckAndLoadInstanceForReactivation() - { - if (Disposed || IsTerminalStateAction || (PersistIdleTimerInProgressOrTriggered == false && this.wfAppNeverLoaded == false)) - return; - - lock (ReactivateSync) - { - if (Disposed || IsTerminalStateAction || (PersistIdleTimerInProgressOrTriggered == false && this.wfAppNeverLoaded == false)) - return; - - if (this.InternalUnloaded || this.wfAppNeverLoaded) - { - this.DoLoadInstanceForReactivation(); - SetInternalUnloaded(false); - } - - PersistIdleTimerInProgressOrTriggered = false; - } - } - - private bool CheckIfBookmarkExistInCollection(string bookmarkName, ReadOnlyCollection bookmarks) - { - foreach (BookmarkInfo bookmark in bookmarks) - { - if (bookmarkName == bookmark.BookmarkName) - return true; - } - - return false; - } - - private void StopBookMarkedWorkflow() - { - // Workflow is currently in booked marked state - // we will try to cancel all async operations - // and then perform the terminal tasks related to stop workflow - - if (Disposed) - return; - - if (System.Threading.Interlocked.CompareExchange(ref terminalStateHandled, Handled, NotHandled) == Handled) - return; - - lock (ReactivateSync) - { - if (Disposed) - return; - - IsTerminalStateAction = true; - - StopAllAsyncExecutions(); - - Tracer.WriteMessage("Workflow is in Canceled state."); - - State = JobState.Stopped; - this.PerformTaskAtTerminalState(); - - // do all cleanups here to save memory - PerformCleanupAtTerminalState(); - - if (this.OnStopped != null) - this.OnStopped(this); - } - } - - private void StopAllAsyncExecutions() - { - if (Disposed) - return; - - if (this.asyncExecutionCollection != null && this.asyncExecutionCollection.Count > 0) - { - // This is not a full fix: - // there could be a race condition where we have taken the copy but context is removed from by activity and disposed in that case contextinstance.cancel will throw - // there could be one more possibility that is if copy has been made and after that activity adds a new context and will not get canceled and streams will get closed so async execution might throw - // the solution is make every these operation on async execution collection thread safe - - foreach (PSActivityContext psActivityContextInstance in this.asyncExecutionCollection.Values.ToList()) - { - if (psActivityContextInstance != null) - { - psActivityContextInstance.IsCanceled = true; - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity: Executing cancel request.")); - - psActivityContextInstance.Cancel(); - } - } - - this.asyncExecutionCollection.Clear(); - } - } - - private void StartPersistUnloadWithZeroSeconds() - { - lock (ReactivateSync) - { - if (IsTerminalStateAction) - return; - - PersistIdleTimerInProgressOrTriggered = true; - SetInternalUnloaded(true); - } - } - - private void StartPersistUnloadTimer(int delaySeconds) - { - // return if persist idle is in progress - if (PersistIdleTimerInProgressOrTriggered) - return; - - lock (ReactivateSync) - { - // return if persist idle is in progress - if (PersistIdleTimerInProgressOrTriggered) - return; - - if (IsTerminalStateAction) - return; - - PersistIdleTimerInProgressOrTriggered = true; - PersistUnloadTimer.Enabled = true; - } - } - - private void DisposePersistUnloadTimer() - { - if (PersistUnloadTimer == null) - return; - - lock (ReactivateSync) - { - if (PersistUnloadTimer == null) - return; - - PersistUnloadTimer.Elapsed -= new ElapsedEventHandler(PersistUnloadTimer_Elapsed); - PersistUnloadTimer.Dispose(); - PersistUnloadTimer = null; - } - } - - private void PersistUnloadTimer_Elapsed(object sender, ElapsedEventArgs e) - { - if (PersistIdleTimerInProgressOrTriggered == false || IsTerminalStateAction) - return; - - lock (ReactivateSync) - { - if (IsTerminalStateAction) - return; - - if (PersistIdleTimerInProgressOrTriggered) - { - SetInternalUnloaded(true); - - // Check for the race condition. - // There could be possibility workflow application is unloaded because of terminal state is reached. - if (this.workflowApplication != null) - { - try - { - this.OnUnloadComplete(this.workflowApplication.BeginUnload(null, null)); - } - catch (Exception exp) - { - Tracer.WriteMessage("PSWorkflowInstance", "PersistUnloadTimer_Elapsed", id, "Got an exception while unloading the workflow Application."); - Tracer.TraceException(exp); - return; - } - } - } - } - } - private void OnUnloadComplete(IAsyncResult result) - { - if (Disposed) return; - - try - { - this.workflowApplication.EndUnload(result); - } - catch (Exception e) - { - Tracer.WriteMessage("PSWorkflowInstance", "PersistUnloadTimer_Elapsed", id, "Not able to unload workflow application in a given timeout."); - Tracer.TraceException(e); - return; - } - - this.DisposeWorkflowApplication(); - } - - /// - /// CheckForTerminalAction - /// - internal override void CheckForTerminalAction() - { - lock (ReactivateSync) - { - IsTerminalStateAction = true; - PersistIdleTimerInProgressOrTriggered = false; - if (this.InternalUnloaded) - { - this.DoLoadInstanceForReactivation(); - SetInternalUnloaded(false); - } - } - } - - /// - /// Validate if the label exists. - /// - /// - internal override void ValidateIfLabelExists(string label) - { - string prefix = PSActivity.PSSuspendBookmarkPrefix + label; - - ReadOnlyCollection bookmarkInfos = this.workflowApplication.GetBookmarks(); - - bool found = false; - foreach (BookmarkInfo bookmarkInfo in bookmarkInfos) - { - if (bookmarkInfo.BookmarkName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - found = true; - } - } - - if (!found) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidLabel, label)); - } - } - - #endregion - - #region Static Methods - - /// - /// Helper method to translate internal Workflow metadata names to - /// public Workflow variable names. - /// - /// Metadata key string - /// Public workflow variable name - internal static string TranslateMetaDataName(string metaDataName) - { - string rtnName; - - switch (metaDataName) - { - case Constants.JobMetadataName: - rtnName = "JobName"; - break; - - case Constants.JobMetadataInstanceId: - rtnName = "JobInstanceId"; - break; - - case Constants.JobMetadataSessionId: - rtnName = "JobId"; - break; - - case Constants.JobMetadataCommand: - rtnName = "JobCommandName"; - break; - - case Constants.JobMetadataParentName: - rtnName = "ParentJobName"; - break; - - case Constants.JobMetadataParentInstanceId: - rtnName = "ParentJobInstanceId"; - break; - - case Constants.JobMetadataParentSessionId: - rtnName = "ParentJobId"; - break; - - case Constants.JobMetadataParentCommand: - rtnName = "ParentCommandName"; - break; - - case Constants.JobMetadataLocation: - rtnName = "PSComputerName"; - break; - - default: - rtnName = metaDataName; - break; - } - - return rtnName; - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJob2.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJob2.cs deleted file mode 100644 index 9ca89c5d6a8..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJob2.cs +++ /dev/null @@ -1,2205 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Linq; -using System.Activities; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Globalization; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Tracing; -using System.Management.Automation.PerformanceData; -using System.Threading; -using Dbg = System.Diagnostics.Debug; -using System.Activities.Statements; -using System.Collections.ObjectModel; -using System.Activities.Hosting; -using Microsoft.PowerShell.Activities; - -// Stops compiler from warning about unknown warnings -#pragma warning disable 1634, 1691 - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// Workflow Job type implementation. For use with the WorkflowJobSourceAdapter. - /// - public class PSWorkflowJob : Job2, IJobDebugger - { - #region Public Accessors - - /// - /// Delegate action on workflow idling - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is needed for taking the decision for evicting the workflows.")] - public Action> OnIdle { get; set; } - - /// - /// Delegate function on workflow persist idle action - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is needed for taking the decision for evicting the workflows.")] - public Func, bool, PSPersistableIdleAction> OnPersistableIdleAction { get; set; } - - /// - /// Delegate action on workflow unloaded - /// - public Action OnUnloaded { get; set; } - - /// - /// Signaled when job reaches running state - /// - internal WaitHandle Running { get { return this.JobRunning; } } - - /// - /// Signaled when job finishes suspending or aborting - /// - internal WaitHandle SuspendedOrAborted { get { return this.JobSuspendedOrAborted; } } - - /// - /// Job input parameters - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - internal Dictionary WorkflowParameters { get { return _workflowParameters; } } - - /// - /// Job input parameters including system provided parameters - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - internal Dictionary PSWorkflowCommonParameters { get { return _psWorkflowCommonParameters; } } - - /// - /// Job metadata collection - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - internal Dictionary JobMetadata { get { return _jobMetadata; } set { _jobMetadata = value; } } - - /// - /// Job invoker metadata collection - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - internal Dictionary PrivateMetadata { get { return _privateMetadata; } } - - /// - /// Workflow instance for the job - /// - public PSWorkflowInstance PSWorkflowInstance - { - get { return _workflowInstance; } - internal set { _workflowInstance = value; } - } - - /// - /// Workflow debugger - /// - public Debugger PSWorkflowDebugger - { - get - { - return (_workflowInstance != null) ? _workflowInstance.PSWorkflowDebugger : null; - } - } - - /// - /// Workflow job definition - /// - internal WorkflowJobDefinition PSWorkflowJobDefinition - { - get { return _definition; } - } - - internal Dictionary JobCreationContext - { - get { return _jobCreationContext; } - } - - #endregion - - #region Private Members - - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private const string ClassNameTrace = "PSWorkflowJob"; - private static readonly Tracer StructuredTracer = new Tracer(); - private readonly object _syncObject = new object(); - private readonly object _resumeErrorSyncObject = new object(); - private bool _isDisposed; - private string _statusMessage = string.Empty; - private string _location = string.Empty; - private readonly WorkflowJobDefinition _definition; - private readonly PSWorkflowRuntime _runtime; - private JobState _previousState; - private PSWorkflowInstance _workflowInstance; - private static readonly PSPerfCountersMgr _perfCountersMgr = PSPerfCountersMgr.Instance; - private readonly Dictionary _resumeErrors = new Dictionary(); - private readonly Dictionary _jobCreationContext; - - /// - /// Input parameters to the workflow instance. - /// - - private Dictionary _workflowParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary _psWorkflowCommonParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary _jobMetadata = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary _privateMetadata = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // Holds the collection of input objects received from the pipeline - private PSDataCollection _inputCollection; - - private ManualResetEvent _jobRunning; - private ManualResetEvent JobRunning - { - get - { - if (_jobRunning == null) - { - lock (_syncObject) - { - if (_jobRunning == null) - { - // this assert is required so that a wait handle - // is not created after the object is disposed - // which will result in a leak - AssertNotDisposed(); - _jobRunning = new ManualResetEvent(false); - } - } - } - - return _jobRunning; - } - } - - private ManualResetEvent _jobSuspendedOrAborted; - private ManualResetEvent JobSuspendedOrAborted - { - get - { - if (_jobSuspendedOrAborted == null) - { - lock (_syncObject) - { - if (_jobSuspendedOrAborted == null) - { - // this assert is required so that a wait handle - // is not created after the object is disposed - // which will result in a leak - AssertNotDisposed(); - _jobSuspendedOrAborted = new ManualResetEvent(false); - } - } - } - return _jobSuspendedOrAborted; - } - } - - private bool _isAsync = true; - - #endregion Private Members - - #region Private Methods - - /// - /// Provide validation of constructor parameter that could cause NullReferenceException. - /// - /// JobInvocationInfo for construction. - /// specification parameter if not null. - private static JobInvocationInfo Validate(JobInvocationInfo specification) - { - if (specification == null) - { - throw new ArgumentNullException("specification"); - } - - if (specification.Definition == null) - { - throw new ArgumentException(Resources.UninitializedSpecification, "specification"); - } - - if (string.IsNullOrEmpty(specification.Definition.Command)) - { - throw new ArgumentException(Resources.UninitializedSpecification, "specification"); - } - - return specification; - } - - private void InitializeWithWorkflow(PSWorkflowInstance instance, bool closeStreams = false) - { - Dbg.Assert(instance.Streams != null, "Workflow Instance has no stream data."); - _tracer.WriteMessage(ClassNameTrace, "InitializeWithWorkflow", WorkflowGuidForTraces, this, "Setting streams"); - Output = instance.Streams.OutputStream ?? new PSDataCollection(); - Progress = instance.Streams.ProgressStream ?? new PSDataCollection(); - Warning = instance.Streams.WarningStream ?? new PSDataCollection(); - Error = instance.Streams.ErrorStream ?? new PSDataCollection(); - Debug = instance.Streams.DebugStream ?? new PSDataCollection(); - Verbose = instance.Streams.VerboseStream ?? new PSDataCollection(); - Information = instance.Streams.InformationStream ?? new PSDataCollection(); - - if (!closeStreams) return; - - Output.Complete(); - Progress.Complete(); - Warning.Complete(); - Error.Complete(); - Debug.Complete(); - Verbose.Complete(); - Information.Complete(); - } - - private bool _starting; - private void DoStartJobLogic(object state) - { - if (_isDisposed) return; - Dbg.Assert(state == null, "State is never used"); - _tracer.WriteMessage(ClassNameTrace, "DoStartJobLogic", WorkflowGuidForTraces, this, "BEGIN"); - StructuredTracer.BeginJobLogic(InstanceId); - lock (SyncRoot) - { - AssertValidState(JobState.NotStarted); - if (_starting || _suspending || _resuming) return; - _starting = true; - } - - // Do Not set job state from within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Running); - - Dbg.Assert(_workflowInstance != null, "PSWorkflowInstance should have been populated by the adapter"); - Dbg.Assert(_workflowInstance.Id != Guid.Empty, "Workflow has not been loaded before StartJob"); - - _tracer.WriteMessage(ClassNameTrace, "DoStartJobLogic", WorkflowGuidForTraces, this, "ready to start"); - - _workflowInstance.ExecuteInstance(); - - // This message could appear after completion traces in some cases. - StructuredTracer.WorkflowExecutionStarted(_workflowInstance.Id, string.Empty); - _tracer.WriteMessage(ClassNameTrace, "DoStartJobLogic", WorkflowGuidForTraces, this, "END"); - } - - private bool _stopCalled; - private void DoStopJob() - { - if (_isDisposed || IsFinishedState(JobStateInfo.State) || JobState.Stopping == JobStateInfo.State || _stopCalled) return; - - bool cancel = false; - bool waitForRunning = false; - bool waitForSuspendorAbort = false; - - lock (SyncRoot) - { - if (_isDisposed || IsFinishedState(JobStateInfo.State) || JobState.Stopping == JobStateInfo.State || _stopCalled) return; - - if (_suspending) - waitForSuspendorAbort = true; - else if (_starting || _resuming) - waitForRunning = true; - - _tracer.WriteMessage(ClassNameTrace, "DoStopJob", WorkflowGuidForTraces, this, "BEGIN"); - - _stopCalled = true; - - if (JobStateInfo.State == JobState.Running) - { - cancel = true; - } - } - - // actual stop logic goes here - if (cancel) - { - if (waitForSuspendorAbort) - JobSuspendedOrAborted.WaitOne(); - else if (waitForRunning) - JobRunning.WaitOne(); - - // Do not set job state from within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Stopping); - - StructuredTracer.CancellingWorkflowExecution(_workflowInstance.Id); - _workflowInstance.StopInstance(); - } - else - { - // Do not set job state from within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Stopping); - - _workflowInstance.State = JobState.Stopped; - _workflowInstance.PerformTaskAtTerminalState(); - DoSetJobState(JobState.Stopped); - } - StructuredTracer.WorkflowExecutionCancelled(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - - _tracer.WriteMessage(ClassNameTrace, "DoStopJob", WorkflowGuidForTraces, this, "END"); - } - - private bool _suspending = false; - - /// - /// DoSuspendJob returns a bool, but - /// in the case of a SuspendJob call, we do not - /// want to return until the job is in the correct state. - /// The boolean return value is used by the WorkflowManager - /// for Shutdown purposes. - /// - /// - internal bool DoSuspendJob() - { - bool waitNeeded = true; - - if (_isDisposed) - { - return waitNeeded; - } - - bool waitForRunning = false; - bool notStarted = false; - - lock (SyncRoot) - { - if (_isDisposed || JobStateInfo.State == JobState.Suspending || JobStateInfo.State == JobState.Suspended || - _suspending) - { - return waitNeeded; - } - - if (this.IsSuspendable != null && IsSuspendable.HasValue && IsSuspendable.Value == false) - { - _tracer.WriteMessage(ClassNameTrace, "DoSuspendJob", WorkflowGuidForTraces, this, - "The job is not suspendable."); - throw new InvalidOperationException(Resources.ErrorMessageForPersistence); - } - - if (_starting) - waitForRunning = true; - - _tracer.WriteMessage(ClassNameTrace, "DoSuspendJob", WorkflowGuidForTraces, this, "BEGIN"); - _suspending = true; - - if (JobStateInfo.State != JobState.Running && JobStateInfo.State != JobState.NotStarted) - { - _tracer.WriteMessage(ClassNameTrace, "DoSuspendJob", WorkflowGuidForTraces, this, "InvalidJobState"); - throw new InvalidJobStateException(JobStateInfo.State, Resources.SuspendNotValidState); - } - if (JobStateInfo.State == JobState.NotStarted) - { - notStarted = true; - } - } - - if (waitForRunning) - JobRunning.WaitOne(); - - // Do not set job state from within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Suspending); - - lock (SyncRoot) - { - _resuming = false; - } - - _workflowInstance.SuspendInstance(notStarted); - - _tracer.WriteMessage(ClassNameTrace, "DoSuspendJob", WorkflowGuidForTraces, this, "END"); - return waitNeeded; - } - - /// - /// Do Set Job State - /// - /// - /// - /// returns false if state transition is not possible. Return value is required to update SQM perf counters - private bool DoSetJobState(JobState state, Exception reason = null) - { - if (IsFinishedState(_previousState) || _isDisposed) return false; - - lock (_syncObject) - { - if (IsFinishedState(_previousState) || _isDisposed) return false; - - // State should not be transitioned to suspended from stopping - if (_previousState == JobState.Stopping && state == JobState.Suspended) return false; - - // State should not be transitioned to suspended from suspended - if (_previousState == JobState.Suspended && state == JobState.Suspended) return false; - - if (state != _previousState && StructuredTracer.IsEnabled) - { - StructuredTracer.JobStateChanged(Id, InstanceId, state.ToString(), _previousState.ToString()); - } - -#if DEBUG - switch (_previousState) - { - case JobState.Running: - Dbg.Assert(state != JobState.NotStarted && state != JobState.Blocked, - "WorkflowJob invalid state transition from Running."); - break; - case JobState.Stopping: - //Dbg.Assert( - // state == JobState.Stopped || state == JobState.Failed || state == JobState.Completed, - // "WorkflowJob invalid state transition from Stopping."); - break; - case JobState.Stopped: - Dbg.Assert(false, "WorkflowJob should never transition after Stopped state."); - break; - case JobState.Suspending: - Dbg.Assert( - state == JobState.Suspended || state == JobState.Completed || state == JobState.Failed || - state == JobState.Stopped || state == JobState.Stopping, - "WorkflowJob invalid state transition from Suspending."); - break; - case JobState.Suspended: - Dbg.Assert( - state == JobState.Running || state == JobState.Stopping || state == JobState.Stopped || - state == JobState.Completed || state == JobState.Failed, - "WorkflowJob invalid state transition from Suspended."); - break; - case JobState.Failed: - Dbg.Assert(false, "WorkflowJob should never transition after Failed state."); - break; - case JobState.Completed: - Dbg.Assert(false, "WorkflowJob should never transition after Completed state."); - break; - case JobState.Disconnected: - Dbg.Assert(false, "WorkflowJob should never be in a disconnected state"); - break; - case JobState.Blocked: - Dbg.Assert(false, "WorkflowJob should never be in a blocked state"); - break; - default: - break; - } -#endif - - _previousState = state; - // Update JobMetadata - if (_workflowInstance != null) - { - _workflowInstance.PSWorkflowContext.JobMetadata.Remove(Constants.JobMetadataStateReason); - if (reason != null) - { - if (StructuredTracer.IsEnabled) - { - StructuredTracer.JobError(Id, InstanceId, Tracer.GetExceptionString(reason)); - } - _workflowInstance.PSWorkflowContext.JobMetadata.Add(Constants.JobMetadataStateReason, reason); - } - } - } - - _tracer.WriteMessage(ClassNameTrace, "DoSetJobState", WorkflowGuidForTraces, this, "Setting state to {0}, Setting Reason to exception: {1}", state.ToString(), reason == null ? null : reason.ToString()); - _workflowInstance.State = state; - - SetJobState(state, reason); - _tracer.WriteMessage(ClassNameTrace, "DoSetJobState", WorkflowGuidForTraces, this, "Done setting state"); - return true; - } - - /// - /// Create necessary dictionaries for WorkflowManager consumption based on StartParameters. - /// - private void SortStartParameters(DynamicActivity dynamicActivity, CommandParameterCollection parameters) - { - bool selfRemoting = dynamicActivity != null && dynamicActivity.Properties.Any(x => x.Name.Equals(Constants.ComputerName, StringComparison.CurrentCultureIgnoreCase)); - bool takesPSPrivateMetadata = dynamicActivity != null && dynamicActivity.Properties.Contains(Constants.PrivateMetadata); - _jobMetadata.Add(Constants.WorkflowTakesPrivateMetadata, takesPSPrivateMetadata); - - if (parameters != null) - { - foreach (var parameter in parameters) - { - _tracer.WriteMessage(ClassNameTrace, "SortStartParameters", WorkflowGuidForTraces, this, "Found parameter; {0}; {1}", parameter.Name, - parameter.Value == null ? null : parameter.Value.ToString()); - switch (parameter.Name) - { - case Constants.ComputerName: - if (selfRemoting) - { - // If we're self-remoting, location becomes the default computer - // and the PSComputerNames is passed in as an argument instead - // of going to the ubiquitous parameters - _location = Constants.DefaultComputerName; - string parameterName = dynamicActivity.Properties.First(x => x.Name.Equals(Constants.ComputerName, StringComparison.CurrentCultureIgnoreCase)).Name; - _workflowParameters[parameterName] = LanguagePrimitives.ConvertTo(parameter.Value); - } - else - { - // Set _location before adding parameter. - var computer = parameter.Value as string; - _location = computer; - string[] computerNames = LanguagePrimitives.ConvertTo(parameter.Value); - _psWorkflowCommonParameters[parameter.Name] = computerNames; - } - break; - case Constants.PrivateMetadata: - Hashtable privateData = parameter.Value as Hashtable; - if (privateData != null) - { - IDictionaryEnumerator enumerator = privateData.GetEnumerator(); - while (enumerator.MoveNext()) - { - _privateMetadata.Add(enumerator.Key.ToString(), enumerator.Value); - } - - // Make the combined object available within the workflow as well... - if (takesPSPrivateMetadata) - { - _workflowParameters.Add(parameter.Name, parameter.Value); - } - } - break; - - case Constants.PSInputCollection: - { - // Remove the input collection so we can properly pass it to the workflow job - object baseObject = parameter.Value is PSObject - ? ((PSObject)parameter.Value).BaseObject - : parameter.Value; - - if (baseObject is PSDataCollection) - { - _inputCollection = baseObject as PSDataCollection; - } - else - { - var inputCollection = new PSDataCollection(); - var e = LanguagePrimitives.GetEnumerator(baseObject); - if (e != null) - { - while (e.MoveNext()) - { - inputCollection.Add(PSObject.AsPSObject(e.Current)); - } - } - else - { - inputCollection.Add(PSObject.AsPSObject(parameter.Value)); - } - _inputCollection = inputCollection; - } - } - break; - - case Constants.PSParameterCollection: - // Remove this one from the parameter collection... - break; - case Constants.PSRunningTime: - case Constants.PSElapsedTime: - case Constants.ConnectionRetryCount: - case Constants.ActionRetryCount: - case Constants.ConnectionRetryIntervalSec: - case Constants.ActionRetryIntervalSec: - _psWorkflowCommonParameters.Add(parameter.Name, parameter.Value); - break; - - case Constants.Persist: - case Constants.Credential: - case Constants.Port: - case Constants.UseSSL: - case Constants.ConfigurationName: - case Constants.ApplicationName: - case Constants.ConnectionURI: - case Constants.SessionOption: - case Constants.Authentication: - case Constants.AuthenticationLevel: - case Constants.CertificateThumbprint: - case Constants.AllowRedirection: - case Constants.Verbose: - case Constants.Debug: - case Constants.ErrorAction: - case Constants.WarningAction: - case Constants.InformationAction: - case Constants.PSWorkflowErrorAction: - case Constants.PSSuspendOnError: - case Constants.PSSenderInfo: - case Constants.ModulePath: - case Constants.PSCurrentDirectory: - // Note: We don't add ErrorVariable, WarningVariable, OutVariable, or OutBuffer - // here because they are interpreted by PowerShell in the function generated over - // the workflow definition. - _psWorkflowCommonParameters.Add(parameter.Name, parameter.Value); - break; - default: - _workflowParameters.Add(parameter.Name, parameter.Value); - break; - } - } - } - - // Add in the workflow command name... - _psWorkflowCommonParameters.Add("WorkflowCommandName", _definition.Command); - } - - - private void HandleMyStateChanged(object sender, JobStateEventArgs e) - { - _tracer.WriteMessage(ClassNameTrace, "HandleMyStateChanged", WorkflowGuidForTraces, this, - "NewState: {0}; OldState: {1}", e.JobStateInfo.State.ToString(), - e.PreviousJobStateInfo.State.ToString()); - bool unloadStreams = false; - if (e.PreviousJobStateInfo.State == JobState.NotStarted) - { - PSBeginTime = DateTime.Now; - } - - switch (e.JobStateInfo.State) - { - case JobState.Running: - { - lock (SyncRoot) - { - _suspending = false; - _resuming = false; - wfSuspendInProgress = false; - } - - lock (_syncObject) - { - JobRunning.Set(); - - // Do not create the event if it doesn't already exist. Suspend may never be called. - if (_jobSuspendedOrAborted != null) - JobSuspendedOrAborted.Reset(); - - // Clear the message indicating that the job was suspended or that the job was queued - // for resume. The job is now running, and that status is no longer valid. - _statusMessage = String.Empty; - } - } - break; - - case JobState.Suspended: - { - lock (SyncRoot) - { - _suspending = false; - _resuming = false; - wfSuspendInProgress = false; - } - - PSEndTime = DateTime.Now; - lock (_syncObject) - { - JobSuspendedOrAborted.Set(); - JobRunning.Reset(); - } - unloadStreams = true; - } - break; - case JobState.Failed: - case JobState.Completed: - case JobState.Stopped: - { - PSEndTime = DateTime.Now; - - lock (_syncObject) - { - StructuredTracer.EndJobLogic(InstanceId); - JobSuspendedOrAborted.Set(); - - // Do not reset JobRunning when the state is terminal. - // No thread should wait on a job transitioning again to - // JobState.Running. - JobRunning.Set(); - } - unloadStreams = true; - } - break; - } - - if (!unloadStreams || !_unloadStreamsOnPersistentState) return; - - _tracer.WriteMessage(ClassNameTrace, "HandleMyStateChanged", WorkflowGuidForTraces, this, - "BEGIN Unloading streams from memory"); - SelfUnloadJobStreams(); - _tracer.WriteMessage(ClassNameTrace, "HandleMyStateChanged", WorkflowGuidForTraces, this, - "END Unloading streams from memory"); - } - - private void DoStartJobAsync(object state) - { - _tracer.WriteMessage(ClassNameTrace, "DoStartJobAsync", WorkflowGuidForTraces, this, ""); - Dbg.Assert(state == null, "State is never used"); - var asyncOp = AsyncOperationManager.CreateOperation(null); - - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - workerDelegate.BeginInvoke( - asyncOp, - ActionType.Start, - string.Empty, - string.Empty, - false, - null, - null); - } - - private void AssertValidState(JobState expectedState) - { - AssertNotDisposed(); - lock (SyncRoot) - { - if (JobStateInfo.State != expectedState) - { - throw new InvalidJobStateException(JobStateInfo.State, Resources.JobCannotBeStarted); - } - } - } - - private bool _resuming; - private void DoResumeJob(object state) - { - if (_isDisposed) return; - var tuple = state as Tuple; - string label = tuple != null ? tuple.Item1 ?? String.Empty : String.Empty; - - if (string.IsNullOrEmpty(label) == false) - { - DoLabeledResumeJob(label); - return; - } - - lock (SyncRoot) - { - if (_isDisposed || JobStateInfo.State == JobState.Running || _resuming) return; - _tracer.WriteMessage(ClassNameTrace, "DoResumeJob", WorkflowGuidForTraces, this, "BEGIN"); - - if (JobStateInfo.State != JobState.Suspended) - { - _tracer.WriteMessage(ClassNameTrace, "DoResumeJob", WorkflowGuidForTraces, this, - "InvalidJobState"); - throw new InvalidJobStateException(JobStateInfo.State, Resources.ResumeNotValidState); - } - - // this will avoid the race condition between two resume requests and both are trying to load instance and loadstreams. - _resuming = true; - } - - _workflowInstance.DoLoadInstanceForReactivation(); - - // load the streams before resuming - LoadJobStreams(); - - // Do not set state within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Running); - - // actual logic of resuming a job - StructuredTracer.WorkflowResuming(_workflowInstance.Id); - _workflowInstance.ResumeInstance(label); - - lock (SyncRoot) - { - // once the resume instance is called then we can allow the subsequent suspend requests - _suspending = false; - wfSuspendInProgress = false; - - } - - StructuredTracer.WorkflowResumed(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "DoResumeJob", WorkflowGuidForTraces, this, "END"); - } - - private void DoLabeledResumeJob(string label) - { - bool waitNeeded = false; - - lock (SyncRoot) - { - if (_isDisposed) return; - _tracer.WriteMessage(ClassNameTrace, "DoLabeledResumeJob", WorkflowGuidForTraces, this, "BEGIN"); - - if (wfSuspendInProgress) - { - waitNeeded = true; - } - - listOfLabels.Add(label); - } - - try - { - if (waitNeeded) - JobSuspendedOrAborted.WaitOne(); - - lock (SyncRoot) - { - wfSuspendInProgress = false; - - if (JobStateInfo.State != JobState.Suspended && JobStateInfo.State != JobState.Running) - { - _tracer.WriteMessage(ClassNameTrace, "DoLabeledResumeJob", WorkflowGuidForTraces, this, "InvalidJobState"); - throw new InvalidJobStateException(JobStateInfo.State, Resources.ResumeNotValidState); - } - - if (JobStateInfo.State != JobState.Running && !_resuming) - _workflowInstance.DoLoadInstanceForReactivation(); - - // this needs to be done here because this call may throw - // invalid bookmark exception, which should be happened before - // setting the job state to running. - _workflowInstance.ValidateIfLabelExists(label); - - _resuming = true; - } - - // load the streams before resuming - LoadJobStreams(); - - // Do not set state within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Running); - - // actual logic of resuming a job - StructuredTracer.WorkflowResuming(_workflowInstance.Id); - _workflowInstance.ResumeInstance(label); - - lock (SyncRoot) - { - // once the resume instance is called then we can allow the subsequent suspend requests - _suspending = false; - } - } - finally - { - lock (SyncRoot) - { - listOfLabels.Remove(label); - } - } - - StructuredTracer.WorkflowResumed(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "DoLabeledResumeJob", WorkflowGuidForTraces, this, "END"); - } - - private void DoResumeJobCatchException(object state) - { - var tuple = state as Tuple; - Dbg.Assert(tuple != null, "ResumeJob with exception handling should have tuple."); - try - { - DoResumeJob(tuple.Item1); - } - catch (Exception e) - { - lock (_resumeErrorSyncObject) - { - if (!_resumeErrors.ContainsKey(tuple.Item2)) - _resumeErrors.Add(tuple.Item2, e); - } - } - finally - { - // Blue: 79098 - // There was a race condition between ResumeJob(label) and DoResumeJobCatchException() in setting and accessing the resume errors. - // ManualResetEvent should be set here after adding resume errors if any. - var eventTuple = tuple.Item1 as Tuple; - if (eventTuple != null && eventTuple.Item2 != null) - { - eventTuple.Item2.Set(); - } - } - } - - /// - /// DoResumeBookmark - /// - /// The Bookmark which needs to be resumed. - /// The state, which will be passed to the activity, which gets resumed. - protected virtual void DoResumeBookmark(Bookmark bookmark, object state) - { - if (_isDisposed) return; - - lock (SyncRoot) - { - if (_isDisposed) return; - - if (this.IsFinishedState(JobStateInfo.State) || JobStateInfo.State == JobState.Stopping) - { - _tracer.WriteMessage(ClassNameTrace, "DoResumeBookmark", WorkflowGuidForTraces, this, "InvalidJobState to resume a bookmark"); - throw new InvalidJobStateException(JobStateInfo.State, Resources.ResumeNotValidState); - } - } - - // Do Not set job state from within the lock. - // This can cause a deadlock because events are raised. - // - DoSetJobState(JobState.Running); - - _workflowInstance.ResumeBookmark(bookmark, state); - } - - private bool DoTerminateJob(string reason, bool suppressError = false) - { - if (_isDisposed) return false; - bool terminated = false; - lock (SyncRoot) - { - if (_isDisposed) return false; - _tracer.WriteMessage(ClassNameTrace, "DoTerminateJob", WorkflowGuidForTraces, this, "BEGIN"); - if (JobStateInfo.State == JobState.Running || JobStateInfo.State == JobState.NotStarted) - { - _tracer.WriteMessage("trying to terminate running workflow job"); - _workflowInstance.CheckForTerminalAction(); - _workflowInstance.TerminateInstance(reason, suppressError); - terminated = true; - } - else if (JobStateInfo.State == JobState.Suspended) - { - _tracer.WriteMessage("Trying to load and terminate suspended workflow"); - _workflowInstance.DoLoadInstanceForReactivation(); - _workflowInstance.TerminateInstance(reason, suppressError); - terminated = true; - } - } - - _tracer.WriteMessage(ClassNameTrace, "DoTerminateJob", WorkflowGuidForTraces, this, "END"); - return terminated; - } - - internal bool DoAbortJob(string reason) - { - if (_isDisposed) return false; - - bool waitForRunning = false; - - lock (SyncRoot) - { - if (_isDisposed) return false; - - if (_isDisposed || JobStateInfo.State == JobState.Suspending || JobStateInfo.State == JobState.Suspended || _suspending) - return false; - - if (_starting) - waitForRunning = true; - - _tracer.WriteMessage(ClassNameTrace, "DoAbortJob", WorkflowGuidForTraces, this, "BEGIN"); - _suspending = true; - - if (JobStateInfo.State != JobState.Running && JobStateInfo.State != JobState.NotStarted) - { - _tracer.WriteMessage(ClassNameTrace, "DoAbortJob", WorkflowGuidForTraces, this, "InvalidJobState"); - throw new InvalidJobStateException(JobStateInfo.State, Resources.SuspendNotValidState); - } - } - - // Do not set job state from within lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Suspending); - - lock (SyncRoot) - { - _resuming = false; - } - - if (waitForRunning) - JobRunning.WaitOne(); - - _workflowInstance.CheckForTerminalAction(); - _workflowInstance.AbortInstance(reason); - - _tracer.WriteMessage(ClassNameTrace, "DoAbortJob", WorkflowGuidForTraces, this, "END"); - - return true; - } - - private void OnWorkflowCompleted(object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowCompleted", WorkflowGuidForTraces, this, "BEGIN"); - DoSetJobState(JobState.Completed); - StructuredTracer.WorkflowExecutionFinished(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.SucceededWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.SucceededWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowCompleted", WorkflowGuidForTraces, this, "END"); - } - - private void OnWorkflowAborted(Exception e, object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowAborted", WorkflowGuidForTraces, this, "BEGIN"); - - DoSetJobState(JobState.Suspended, e); - - StructuredTracer.WorkflowExecutionAborted(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.FailedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.FailedWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowAborted", WorkflowGuidForTraces, this, "END"); - } - - private void OnWorkflowFaulted(Exception e, object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowFaulted", WorkflowGuidForTraces, this, "BEGIN"); - StructuredTracer.WorkflowExecutionError(_workflowInstance.Id, Tracer.GetExceptionString(e) + Environment.NewLine + e); - DoSetJobState(JobState.Failed, e); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.FailedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.FailedWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowFaulted", WorkflowGuidForTraces, this, "END"); - } - - private void OnWorkflowStopped(object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowStopped", WorkflowGuidForTraces, this, "BEGIN"); - DoSetJobState(JobState.Stopped); - StructuredTracer.WorkflowExecutionCancelled(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowStopped", WorkflowGuidForTraces, this, "END"); - } - - private void OnWorkflowSuspended(object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowSuspended", WorkflowGuidForTraces, this, "BEGIN"); - - // CheckStopping() was not thread safe. - // Now DoSetJobState handles the invalid state transition from stopping to suspended - if (DoSetJobState(JobState.Suspended)) - { - StructuredTracer.WorkflowUnloaded(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.SuspendedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.SuspendedWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - } - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowSuspended", WorkflowGuidForTraces, this, "END"); - } - - private void OnWorkflowIdle(ReadOnlyCollection bookmarks, object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowIdle", WorkflowGuidForTraces, this, "BEGIN"); - - if (this.OnIdle != null) OnIdle(this, bookmarks); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowIdle", WorkflowGuidForTraces, this, "END"); - } - - private PSPersistableIdleAction OnWorkflowPersistableIdleAction(ReadOnlyCollection bookmarks, bool externalSuspendRequest, object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowPersistIdleAction", WorkflowGuidForTraces, this, "BEGIN"); - - PSPersistableIdleAction rc = PSPersistableIdleAction.NotDefined; - - if (this.OnPersistableIdleAction != null) - rc = this.OnPersistableIdleAction(this, bookmarks, externalSuspendRequest); - - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowPersistIdleAction", WorkflowGuidForTraces, this, "END"); - - return rc; - } - - private void OnWorkflowUnloaded(object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowUnloaded", WorkflowGuidForTraces, this, "BEGIN"); - if (this.OnUnloaded != null) OnUnloaded(this); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowUnloaded", WorkflowGuidForTraces, this, "END"); - } - - /// - /// Unloads the streams of the job. - /// - /// - /// To be called from this class only - private void SelfUnloadJobStreams() - { - if (_hasMoreDataOnDisk) return; - lock (SyncRoot) - { - if (_hasMoreDataOnDisk) return; - - Dbg.Assert(JobStateInfo.State == JobState.Stopped || - JobStateInfo.State == JobState.Completed || - JobStateInfo.State == JobState.Failed || - JobStateInfo.State == JobState.Suspended, "Job state is incorrect when unload is called"); - UnloadJobStreams(); - } - } - - #endregion Private Methods - - #region Internal Accessors - - internal Guid WorkflowGuid { get { return _workflowInstance.Id; } } - private Guid WorkflowGuidForTraces { get { if (_workflowInstance != null) return _workflowInstance.Id; return Guid.Empty; } } - - internal bool WorkflowInstanceLoaded { get; set; } - - internal bool SynchronousExecution { get; set; } - - internal bool? IsSuspendable = null; - - internal PSLanguageMode? SourceLanguageMode { get; set; } - - #endregion Internal Accessors - - #region Internal Methods - - private bool _unloadStreamsOnPersistentState; - internal void EnableStreamUnloadOnPersistentState() - { - _unloadStreamsOnPersistentState = true; - } - - /// - /// Helper function to check if job is finished - /// - /// - /// - internal bool IsFinishedState(JobState state) - { - return (state == JobState.Completed || state == JobState.Failed || state == JobState.Stopped); - } - - internal void LoadWorkflow(CommandParameterCollection commandParameterCollection, Activity activity, string xaml) - { - _tracer.WriteMessage(ClassNameTrace, "LoadWorkflow", WorkflowGuidForTraces, this, "BEGIN"); - Dbg.Assert(_workflowInstance == null, "LoadWorkflow() should only be called once by the adapter"); - - // If activity hasn't been cached, we can't generate it from _definition. - if (activity == null) - { - bool windowsWorkflow; - activity = DefinitionCache.Instance.GetActivityFromCache(_definition, out windowsWorkflow); - - if (activity == null) - { - // The workflow cannot be run. - throw new InvalidOperationException(Resources.ActivityNotCached); - } - } - - string workflowXaml; - string runtimeAssemblyPath; - - if (string.IsNullOrEmpty(xaml)) - { - workflowXaml = DefinitionCache.Instance.GetWorkflowXaml(_definition); - runtimeAssemblyPath = DefinitionCache.Instance.GetRuntimeAssemblyPath(_definition); - } - else - { - workflowXaml = xaml; - runtimeAssemblyPath = null; - } - - _location = null; - SortStartParameters(activity as DynamicActivity, commandParameterCollection); - - // Set location if ComputerName wasn't specified in the parameters. - if (string.IsNullOrEmpty(_location)) _location = Constants.DefaultComputerName; - if (_jobMetadata.ContainsKey(Constants.JobMetadataLocation)) - _jobMetadata.Remove(Constants.JobMetadataLocation); - _jobMetadata.Add(Constants.JobMetadataLocation, _location); - - if (_jobMetadata.ContainsKey(Constants.WorkflowJobCreationContext)) - _jobMetadata.Remove(Constants.WorkflowJobCreationContext); - _jobMetadata.Add(Constants.WorkflowJobCreationContext, _jobCreationContext); - - PSWorkflowDefinition definition = new PSWorkflowDefinition(activity, workflowXaml, runtimeAssemblyPath, _definition.RequiredAssemblies); - PSWorkflowContext metadatas = new PSWorkflowContext(_workflowParameters, _psWorkflowCommonParameters, _jobMetadata, _privateMetadata); - - _workflowInstance = _runtime.Configuration.CreatePSWorkflowInstance(definition, metadatas, _inputCollection, this); - this.ConfigureWorkflowHandlers(); - - // Create a WorkflowApplication instance. - _tracer.WriteMessage(ClassNameTrace, "LoadWorkflow", WorkflowGuidForTraces, this, "Calling instance loader"); - - #if DEBUG - try - { - _workflowInstance.CreateInstance(); - } - catch (Exception e) - { - if (e.Message.IndexOf("Cannot create unknown type", StringComparison.OrdinalIgnoreCase) >= 0) - { - // Capture environment to help diagnose: MSFT:246456 - PSObject inputObject = new PSObject(); - inputObject.Properties.Add( - new PSNoteProperty("activity", activity)); - inputObject.Properties.Add( - new PSNoteProperty("workflowXaml", workflowXaml)); - inputObject.Properties.Add( - new PSNoteProperty("runtimeAssemblyPath", runtimeAssemblyPath)); - inputObject.Properties.Add( - new PSNoteProperty("_definition.RequiredAssemblies", _definition.RequiredAssemblies)); - - string tempPath = System.IO.Path.GetTempFileName(); - System.Management.Automation.PowerShell.Create().AddCommand("Export-CliXml"). - AddParameter("InputObject", inputObject). - AddParameter("Depth", 10). - AddParameter("Path", tempPath).Invoke(); - - throw new Exception("Bug MSFT:246456 detected. Please capture " + tempPath + ", open a new issue " + - "at https://github.com/PowerShell/PowerShell/issues/new and attach the file."); - } - else - { - throw; - } - } - #else - _workflowInstance.CreateInstance(); - #endif - - InitializeWithWorkflow(_workflowInstance); - WorkflowInstanceLoaded = true; - _tracer.WriteMessage(ClassNameTrace, "LoadWorkflow", WorkflowGuidForTraces, this, "END"); - } - - internal void ConfigureWorkflowHandlers() - { - _workflowInstance.OnCompletedDelegate = OnWorkflowCompleted; - _workflowInstance.OnSuspendedDelegate = OnWorkflowSuspended; - _workflowInstance.OnStoppedDelegate = OnWorkflowStopped; - _workflowInstance.OnAbortedDelegate = OnWorkflowAborted; - _workflowInstance.OnFaultedDelegate = OnWorkflowFaulted; - _workflowInstance.OnIdleDelegate = OnWorkflowIdle; - _workflowInstance.OnPersistableIdleActionDelegate = OnWorkflowPersistableIdleAction; - _workflowInstance.OnUnloadedDelegate = OnWorkflowUnloaded; - } - - internal void RestoreFromWorkflowInstance(PSWorkflowInstance instance) - { - _tracer.WriteMessage(ClassNameTrace, "RestoreFromWorkflowInstance", WorkflowGuidForTraces, this, "BEGIN"); - Dbg.Assert(instance != null, "cannot restore a workflow job with null workflow instance"); - - object data; - Exception reason = null; - - if (instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataStateReason, out data)) - reason = data as Exception; - else - reason = instance.Error; - - // igorse: restore all of the job metadata - _workflowParameters = instance.PSWorkflowContext.WorkflowParameters; - _psWorkflowCommonParameters = instance.PSWorkflowContext.PSWorkflowCommonParameters; - _jobMetadata = instance.PSWorkflowContext.JobMetadata; - _privateMetadata = instance.PSWorkflowContext.PrivateMetadata; - - if (instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataLocation, out data)) _location = data as string; - if (instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataStatusMessage, out data)) _statusMessage = data as string; - if (instance.State == JobState.Suspended) - { - // Status message cannot be set publicly. - _statusMessage = Resources.SuspendedJobRecoveredFromPreviousSession; - } - - // indicate that this job has results on disk. This will ensure - // that the results are not loaded when doing a get-job - lock (_syncObject) - { - _hasMoreDataOnDisk = true; - } - - Dbg.Assert(instance.JobStateRetrieved, - "Cannot set job state when job state is not retrieved or when there is an error in retrieval"); - // igorse: set job state when job is fully restored as StateChanged event will fire - DoSetJobState(instance.State, reason); - _tracer.WriteMessage(ClassNameTrace, "RestoreFromWorkflowInstance", WorkflowGuidForTraces, this, "END"); - } - - #endregion Internal Methods - - #region Async helpers - - private enum ActionType - { - Start = 0, - Stop = 1, - Suspend = 2, - Resume = 3, - Abort = 4, - Terminate = 5 - } - - private class AsyncCompleteContainer - { - internal AsyncCompletedEventArgs EventArgs; - internal ActionType Action; - } - - private delegate void JobActionWorkerDelegate(AsyncOperation asyncOp, ActionType action, string reason, string label, bool suppressError); - private void JobActionWorker(AsyncOperation asyncOp, ActionType action, string reason, string label, bool suppressError) - { - Exception exception = null; - -#pragma warning disable 56500 - try - { - switch (action) - { - case ActionType.Start: - DoStartJobLogic(null); - break; - - case ActionType.Stop: - DoStopJob(); - break; - - case ActionType.Suspend: - DoSuspendJob(); - break; - - case ActionType.Resume: - DoResumeJob(label); - break; - - case ActionType.Abort: - DoAbortJob(reason); - break; - - case ActionType.Terminate: - DoTerminateJob(reason, suppressError); - break; - } - } - catch (Exception e) - { - // Called on a background thread, need to include any exception in - // event arguments. - exception = e; - } -#pragma warning restore 56500 - - var eventArgs = new AsyncCompletedEventArgs(exception, false, asyncOp.UserSuppliedState); - - var container = new AsyncCompleteContainer { EventArgs = eventArgs, Action = action }; - - // End the task. The asyncOp object is responsible - // for marshaling the call. - asyncOp.PostOperationCompleted(JobActionAsyncCompleted, container); - } - - private void JobActionAsyncCompleted(object operationState) - { - if (_isDisposed) return; - var container = operationState as AsyncCompleteContainer; - Dbg.Assert(container != null, "AsyncCompleteContainer cannot be null"); - _tracer.WriteMessage(ClassNameTrace, "JobActionAsyncCompleted", WorkflowGuidForTraces, this, "operation: {0}", container.Action.ToString()); - try - { - switch (container.Action) - { - case ActionType.Start: - if (container.EventArgs.Error == null) JobRunning.WaitOne(); - OnStartJobCompleted(container.EventArgs); - break; - case ActionType.Stop: - case ActionType.Terminate: - if (container.EventArgs.Error == null) Finished.WaitOne(); - OnStopJobCompleted(container.EventArgs); - break; - case ActionType.Suspend: - if (container.EventArgs.Error == null) JobSuspendedOrAborted.WaitOne(); - OnSuspendJobCompleted(container.EventArgs); - break; - case ActionType.Abort: - if (container.EventArgs.Error == null) JobSuspendedOrAborted.WaitOne(); - OnSuspendJobCompleted(container.EventArgs); - break; - case ActionType.Resume: - if (container.EventArgs.Error == null) JobRunning.WaitOne(); - OnResumeJobCompleted(container.EventArgs); - break; - } - } - catch (ObjectDisposedException) - { - // An object disposed exception can be thrown by any of the above WaitOne() statements. - // To otherwise prevent this, locking could be done. That would perf implications for every - // iteration of this code, and would open a possibility for deadlocks through multiple lock - // objects. - } - } - - #endregion Async helpers - - #region Constructors - - /// - /// Construct a PSWorkflowJob. - /// - /// - /// JobInvocationInfo representing the command this job - /// will invoke. - internal PSWorkflowJob(PSWorkflowRuntime runtime, JobInvocationInfo specification) - : base(Validate(specification).Command) - { - Dbg.Assert(runtime != null, "runtime must not be null."); - // If specification is null, ArgumentNullException would be raised from - // the static validate method. - StartParameters = specification.Parameters; - _definition = WorkflowJobDefinition.AsWorkflowJobDefinition(specification.Definition); - - _runtime = runtime; - CommonInit(); - } - - /// - /// Construct a PSWorkflowJob. - /// - /// - /// JobInvocationInfo representing the command this job will invoke. - /// - /// - internal PSWorkflowJob(PSWorkflowRuntime runtime, JobInvocationInfo specification, Guid JobInstanceId, Dictionary creationContext) - : base(Validate(specification).Command, specification.Definition.Name, JobInstanceId) - { - Dbg.Assert(runtime != null, "runtime must not be null."); - // If specification is null, ArgumentNullException would be raised from - // the static validate method. - StartParameters = specification.Parameters; - _definition = WorkflowJobDefinition.AsWorkflowJobDefinition(specification.Definition); - _jobCreationContext = creationContext; - - _runtime = runtime; - CommonInit(); - } - - /// - /// - /// - /// - /// - /// - internal PSWorkflowJob(PSWorkflowRuntime runtime, string command, string name) - : base(command, name) - { - Dbg.Assert(runtime != null, "runtime must not be null."); - - _runtime = runtime; - CommonInit(); - } - - /// - /// - /// - /// - /// - /// - /// - internal PSWorkflowJob(PSWorkflowRuntime runtime, string command, string name, JobIdentifier token) - : base(command, name, token) - { - Dbg.Assert(runtime != null, "runtime must not be null."); - - _runtime = runtime; - CommonInit(); - } - - /// - /// - /// - /// - /// - /// - /// - internal PSWorkflowJob(PSWorkflowRuntime runtime, string command, string name, Guid instanceId) - : base(command, name, instanceId) - { - Dbg.Assert(runtime != null, "runtime must not be null."); - - _runtime = runtime; - CommonInit(); - } - - private void CommonInit() - { - PSJobTypeName = WorkflowJobSourceAdapter.AdapterTypeName; - StateChanged += HandleMyStateChanged; - _tracer.WriteMessage(ClassNameTrace, "CommonInit", WorkflowGuidForTraces, this, "Construction/initialization"); - - } - - #endregion Constructors - - #region Overrides of Job - - /// - /// - /// - public override void StopJob() - { - AssertNotDisposed(); - DoStopJob(); - Finished.WaitOne(); - } - - /// - /// Success status of the command execution. - /// - public override string StatusMessage - { - get { return _statusMessage; } - } - - /// - /// Indicates that more data is available in this result object for reading. - /// - public override bool HasMoreData - { - get - { - return (_hasMoreDataOnDisk - || Output.IsOpen || Output.Count > 0 - || Error.IsOpen || Error.Count > 0 - || Verbose.IsOpen || Verbose.Count > 0 - || Debug.IsOpen || Debug.Count > 0 - || Warning.IsOpen || Warning.Count > 0 - || Progress.IsOpen || Progress.Count > 0 - || Information.IsOpen || Information.Count > 0 - ); - } - } - - private bool _hasMoreDataOnDisk; - - /// - /// Indicates a location where this job is running. - /// - public override string Location - { - get { return _location; } - } - - #endregion - - #region Overrides of Job2 - - /// - /// Implementation of this method will allow the delayed loading of streams. - /// - protected override void DoLoadJobStreams() - { - lock (_syncObject) - { - bool closeStreams = IsFinishedState(JobStateInfo.State); - InitializeWithWorkflow(PSWorkflowInstance, closeStreams); - _hasMoreDataOnDisk = false; - } - } - - /// - /// Unloads job streams information. Enables jobs to - /// clear stream information from memory - /// - protected override void DoUnloadJobStreams() - { - if (_workflowInstance == null) return; - - lock (_syncObject) - { - if (_workflowInstance == null) return; - - // if persisting the streams was necessary and - // there was an error it means that the streams - // contain objects that are not serializable - // In such a case we should not dispose the - // streams from memory - if (!_workflowInstance.SaveStreamsIfNecessary()) return; - - _hasMoreDataOnDisk = true; - _workflowInstance.DisposeStreams(); - } - } - - /// - /// start a job. The job will be started with the parameters - /// specified in StartParameters - /// - /// It is redundant to have a method named StartJob - /// on a job class. However, this is done so as to avoid - /// an FxCop violation "CA1716:IdentifiersShouldNotMatchKeywords" - /// Stop and Resume are reserved keyworks in C# and hence cannot - /// be used as method names. Therefore to be consistent it has - /// been decided to use *Job in the name of the methods - public override void StartJob() - { - AssertValidState(JobState.NotStarted); - _isAsync = false; - _runtime.JobManager.SubmitOperation(this, DoStartJobLogic, null, JobState.NotStarted); - JobRunning.WaitOne(); - } - - /// - /// Start a job asynchronously - /// - public override void StartJobAsync() - { -#pragma warning disable 56500 - try - { - // Assert here and catch it for event arguments to reduce the number - // of error operations in the throttle manager. - AssertValidState(JobState.NotStarted); - _runtime.JobManager.SubmitOperation(this, DoStartJobAsync, null, JobState.NotStarted); - } - catch (Exception e) - { - // Catching all exceptions is valid here because - // Transferring exception with event arguments. - OnStartJobCompleted(new AsyncCompletedEventArgs(e, false, null)); - } -#pragma warning restore 56500 - } - - /// - /// Stop a job asynchronously - /// - public override void StopJobAsync() - { - AssertNotDisposed(); - _tracer.WriteMessage(ClassNameTrace, "StopJobAsync", WorkflowGuidForTraces, this, ""); - var asyncOp = AsyncOperationManager.CreateOperation(null); - - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - workerDelegate.BeginInvoke( - asyncOp, - ActionType.Stop, - string.Empty, - string.Empty, - false, - null, - null); - } - - /// - /// Suspend a job - /// - public override void SuspendJob() - { - AssertNotDisposed(); - - DoSuspendJob(); - - // DoSuspendJob returns a bool, but - // in the case of a SuspendJob call, we do not - // want to return until the job is in the correct state. - // The boolean return value is used by the WorkflowManager - // for Shutdown purposes. - JobSuspendedOrAborted.WaitOne(); - } - - /// - /// Asynchronously suspend a job - /// - public override void SuspendJobAsync() - { - AssertNotDisposed(); - _tracer.WriteMessage(ClassNameTrace, "SuspendJobAsync", WorkflowGuidForTraces, this, ""); - var asyncOp = AsyncOperationManager.CreateOperation(null); - - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - workerDelegate.BeginInvoke( - asyncOp, - ActionType.Suspend, - string.Empty, - string.Empty, - false, - null, - null); - } - - /// - /// Stop Job - /// - /// True to force stop - /// Reason for forced stop - public override void StopJob(bool force, string reason) - { - StopJob(force, reason, false); - } - - /// - /// Stop Job - /// - /// True to force stop - /// Reason for forced stop - /// Suppress error for forced stop - public void StopJob(bool force, string reason, bool suppressError) - { - AssertNotDisposed(); - - if (force) - { - if (DoTerminateJob(reason, suppressError)) - Finished.WaitOne(); - } - else - { - DoStopJob(); - Finished.WaitOne(); - } - } - - /// - /// Stop Job Asynchronously - /// - /// True to force stop - /// Reason for forced stop - public override void StopJobAsync(bool force, string reason) - { - StopJobAsync(force, reason, false); - } - - /// - /// Stop job asynchronously - /// - /// True to force stop - /// Reason for forced stop - /// Suppress error for forced stop - public void StopJobAsync(bool force, string reason, bool suppressError) - { - AssertNotDisposed(); - _tracer.WriteMessage(ClassNameTrace, "StopJobAsync", WorkflowGuidForTraces, this, ""); - - var asyncOp = AsyncOperationManager.CreateOperation(null); - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - - ActionType actionType = ActionType.Stop; - - if (force) - { - actionType = ActionType.Terminate; - } - - workerDelegate.BeginInvoke( - asyncOp, - actionType, - reason, - string.Empty, - suppressError, - null, - null); - } - - /// - /// Suspend Job - /// - /// - /// - public override void SuspendJob(bool force, string reason) - { - AssertNotDisposed(); - - if (force) - { - DoAbortJob(reason); - JobSuspendedOrAborted.WaitOne(); - } - else - { - DoSuspendJob(); - JobSuspendedOrAborted.WaitOne(); - } - } - - /// - /// Suspend Job Asynchronously - /// - /// - /// - public override void SuspendJobAsync(bool force, string reason) - { - AssertNotDisposed(); - _tracer.WriteMessage(ClassNameTrace, "SuspendJobAsync", WorkflowGuidForTraces, this, ""); - - var asyncOp = AsyncOperationManager.CreateOperation(null); - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - ActionType actionType = ActionType.Suspend; - - if (force) - { - actionType = ActionType.Abort; - } - - workerDelegate.BeginInvoke( - asyncOp, - actionType, - reason, - string.Empty, - false, - null, - null); - } - - /// - /// Resume a suspended job - /// - public override void ResumeJob() - { - ResumeJob(null); - } - - /// - /// Resume a suspended job - /// - /// - public virtual void ResumeJob(string label) - { - AssertNotDisposed(); - var errorId = Guid.NewGuid(); - var waitHandle = new ManualResetEvent(false); - _statusMessage = Resources.JobQueuedForResume; - _runtime.JobManager.SubmitOperation(this, DoResumeJobCatchException, new Tuple(new Tuple(label, waitHandle), errorId), JobState.Suspended); - waitHandle.WaitOne(); - waitHandle.Dispose(); - - lock (_resumeErrorSyncObject) - { - Exception exception; - if (_resumeErrors.TryGetValue(errorId, out exception)) - { - _resumeErrors.Remove(errorId); - throw exception; - } - } - } - - /// - /// ResumeBookmark - /// - /// - /// - public void ResumeBookmark(Bookmark bookmark, object state) - { - if (bookmark == null) throw new ArgumentNullException("bookmark"); - - DoResumeBookmark(bookmark, state); - } - - /// - /// ResumeBookmark - /// - /// - /// - /// - public void ResumeBookmark(Bookmark bookmark, bool supportDisconnectedStreams, PowerShellStreams streams) - { - if (bookmark == null) throw new ArgumentNullException("bookmark"); - if (streams == null) throw new ArgumentNullException("streams"); - - PSResumableActivityContext arguments = new PSResumableActivityContext(streams); - arguments.SupportDisconnectedStreams = supportDisconnectedStreams; - arguments.Failed = false; - arguments.Error = null; - - DoResumeBookmark(bookmark, arguments); - } - - /// - /// ResumeBookmark - /// - /// - /// - /// - /// - public void ResumeBookmark(Bookmark bookmark, bool supportDisconnectedStreams, PowerShellStreams streams, Exception exception) - { - if (bookmark == null) throw new ArgumentNullException("bookmark"); - if (streams == null) throw new ArgumentNullException("streams"); - if (exception == null) throw new ArgumentNullException("exception"); - - PSResumableActivityContext arguments = new PSResumableActivityContext(streams); - arguments.SupportDisconnectedStreams = supportDisconnectedStreams; - arguments.Failed = true; - arguments.Error = exception; - - DoResumeBookmark(bookmark, arguments); - } - - /// - /// GetPersistableIdleAction - /// - /// - /// - /// - public PSPersistableIdleAction GetPersistableIdleAction(ReadOnlyCollection bookmarks, bool externalSuspendRequest) - { - PSPersistableIdleAction defaultValue = this.PSWorkflowInstance.GetPersistableIdleAction(bookmarks, externalSuspendRequest); - - if (defaultValue == PSPersistableIdleAction.Suspend && listOfLabels.Count > 0) - { - // Labeled resumption is in progress so we cannot allow the suspension - return PSPersistableIdleAction.None; - } - - - lock (_syncObject) - { - if (defaultValue == PSPersistableIdleAction.Suspend && listOfLabels.Count > 0) - { - // Labeled resumption is in progress so we cannot allow the suspension - return PSPersistableIdleAction.None; - } - - if (defaultValue == PSPersistableIdleAction.Suspend) - wfSuspendInProgress = true; - - return defaultValue; - } - } - - private bool wfSuspendInProgress = false; - private List listOfLabels = new List(); - - - /// - /// Resume a suspended job asynchronously. - /// - public override void ResumeJobAsync() - { -#pragma warning disable 56500 - try - { - AssertNotDisposed(); - _runtime.JobManager.SubmitOperation(this, DoResumeJobAsync, null, JobState.Suspended); - } - catch (Exception e) - { - // Catching all exceptions is valid here because - // Transferring exception with event arguments. - OnResumeJobCompleted(new AsyncCompletedEventArgs(e, false, null)); - } -#pragma warning restore 56500 - } - - private void DoResumeJobAsync(object state) - { - _tracer.WriteMessage(ClassNameTrace, "DoResumeJobAsync", WorkflowGuidForTraces, this, ""); - var label = state as string; - var asyncOp = AsyncOperationManager.CreateOperation(null); - - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - workerDelegate.BeginInvoke( - asyncOp, - ActionType.Resume, - string.Empty, - label, - false, - null, - null); - } - - /// - /// Resume a suspended job asynchronously. - /// - /// - public virtual void ResumeJobAsync(string label) - { -#pragma warning disable 56500 - try - { - AssertNotDisposed(); - _runtime.JobManager.SubmitOperation(this, DoResumeJobAsync, label, JobState.Suspended); - } - catch (Exception e) - { - // Catching all exceptions is valid here because - // Transferring exception with event arguments. - OnResumeJobCompleted(new AsyncCompletedEventArgs(e, false, null)); - } -#pragma warning restore 56500 - } - - /// - /// Unblock a blocked job - /// - public override void UnblockJob() - { - throw new NotSupportedException(); - } - - /// - /// Unblock a blocked job asynchronously - /// - public override void UnblockJobAsync() - { - throw new NotSupportedException(); - } - - #endregion - - #region IDisposable - - /// - /// - /// - /// - protected override void Dispose(bool disposing) - { - if (_isDisposed) return; - - lock (SyncRoot) - { - if (_isDisposed) return; - _isDisposed = true; - } - - if (disposing) - { - try - { - // WorkflowInstance should be the first one to be disposed - // - if (_workflowInstance != null) - _workflowInstance.Dispose(); - - StateChanged -= HandleMyStateChanged; - - if (_jobRunning != null) - _jobRunning.Close(); - - if (_jobSuspendedOrAborted != null) - _jobSuspendedOrAborted.Dispose(); - - _tracer.Dispose(); - } - finally - { - base.Dispose(true); - } - } - } - - private void AssertNotDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException("PSWorkflowJob"); - } - } - - /// - /// Check and add StateChanged event handler - /// - /// - /// - /// - internal bool CheckAndAddStateChangedEventHandler(EventHandler handler, JobState expectedState) - { - lock(SyncRoot) - { - // Event Handler should be added - // if JobState is not changed after job submitted to the Workflow job manager queue, OR - // if job is already running due to other labelled resume operation or other start job operation. - // - if (JobStateInfo.State == expectedState || JobStateInfo.State == JobState.Running) - { - Dbg.Assert(expectedState == JobState.NotStarted || expectedState == JobState.Suspended, "Only Start and Resume operations are serviced by PSWorkflowJobManager.StartOperationsFromQueue"); - StateChanged += handler; - return true; - } - else - { - return false; - } - } - } - - #endregion IDisposable - - #region IJobDebugger - - /// - /// Job Debugger - /// - public Debugger Debugger - { - get - { - return PSWorkflowDebugger; - } - } - - /// - /// True if job is synchronous and can be debugged. - /// - public bool IsAsync - { - get { return _isAsync; } - set { _isAsync = value; } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJobSourceAdapter.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJobSourceAdapter.cs deleted file mode 100644 index 2c9f6c71b6e..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJobSourceAdapter.cs +++ /dev/null @@ -1,1019 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Activities; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Tracing; -using System.Management.Automation.Remoting.WSMan; -using Dbg = System.Diagnostics.Debug; -using System.Management.Automation.Remoting; - - -// Stops compiler from warning about unknown warnings -#pragma warning disable 1634, 1691 - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// Adapter that allows workflow instances to be exposed as jobs in PowerShell. - /// NOTE: This class has been unsealed to allow extensibility for Opalis. Further - /// thought is needed around the best way to enable reuse of this class. - /// - public sealed class WorkflowJobSourceAdapter : JobSourceAdapter - { - #region ContainerParentJobRepository - - private class ContainerParentJobRepository : Repository - { - /// - /// - /// - /// - /// - protected override Guid GetKey(ContainerParentJob item) - { - return item.InstanceId; - } - - internal ContainerParentJobRepository(string identifier): base(identifier) - { - - } - } - #endregion ContainerParentJobRepository - - #region Members - - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private readonly Tracer _structuredTracer = new Tracer(); - - private static readonly WorkflowJobSourceAdapter Instance = new WorkflowJobSourceAdapter(); - - private readonly ContainerParentJobRepository _jobRepository = - new ContainerParentJobRepository("WorkflowJobSourceAdapterRepository"); - - private readonly object _syncObject = new object(); - private bool _repositoryPopulated; - internal const string AdapterTypeName = "PSWorkflowJob"; - - #endregion Members - - #region Overrides of JobSourceAdapter - - /// - /// Gets the WorkflowJobSourceAdapter instance. - /// - public static WorkflowJobSourceAdapter GetInstance() - { - return Instance; - } - - private PSWorkflowJobManager _jobManager; - /// - /// GetJobManager - /// - /// - public PSWorkflowJobManager GetJobManager() - { - if (_jobManager != null) - return _jobManager; - - lock (_syncObject) - { - if (_jobManager != null) - return _jobManager; - - _jobManager = PSWorkflowRuntime.Instance.JobManager; - } - return _jobManager; - } - - /// - /// GetPSWorkflowRunTime - /// - /// - public PSWorkflowRuntime GetPSWorkflowRuntime() - { - if (_runtime != null) - return _runtime; - - lock (_syncObject) - { - if (_runtime != null) - return _runtime; - - _runtime = PSWorkflowRuntime.Instance; - } - return _runtime; - } - - private PSWorkflowRuntime _runtime; - private PSWorkflowValidator _wfValidator; - internal PSWorkflowValidator GetWorkflowValidator() - { - if (_wfValidator != null) - return _wfValidator; - - lock (_syncObject) - { - if (_wfValidator != null) - return _wfValidator; - - _wfValidator = new PSWorkflowValidator(PSWorkflowRuntime.Instance.Configuration); - } - return _wfValidator; - } - - /// - /// Create a new job with the specified JobSpecification. - /// - /// specification - /// job object - [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - public override Job2 NewJob(JobInvocationInfo specification) - { - if (specification == null) - throw new ArgumentNullException("specification"); - - if (specification.Definition == null) - throw new ArgumentException(Resources.NewJobDefinitionNull, "specification"); - - if (specification.Definition.JobSourceAdapterType != GetType()) - throw new InvalidOperationException(Resources.NewJobWrongType); - - if (specification.Parameters.Count == 0) - { - // If there are no parameters passed in, we create one child job with nothing specified. - specification.Parameters.Add(new CommandParameterCollection()); - } - - // validation has to happen before job creation - bool? isSuspendable = null; - Activity activity = ValidateWorkflow(specification, null, ref isSuspendable); - ContainerParentJob newJob = GetJobManager().CreateJob(specification, activity); - - // We do not want to generate the warning message if the request is coming from - // Server Manager - if (PSSessionConfigurationData.IsServerManager == false) - { - foreach (PSWorkflowJob job in newJob.ChildJobs) - { - bool? psPersistValue = null; - PSWorkflowContext context = job.PSWorkflowInstance.PSWorkflowContext; - if (context != null && context.PSWorkflowCommonParameters != null && context.PSWorkflowCommonParameters.ContainsKey(Constants.Persist)) - { - psPersistValue = context.PSWorkflowCommonParameters[Constants.Persist] as bool?; - } - - // check for invocation time pspersist value if not true then there is a possibility that workflow is not suspendable. - if (psPersistValue == null || (psPersistValue == false)) - { - // check for authoring time definition of persist activity - if (isSuspendable != null && isSuspendable.Value == false) - { - job.Warning.Add(new WarningRecord(Resources.WarningMessageForPersistence)); - job.IsSuspendable = isSuspendable; - } - } - - } - } - - StoreJobIdForReuse(newJob, true); - _jobRepository.Add(newJob); - - return newJob; - } - - /// - /// Get the list of jobs that are currently available in the workflow instance table. - /// - /// collection of job objects - public override IList GetJobs() - { - _tracer.WriteMessage("WorkflowJobSourceAdapter: Getting all Workflow jobs"); - PopulateJobRepositoryIfRequired(); - return new List(_jobRepository.GetItems()); - } - - /// - /// 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) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Getting Workflow jobs by name: {0}", name)); - PopulateJobRepositoryIfRequired(); - WildcardPattern patternForName = new WildcardPattern(name, WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - - List selectedJobs = new List(); - var jobs = _jobRepository.GetItems().Where(parentJob => patternForName.IsMatch(parentJob.Name)).Cast(). - ToList(); - selectedJobs.AddRange(jobs); - - if (recurse) - { - var filter = new Dictionary { { Constants.JobMetadataName, patternForName } }; - var childJobs = GetJobManager().GetJobs(GetChildJobsFromRepository(), WorkflowFilterTypes.JobMetadata, filter); - selectedJobs.AddRange(childJobs); - } - - return selectedJobs; - } - - /// - /// 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) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Getting Workflow jobs by command: {0}", command)); - PopulateJobRepositoryIfRequired(); - List jobs = new List(); - WildcardPattern patternForCommand = new WildcardPattern(command, WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - - var list = - _jobRepository.GetItems().Where(parentJob => patternForCommand.IsMatch(parentJob.Command)).Cast(). - ToList(); - if (list.Count > 0) - jobs.AddRange(list); - - if (recurse) - { - var filter = new Dictionary {{Constants.JobMetadataParentCommand, patternForCommand}}; - var listChildJobs = GetJobManager().GetJobs(GetChildJobsFromRepository(), WorkflowFilterTypes.JobMetadata, - filter); - jobs.AddRange(listChildJobs); - } - - return jobs; - } - - /// - /// Get list of jobs that has the specified id - /// - /// Guid to match - /// - /// job with the specified guid - public override Job2 GetJobByInstanceId(Guid instanceId, bool recurse) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Getting Workflow job by instance id: {0}", instanceId)); - - // there can be a lot of jobs in the repository. First search whats in - // memory before rehydrating from disk - Job2 selectedJob = _jobRepository.GetItem(instanceId); - if (selectedJob == null) - { - PopulateJobRepositoryIfRequired(); - selectedJob = _jobRepository.GetItem(instanceId); - } - - if (selectedJob != null) return selectedJob; - - if (recurse) - { - selectedJob = GetChildJobsFromRepository().FirstOrDefault(job => job.InstanceId == instanceId); - } - return selectedJob; - } - - /// - /// Get job by session id. - /// - /// The session id. - /// - /// The returned job2 object. - public override Job2 GetJobBySessionId(int id, bool recurse) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Getting Workflow job by session id: {0}", id)); - - PopulateJobRepositoryIfRequired(); - - Job2 selectedJob = _jobRepository.GetItems().FirstOrDefault(job => job.Id == id); - if (selectedJob != null) return selectedJob; - - if (recurse) - { - selectedJob = GetChildJobsFromRepository().FirstOrDefault(job => job.Id == id); - } - return selectedJob; - } - - /// - /// 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) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Getting Workflow jobs by state: {0}", state)); - - PopulateJobRepositoryIfRequired(); - // Parent states are derived from child states, so a simple filter will not work. - List jobs = recurse - ? GetChildJobsFromRepository().Where(job => job.JobStateInfo.State == state).Cast - ().ToList() - : _jobRepository.GetItems().Where(job => job.JobStateInfo.State == state).Cast() - .ToList(); - return jobs; - } - - /// - /// 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 ArgumentNullException("filter"); - } - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "WorkflowJobSourceAdapter: Getting Workflow jobs by filter: {0}", filter)); - PopulateJobRepositoryIfRequired(); - - // Do not modify the user's collection. - Dictionary filter2 = new Dictionary(filter, StringComparer.CurrentCultureIgnoreCase); - bool addPid = false; - bool searchParentJobs = true; - if (filter2.Keys.Count == 0) - searchParentJobs = false; - else - { - if (filter2.Keys.Any(key => (((!key.Equals(Constants.JobMetadataSessionId, StringComparison.OrdinalIgnoreCase) && !key.Equals(Constants.JobMetadataInstanceId, StringComparison.OrdinalIgnoreCase)) && !key.Equals(Constants.JobMetadataName, StringComparison.OrdinalIgnoreCase)) && !key.Equals(Constants.JobMetadataCommand, StringComparison.OrdinalIgnoreCase)) && !key.Equals(Constants.JobMetadataFilterState, StringComparison.OrdinalIgnoreCase))) - { - searchParentJobs = false; - } - } - - List jobs = new List(); - // search container parent jobs first - if (searchParentJobs) - { - List repositoryJobs = _jobRepository.GetItems(); - - List searchList = SearchJobsOnV2Parameters(repositoryJobs, filter2); - repositoryJobs.Clear(); - - if (searchList.Count > 0) - { - jobs.AddRange(searchList); - } - } - - if (recurse) - { - // If the session Id parameter is present, make sure that the Id match is valid by adding the process Id to the filter. - if (filter2.ContainsKey(Constants.JobMetadataSessionId)) - addPid = true; - - if (addPid) filter2.Add(Constants.JobMetadataPid, Process.GetCurrentProcess().Id); - - if (filter2.ContainsKey(Constants.JobMetadataFilterState)) - { - filter2.Remove(Constants.JobMetadataFilterState); - } - - LoadWorkflowInstancesFromStore(); - - // remove state from filter here and do it separately - IEnumerable workflowInstances = GetJobManager().GetJobs(WorkflowFilterTypes.All, filter2); - - if (filter.ContainsKey(Constants.JobMetadataFilterState)) - { - JobState searchState = - (JobState) - LanguagePrimitives.ConvertTo(filter[Constants.JobMetadataFilterState], typeof(JobState), CultureInfo.InvariantCulture); - var list = workflowInstances.Where(job => job.JobStateInfo.State == searchState).ToList(); - jobs.AddRange(list); - } - else - { - jobs.AddRange(workflowInstances); - } - } - - List cpjs = new List(); - foreach (var job in jobs) - { - if (job is ContainerParentJob && !cpjs.Contains(job)) - { - cpjs.Add(job); - continue; - } - - PSWorkflowJob wfj = job as PSWorkflowJob; - Dbg.Assert(wfj != null, "if it's not a containerparentjob, it had better be a workflowjob"); - ContainerParentJob cpj = _jobRepository.GetItem((Guid)wfj.JobMetadata[Constants.JobMetadataParentInstanceId]); - if (!cpjs.Contains(cpj)) - { - cpjs.Add(cpj); - } - } - - return cpjs; - } - - /// - /// Remove a job from the store - /// - /// job object to remove - public override void RemoveJob(Job2 job) - { - if (job == null) - { - throw new ArgumentNullException("job"); - } - _structuredTracer.RemoveJobStarted(job.InstanceId); - - var jobInMemory = _jobRepository.GetItem(job.InstanceId); - if (jobInMemory == null) - { - // the specified job is not available in memory - // load the job repository - PopulateJobRepositoryIfRequired(); - } - - if (!(job is ContainerParentJob)) - { - throw new InvalidOperationException(Resources.CannotRemoveWorkflowJobDirectly); - } - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Removing Workflow job with instance id: {0}", job.InstanceId)); - - Exception innerException = null; - - foreach (Job childJob in job.ChildJobs) - { - PSWorkflowJob workflowChildJob = childJob as PSWorkflowJob; - - if (workflowChildJob == null) continue; - - try - { - GetJobManager().RemoveJob(workflowChildJob.InstanceId); - _structuredTracer.JobRemoved(job.InstanceId, - childJob.InstanceId, workflowChildJob.WorkflowGuid); - } - catch (ArgumentException exception) - { - //ignoring the error message and just logging them into ETW - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "WorkflowJobSourceAdapter: Ignoring the exception. Exception details: {0}", - exception)); - innerException = exception; - _structuredTracer.JobRemoveError(job.InstanceId, - childJob.InstanceId, workflowChildJob.WorkflowGuid, - exception.Message); - } - } - - // remove the container parent job from repository - try - { - _jobRepository.Remove((ContainerParentJob) job); - } - catch(ArgumentException exception) - { - //ignoring the error message and just logging them into ETW - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "WorkflowJobSourceAdapter: Ignoring the exception. Exception details: {0}", - exception)); - innerException = exception; - } - job.Dispose(); - - // Failed to remove the job? - if (innerException != null) - { - ArgumentException exc = new ArgumentException(Resources.WorkflowChildCouldNotBeRemoved, "job", innerException); - throw exc; - } - } - - // in case of multiple child jobs synchronization is very important - private readonly object _syncRemoveChilJob = new object(); - internal void RemoveChildJob(Job2 childWorkflowJob) - { - _structuredTracer.RemoveJobStarted(childWorkflowJob.InstanceId); - PopulateJobRepositoryIfRequired(); - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Removing Workflow job with instance id: {0}", childWorkflowJob.InstanceId)); - - lock (_syncRemoveChilJob) - { - PSWorkflowJob childJob = childWorkflowJob as PSWorkflowJob; - if (childJob == null) return; - - object data; - PSWorkflowInstance instance = childJob.PSWorkflowInstance; - - if (!instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataParentInstanceId, out data)) return; - var parentInstanceId = (Guid)data; - ContainerParentJob job = _jobRepository.GetItem(parentInstanceId); - - job.ChildJobs.Remove(childJob); - - - try - { - GetJobManager().RemoveJob(childJob.InstanceId); - _structuredTracer.JobRemoved(job.InstanceId, - childJob.InstanceId, childJob.WorkflowGuid); - } - catch (ArgumentException exception) - { - //ignoring the error message and just logging them into ETW - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "WorkflowJobSourceAdapter: Ignoring the exception. Exception details: {0}", - exception)); - - _structuredTracer.JobRemoveError(job.InstanceId, - childJob.InstanceId, childJob.WorkflowGuid, - exception.Message); - } - - if (job.ChildJobs.Count == 0) - { - // remove the container parent job from repository - try - { - _jobRepository.Remove(job); - } - catch (ArgumentException exception) - { - //ignoring the error message and just logging them into ETW - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "WorkflowJobSourceAdapter: Ignoring the exception. Exception details: {0}", - exception)); - } - job.Dispose(); - } - } - } - - #endregion - - #region Private Methods - - private Activity ValidateWorkflow(JobInvocationInfo invocationInfo, Activity activity, ref bool? isSuspendable) - { - PSWorkflowRuntime runtime = PSWorkflowRuntime.Instance; - JobDefinition definition = invocationInfo.Definition; - WorkflowJobDefinition workflowJobDefinition = WorkflowJobDefinition.AsWorkflowJobDefinition(definition); - - bool externalActivity = true; - - if (activity == null) - { - bool windowsWorkflow; - activity = DefinitionCache.Instance.GetActivityFromCache(workflowJobDefinition, out windowsWorkflow); - // Debug.Assert(activity != null, "WorkflowManager failed validation for a null activity"); - if (activity == null) - { - throw new InvalidOperationException(); - } - - externalActivity = false; - - // skip validation if it is a windows workflow - if (windowsWorkflow) - return activity; - } - - // If validation is disabled, just return the activity as is... - if (!runtime.Configuration.EnableValidation) - { - return activity; - } - - if (externalActivity && !DefinitionCache.Instance.AllowExternalActivity) - { - // If there is a custom validator activity, then call it. If it returns true - // then just return the activity. - if (Validation.CustomHandler == null) - throw new InvalidOperationException(); - - if (Validation.CustomHandler.Invoke(activity)) - { - return activity; - } - - string displayName = activity.DisplayName; - - if (string.IsNullOrEmpty(displayName)) - displayName = this.GetType().Name; - - string message = string.Format(CultureInfo.CurrentCulture, Resources.InvalidActivity, displayName); - throw new ValidationException(message); - } - PSWorkflowValidationResults validationResults = GetWorkflowValidator().ValidateWorkflow( - definition.InstanceId, activity, - DefinitionCache.Instance.GetRuntimeAssemblyName(workflowJobDefinition)); - - if (validationResults.Results != null) - { - GetWorkflowValidator().ProcessValidationResults(validationResults.Results); - } - - isSuspendable = validationResults.IsWorkflowSuspendable; - - return activity; - } - - /// - /// Searches a list of jobs with a given set of filters for all - /// the V2 search parameters. This function searches in a specific - /// order so that a get call from an API returns without having - /// to do much processing in terms of wildcard pattern matching - /// - /// incoming enumeration of jobs to - /// search - /// dictionary of filters to use as search - /// criteria - /// narrowed down list of jobs that satisfy the filter - /// criteria - internal static List SearchJobsOnV2Parameters(IEnumerable jobsToSearch, IDictionary filter) - { - List searchList = new List(); - searchList.AddRange(jobsToSearch); - - List newlist; - if (filter.ContainsKey(Constants.JobMetadataSessionId)) - { - int searchId = (int)filter[Constants.JobMetadataSessionId]; - newlist = searchList.Where(job => job.Id == searchId).ToList(); - searchList.Clear(); - searchList = newlist; - } - if (filter.ContainsKey(Constants.JobMetadataInstanceId)) - { - var value = filter[Constants.JobMetadataInstanceId]; - Guid searchGuid; - LanguagePrimitives.TryConvertTo(value, CultureInfo.InvariantCulture, out searchGuid); - newlist = searchList.Where(job => job.InstanceId == searchGuid).ToList(); - searchList.Clear(); - searchList = newlist; - } - if (filter.ContainsKey(Constants.JobMetadataFilterState)) - { - JobState searchState = - (JobState) - LanguagePrimitives.ConvertTo(filter[Constants.JobMetadataFilterState], typeof(JobState), CultureInfo.InvariantCulture); - newlist = searchList.Where(job => job.JobStateInfo.State == searchState).ToList(); - searchList.Clear(); - searchList = newlist; - } - if (filter.ContainsKey(Constants.JobMetadataName)) - { - Debug.Assert(filter[Constants.JobMetadataName] is string || - filter[Constants.JobMetadataName] is WildcardPattern, "filter value should be a string or wildcard"); - - WildcardPattern patternForName; - if (filter[Constants.JobMetadataName] is string) - { - string name = (string)filter[Constants.JobMetadataName]; - patternForName = new WildcardPattern(name, WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - } - else - patternForName = (WildcardPattern) filter[Constants.JobMetadataName]; - - newlist = - searchList.Where(parentJob => patternForName.IsMatch(parentJob.Name)).ToList(); - searchList.Clear(); - searchList = newlist; - } - if (filter.ContainsKey(Constants.JobMetadataCommand)) - { - Debug.Assert(filter[Constants.JobMetadataCommand] is string || - filter[Constants.JobMetadataCommand] is WildcardPattern, "filter value should be a string or wildcard"); - - WildcardPattern patternForCommand; - if (filter[Constants.JobMetadataCommand] is string) - { - string command = (string)filter[Constants.JobMetadataCommand]; - patternForCommand = new WildcardPattern(command, WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - } - else - patternForCommand = (WildcardPattern)filter[Constants.JobMetadataCommand]; - - newlist = - searchList.Where(parentJob => patternForCommand.IsMatch(parentJob.Command)).ToList(); - searchList.Clear(); - searchList = newlist; - } - return searchList; - } - - private void PopulateJobRepositoryIfRequired() - { - // we need to populate the repository the first time - if (_repositoryPopulated) return; - lock (_syncObject) - { - if (_repositoryPopulated) return; - - _repositoryPopulated = true; - - LoadWorkflowInstancesFromStore(); - - // start the workflow manager - this will either - // create the store if it doesn't exist or load - // existing workflows - foreach (var job in CreateJobsFromWorkflows(GetJobManager().GetJobs(), true)) - { - try - { - _jobRepository.Add((ContainerParentJob) job); - } - catch(ArgumentException) - { - // if the job was created using NewJob() then - // the repository already has it and an exception - // will be thrown. Ignore the exception in this case - } - } - } - } - - private ICollection GetChildJobsFromRepository() - { - return _jobRepository.GetItems().SelectMany(parentJob => parentJob.ChildJobs).Cast().ToList(); - } - - private IEnumerable CreateJobsFromWorkflows(IEnumerable workflowJobs, bool returnParents) - { - // Jobs in this collection correspond to the ContainerParentJob objects. PSWorkflowJob objects - // are children of these. - var reconstructedParentJobs = new Dictionary(); - var jobs = new List(); - - if (workflowJobs == null) return jobs; - - // If a workflow instance has incomplete metadata, we do not create the job for it. - foreach (var job in workflowJobs) - { - var wfjob = job as PSWorkflowJob; - Debug.Assert(wfjob != null, "Job supplied must be of type PSWorkflowJob"); - PSWorkflowInstance instance = wfjob.PSWorkflowInstance; - Dbg.Assert(instance != null, "PSWorkflowInstance should be reconstructed before attempting to rehydrate job"); - - if (!instance.JobStateRetrieved || instance.PSWorkflowContext.JobMetadata == null || instance.PSWorkflowContext.JobMetadata.Count == 0) continue; - - object data; - string name, command; - Guid instanceId; - if (!GetJobInfoFromMetadata(instance, out command, out name, out instanceId)) continue; - - if (!instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataParentInstanceId, out data)) - continue; - var parentInstanceId = (Guid)data; - - // If the parent job is needed, find or create it now so that the ID is sequentially lower. - if (returnParents && !reconstructedParentJobs.ContainsKey(parentInstanceId)) - { - if (!instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataParentName, out data)) - continue; - var parentName = (string)data; - - if (!instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataParentCommand, out data)) - continue; - var parentCommand = (string)data; - - JobIdentifier parentId = RetrieveJobIdForReuse(parentInstanceId); - ContainerParentJob parentJob = parentId != null - ? new ContainerParentJob(parentCommand, parentName, parentId, AdapterTypeName) - : new ContainerParentJob(parentCommand, parentName, parentInstanceId, AdapterTypeName); - - // update job metadata with new parent session Id--needed for filtering. - // The pid in the metadata has already been updated at this point. - Dbg.Assert( - instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentSessionId), - "Job Metadata for instance incomplete."); - if (instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentSessionId)) - instance.PSWorkflowContext.JobMetadata[Constants.JobMetadataParentSessionId] = parentJob.Id; - - reconstructedParentJobs.Add(parentInstanceId, parentJob); - } - - // update job metadata with new session Id--needed for filtering. - Dbg.Assert(instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataSessionId), "Job Metadata for instance incomplete."); - Dbg.Assert(instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataPid), "Job Metadata for instance incomplete."); - if (instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataSessionId)) - instance.PSWorkflowContext.JobMetadata[Constants.JobMetadataSessionId] = job.Id; - if (instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataPid)) - instance.PSWorkflowContext.JobMetadata[Constants.JobMetadataPid] = Process.GetCurrentProcess().Id; - - job.StartParameters = new List(); - CommandParameterCollection commandParameterCollection = new CommandParameterCollection(); - AddStartParametersFromCollection(instance.PSWorkflowContext.WorkflowParameters, commandParameterCollection); - AddStartParametersFromCollection(instance.PSWorkflowContext.PSWorkflowCommonParameters, commandParameterCollection); - - bool takesPSPrivateMetadata; - if (instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.WorkflowTakesPrivateMetadata)) - { - takesPSPrivateMetadata = (bool)instance.PSWorkflowContext.JobMetadata[Constants.WorkflowTakesPrivateMetadata]; - } - else - { - DynamicActivity da = instance.PSWorkflowDefinition != null ? instance.PSWorkflowDefinition.Workflow as DynamicActivity : null; - takesPSPrivateMetadata = da != null && da.Properties.Contains(Constants.PrivateMetadata); - } - - // If there is Private Metadata and it is not included in the "Input" collection, add it now. - if (instance.PSWorkflowContext.PrivateMetadata != null - && instance.PSWorkflowContext.PrivateMetadata.Count > 0 - && !takesPSPrivateMetadata) - { - Hashtable privateMetadata = new Hashtable(); - foreach (var pair in instance.PSWorkflowContext.PrivateMetadata) - { - privateMetadata.Add(pair.Key, pair.Value); - } - commandParameterCollection.Add(new CommandParameter(Constants.PrivateMetadata, privateMetadata)); - } - job.StartParameters.Add(commandParameterCollection); - - if (returnParents) - { - ((ContainerParentJob)reconstructedParentJobs[parentInstanceId]).AddChildJob(job); - } - else - { - jobs.Add(job); - } - - if (!wfjob.WorkflowInstanceLoaded) - { - // RestoreFromWorkflowInstance sets the job state. Because we've used AddChildJob, the parent's state will be - // updated automatically. - wfjob.RestoreFromWorkflowInstance(instance); - } - } - - if (returnParents) - { - jobs.AddRange(reconstructedParentJobs.Values); - } - - return jobs; - } - - /// - /// Handles the wsman server shutting down event. - /// - /// sender of this event - /// arguments describing the event - private void OnWSManServerShuttingDownEventRaised(object sender, EventArgs e) - { - try - { - IsShutdownInProgress = true; - PSWorkflowConfigurationProvider _configuration = (PSWorkflowConfigurationProvider)PSWorkflowRuntime.Instance.Configuration; - int timeout = _configuration.WorkflowShutdownTimeoutMSec; - - GetJobManager().ShutdownWorkflowManager(timeout); - } - catch (Exception exception) - { - - // when exception on shutdown, not much to do much other than logging the exception - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Shutting down WSMan server: Exception details: {0}", - exception)); - - Dbg.Assert(false, "Exception has happened during the shutdown API. [Message] " + exception.Message + "[StackTrace] " + exception.StackTrace); - - } - } - internal bool IsShutdownInProgress = false; - - private static void AddStartParametersFromCollection(Dictionary collection, CommandParameterCollection allParams) - { - if (collection == null || collection.Count <= 0) return; - foreach (var param in collection.Select(o => new CommandParameter(o.Key, o.Value))) - { - allParams.Add(param); - } - } - - #endregion Private Methods - - #region Constructors - - internal WorkflowJobSourceAdapter() - { - WSManServerChannelEvents.ShuttingDown += OnWSManServerShuttingDownEventRaised; - Name = AdapterTypeName; - } - - #endregion Constructors - - #region Internal Methods - - // called from test code to clean up - internal void ClearRepository() - { - foreach (var item in _jobRepository.GetItems()) - { - _jobRepository.Remove(item); - } - } - - internal static bool GetJobInfoFromMetadata(PSWorkflowInstance workflowInstance, out string command, out string name, out Guid instanceId) - { - bool retVal = false; - command = string.Empty; - name = string.Empty; - instanceId = Guid.Empty; - - do - { - object data; - if (!workflowInstance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataName, out data)) break; - name = (string)data; - - if (!workflowInstance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataCommand, out data)) break; - command = (string)data; - - if (!workflowInstance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataInstanceId, out data)) break; - instanceId = (Guid)data; - - retVal = true; - } while (false); - - return retVal; - } - - private bool _fullyLoaded; - internal void LoadWorkflowInstancesFromStore() - { - if (_fullyLoaded) return; - lock (_syncObject) - { - if (_fullyLoaded) return; - - foreach (PSWorkflowId storedInstanceId in PSWorkflowFileInstanceStore.GetAllWorkflowInstanceIds()) - { - try - { - GetJobManager().LoadJobWithIdentifier(storedInstanceId); - } - catch (Exception e) - { - // Auto loading failing so logging into the message trace - - _tracer.WriteMessage("Getting an exception while loading the previously persisted workflows..."); - _tracer.TraceException(e); - // Intentionally continuing on with exception - } - } - - _fullyLoaded = true; - } - - GetJobManager().CleanUpWorkflowJobTable(); - } - - /// - /// Called from tests to cleanup instance table - /// - internal void ClearWorkflowTable() - { - // First clean up existing jobs - GetJobManager().ClearWorkflowManagerInstanceTable(); - - // Now force load all the jobs from disk - _fullyLoaded = false; - LoadWorkflowInstancesFromStore(); - - // Now clean up the stuff loaded from disk - GetJobManager().ClearWorkflowManagerInstanceTable(); - - } - - #endregion Internal Methods - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowManager.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowManager.cs deleted file mode 100644 index 77c9831f08a..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowManager.cs +++ /dev/null @@ -1,1482 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Activities.Validation; -using System.Collections.ObjectModel; -using System.Linq; -using System.Management.Automation.Remoting; -using System.Management.Automation.Remoting.WSMan; -using System.Runtime.InteropServices; - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Activities; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Diagnostics; - using System.Management.Automation; - using System.Management.Automation.Tracing; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.Management.Automation.Runspaces; - using System.Management.Automation.PerformanceData; - using System.Threading; - using Timer = System.Timers.Timer; - - /// - /// Provides interface to upper layers of M3P for calling the Workflow core functionality. - /// Throttles the number of jobs run simultaneously. Used to control the number of workflows that will execute simultaneously - /// - public sealed class PSWorkflowJobManager: IDisposable - { - #region Private Members - - private static readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static readonly Tracer StructuredTracer = new Tracer(); - - private readonly PSWorkflowRuntime _runtime; - /// - /// The class name. - /// - private const string Facility = "WorkflowManager : "; - private LockObjectsCollection lockObjects = new LockObjectsCollection(); - - #endregion Private Members - - #region Job Throttle related Private Members - - private readonly ConcurrentQueue, object, JobState>> _pendingQueue = new ConcurrentQueue, object, JobState>>(); - private static readonly PSPerfCountersMgr PerfCountersMgr = PSPerfCountersMgr.Instance; - private readonly int _throttleLimit; - private int _inProgressCount; - - private const int WaitInterval = 5 * 1000; - - // Timer to call WSManPluginOperationComplete on zero active sessions and no jobs are inprogress or in pending queue - private Timer _shutdownTimer; - - // Plugin/Endpoint process is created during the first shell session creation, so the default value of _activeSessionsCount is 1 - private int _activeSessionsCount = 1; - - /// - /// this is set to a date in the past so that the first time - /// GC happens correctly - /// - private static DateTime _lastGcTime = new DateTime(2011, 1, 1); - private const int GcDelayInMinutes = 5; - private static int _workflowsBeforeGc; - private static int _gcStatus = NotInProgress; - private const int InProgress = 1; - private const int NotInProgress = 0; - private static readonly Tracer etwTracer = new Tracer(); - - /// - /// if these many workflows are run without a GC - /// in between them, then we will force a GC - /// - private const int WorkflowLimitBeforeGc = 25 * 5; - - #endregion Job Throttle related Private Members - - // For testing purpose ONLY - internal static bool TestMode = false; - // For testing purpose ONLY - internal static long ObjectCounter = 0; - - private bool _isDisposed; - private bool _isDisposing; - private readonly object _syncObject = new object(); - - #region WorkflowManager Management - - /// - /// Construct a workflow manager - /// - /// - /// - public PSWorkflowJobManager(PSWorkflowRuntime runtime, int throttleLimit) - { - if (runtime == null) - { - throw new ArgumentNullException("runtime"); - } - - if (TestMode) - { - System.Threading.Interlocked.Increment(ref ObjectCounter); - } - - _runtime = runtime; - _throttleLimit = throttleLimit; - - if (PSWorkflowSessionConfiguration.IsWorkflowTypeEndpoint) - { - _shutdownTimer = new Timer(_runtime.Configuration.WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec); - _shutdownTimer.Elapsed += ShutdownWaitTimerElapsed; - _shutdownTimer.AutoReset = false; - - WSManServerChannelEvents.ActiveSessionsChanged += OnWSManServerActiveSessionsChangedEvent; - } - } - - - /// - /// Dispose implementation. - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose implementation. - /// - /// - private void Dispose(bool disposing) - { - if (_isDisposed || !disposing) - return; - - lock (_syncObject) - { - if (_isDisposed) - return; - - _isDisposing = true; - - // To release the job throttling thread - _waitForJobs.Value.Set(); - - ShutdownWorkflowManager(); - - // Set _waitForJobs to free the service thread - _waitForJobs.Value.Dispose(); - - if (PSWorkflowSessionConfiguration.IsWorkflowTypeEndpoint) - { - WSManServerChannelEvents.ActiveSessionsChanged -= OnWSManServerActiveSessionsChangedEvent; - } - - if (_shutdownTimer != null) - { - _shutdownTimer.Elapsed -= ShutdownWaitTimerElapsed; - _shutdownTimer.Close(); - _shutdownTimer.Dispose(); - _shutdownTimer = null; - } - - _isDisposed = true; - } - } - - private void AssertNotDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException("PSWorkflowJobManager"); - } - } - - /// - /// LoadJob - /// - /// - /// - public PSWorkflowJob LoadJob(PSWorkflowId storedInstanceId) - { - AssertNotDisposed(); - - if (storedInstanceId == null) throw new ArgumentNullException("storedInstanceId"); - - if(LoadJobWithIdentifier(storedInstanceId)) - { - return Get(null, storedInstanceId.Guid); - } - - return null; - } - - internal bool LoadJobWithIdentifier(PSWorkflowId storedInstanceId) - { - PSWorkflowInstance instance = _runtime.Configuration.CreatePSWorkflowInstance(storedInstanceId); - - try - { - instance.InstanceStore.Load(WorkflowStoreComponents.JobState | WorkflowStoreComponents.Metadata); - } - catch (Exception e) - { - Tracer.TraceException(e); - - instance.JobStateRetrieved = false; - instance.PSWorkflowContext = new PSWorkflowContext(); - } - - if (!instance.JobStateRetrieved) - { - instance.RemoveInstance(); - return false; - } - - string command; - string name; - Guid instanceId; - if (!WorkflowJobSourceAdapter.GetJobInfoFromMetadata(instance, out command, out name, out instanceId)) - { - instance.RemoveInstance(); - return false; - } - - if (instance.Timer != null) - { - if (instance.Timer.CheckIfTimerHasReachedAlready(WorkflowTimerType.ElapsedTimer)) - { - instance.RemoveInstance(); - return false; - } - - // starting the elapsed timer immediately. - instance.Timer.StartTimer(WorkflowTimerType.ElapsedTimer); - } - - if (_wfJobTable.ContainsKey(instanceId)) - { - return true; - } - - lock (lockObjects.GetLockObject(instanceId)) - { - if (_wfJobTable.ContainsKey(instanceId)) - { - return true; - } - - PSWorkflowJob newjob = new PSWorkflowJob(_runtime, command, name, instanceId); - newjob.PSWorkflowInstance = instance; - instance.PSWorkflowJob = newjob; - newjob.RestoreFromWorkflowInstance(instance); - newjob.WorkflowInstanceLoaded = true; - newjob.ConfigureWorkflowHandlers(); - if (!_wfJobTable.ContainsKey(newjob.InstanceId)) - { - AddJob(newjob); - } - return true; - } - } - - private readonly object _servicingThreadSyncObject = new object(); - private bool _needToStartServicingThread = true; - - /// - /// Indicates whether the dedicated thread to start jobs is required - /// The thread needs to started only the first time - /// - /// This property helps in delay creation of - /// the thread - private bool NeedToStartServicingThread - { - get - { - if (_needToStartServicingThread) - { - lock(_servicingThreadSyncObject) - { - if (_needToStartServicingThread) - { - _needToStartServicingThread = false; - return true; - } - } - } - return false; - } - } - - private readonly Lazy _waitForJobs = new Lazy(() => new AutoResetEvent(false)); - - private void CheckAndStartServicingThread() - { - // check to see if waking the servicing thread at - // this point will actually lead to servicing. If - // so wake it up - if (_inProgressCount < _throttleLimit && _pendingQueue.Count > 0) - _waitForJobs.Value.Set(); - - if (PSWorkflowSessionConfiguration.IsWorkflowTypeEndpoint) - { - lock (_syncObject) - { - // Start the shutdown timer when there is no inprogress job, no pending job requests and no active sessions - // Otherwise disable it - if (_activeSessionsCount == 0 && _inProgressCount == 0 && _pendingQueue.Count == 0) - { - _shutdownTimer.Enabled = true; - } - else - { - _shutdownTimer.Enabled = false; - } - } - } - - if (!NeedToStartServicingThread) return; - - Thread servicingThread = new Thread(StartOperationsFromQueue) - {Name = "Job Throttling Thread"}; - servicingThread.IsBackground = true; - servicingThread.Start(); - } - - private void WaitTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) - { - Timer waitTimer = sender as Timer; - Debug.Assert(waitTimer != null, "Sender was not send correctly"); - - if (_inProgressCount == 0 && _pendingQueue.Count == 0) - { - // at the end of wait time if there are no jobs - // then run garbage collection. This need not be - // perfectly thread safe hence these lockless - // checks - to minimize overhead - RunGarbageCollection(false); - } - - // cleanup - waitTimer.Elapsed -= WaitTimerElapsed; - waitTimer.Dispose(); - } - - /// - /// PerformWSManPluginReportCompletion - /// - [DllImport("pwrshplugin.dll", EntryPoint = "PerformWSManPluginReportCompletion", SetLastError = false)] - private static extern void PerformWSManPluginReportCompletion(); - - private void ShutdownWaitTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) - { - Timer waitTimer = sender as Timer; - Debug.Assert(waitTimer != null, "Sender was not send correctly"); - Debug.Assert(waitTimer.Equals(_shutdownTimer), "Sender was not send correctly"); - - bool callWSManPluginReportCompletion = false; - - lock(_syncObject) - { - if (_activeSessionsCount == 0 && _inProgressCount == 0 && _pendingQueue.Count == 0) - { - callWSManPluginReportCompletion = true; - } - } - - if(callWSManPluginReportCompletion) - { - Tracer.WriteMessage("Calling WSManPluginReportCompletion as there are no active sessions and no inprogress/pending jobs"); - // Endpoint will not be shutdown if there is any active session - PerformWSManPluginReportCompletion(); - } - } - - /// - /// Handles the wsman server shutting down event. - /// - /// sender of this event - /// arguments describing the event - private void OnWSManServerActiveSessionsChangedEvent(object sender, ActiveSessionsChangedEventArgs eventArgs) - { - if (eventArgs != null) - { - lock (_syncObject) - { - _activeSessionsCount = eventArgs.ActiveSessionsCount; - - if (_shutdownTimer != null) - { - // Start the shutdown timer when there is no inprogress job, no pending job requests and no active sessions - // Otherwise disable it - if (_activeSessionsCount == 0 && _inProgressCount == 0 && _pendingQueue.Count == 0) - { - _shutdownTimer.Enabled = true; - } - else - { - _shutdownTimer.Enabled = false; - } - } - } - } - } - - /// - /// Contains the logic for running garbage collection - /// - /// if true, time check will not be - /// done and a forced garbage collection will take place - /// This method ensures that garbage collection - /// runs from only one thread at a time - [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] - private static void RunGarbageCollection(bool force) - { - if (Interlocked.CompareExchange(ref _gcStatus, InProgress, NotInProgress) != NotInProgress) - return; - - // if GC is run based on time elapsed, then we want atleast a 5 minute gap between 2 GC runs - if (force || DateTime.Compare(DateTime.Now, _lastGcTime.AddMinutes(GcDelayInMinutes)) >= 0) - { - _lastGcTime = DateTime.Now; - Interlocked.Exchange(ref _workflowsBeforeGc, 0); - - etwTracer.BeginRunGarbageCollection(); - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - etwTracer.EndRunGarbageCollection(); - } - - Interlocked.CompareExchange(ref _gcStatus, NotInProgress, InProgress); - } - - private void StartOperationsFromQueue() - { - do - { - Tuple, object, JobState> tuple; - while (_inProgressCount < _throttleLimit && _pendingQueue.TryDequeue(out tuple)) - { - Action operationDelegate = tuple.Item1; - - // decrement the current queue length perf counter - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.WaitingWorkflowJobsCount, - -1); - - - var wfJob = tuple.Item1.Target as PSWorkflowJob; - Debug.Assert(wfJob != null, "Target cannot be null"); - - // Increment in progress job count only when OnJobStateChanged Event handler is registered. - // Event handler will be added if job's current is state is either expected state or running. - // OnJobStateChanged handler decrements the in progress job count when job is Suspended, Completed, Failed or Stopped. - // - if (wfJob.CheckAndAddStateChangedEventHandler(OnJobStateChanged, tuple.Item3)) - Interlocked.Increment(ref _inProgressCount); - - // increment the current running workflow jobs counter - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.RunningWorkflowJobsCount); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.RunningWorkflowJobsPerSec); - operationDelegate(tuple.Item2); - operationDelegate = null; - tuple = null; - } - - // there are no more requests to service however there - // can be running jobs. When everything is complete and - // _inProgressCount is 0 and there no further requests, - // this method will still be called. At that point fire - // the wait timer - if (_inProgressCount == 0 && _pendingQueue.Count == 0) - { - Timer waitTimer = new Timer(); - - waitTimer.Elapsed += WaitTimerElapsed; - waitTimer.Interval = WaitInterval; - waitTimer.AutoReset = false; - waitTimer.Enabled = true; - } - - try - { - // we have reached this point either because there are - // no pending jobs or because we have hit the number of - // jobs that can be currently run. This thread will now - // sleep until thawed by an appropriate action - _waitForJobs.Value.WaitOne(); - } - catch (ObjectDisposedException) - { - // ObjectDisposedException will be thrown when _waitForJobs.Value is disposed. - } - } while (!_isDisposed && !_isDisposing); - } - - private void OnJobStateChanged(object sender, JobStateEventArgs eventArgs) - { - switch (eventArgs.JobStateInfo.State) - { - case JobState.Suspended: - case JobState.Stopped: - case JobState.Failed: - case JobState.Completed: - { - Job2 job = sender as Job2; - Debug.Assert(job != null, "Job instance need to passed when StateChanged event is raised"); - job.StateChanged -= OnJobStateChanged; - - // start more operations from queue if required - Interlocked.Decrement(ref _inProgressCount); - // decrement the workflow jobs being serviced perf counter - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.RunningWorkflowJobsCount, - -1); - - if (Interlocked.Increment(ref _workflowsBeforeGc) >= WorkflowLimitBeforeGc) - { - RunGarbageCollection(true); - } - - CheckAndStartServicingThread(); - } - break; - } - } - - /// - /// SubmitOperation - /// - /// - /// - /// - /// - internal void SubmitOperation(Job2 job, Action operationHandler, object state, JobState expectedState) - { - Debug.Assert(job != null, "Null job passed"); - Debug.Assert(operationHandler != null, "Null delegate passed"); - - if (job == null) throw new ArgumentNullException("job"); - - // Adding OnJobStateChanged event handler here caused negative _inProgressCount when jobs are stopped before they got picked up by servicing thread. - _pendingQueue.Enqueue(Tuple.Create(operationHandler, state, expectedState)); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.WaitingWorkflowJobsCount); - CheckAndStartServicingThread(); - } - - /// - /// Create a workflow job by providing the activity-tree representing the workflow. - /// - /// - /// - /// - /// - /// - /// - public PSWorkflowJob CreateJob(Guid jobInstanceId, Activity workflow, string command, string name, Dictionary parameters) - { - return CreateJob(jobInstanceId, workflow, command, name, parameters, null); - } - - /// - /// Create a workflow job by providing the xaml representing the workflow. - /// - /// - /// - /// - /// - /// - /// - public PSWorkflowJob CreateJob(Guid jobInstanceId, string workflowXaml, string command, string name, Dictionary parameters) - { - return CreateJob(jobInstanceId, workflowXaml, command, name, parameters, null); - } - - /// - /// Create a workflow job by providing the activity-tree representing the workflow. - /// - /// - /// - /// - /// - /// - /// - /// - public PSWorkflowJob CreateJob(Guid jobInstanceId, Activity workflow, string command, string name, Dictionary parameters, Dictionary creationContext) - { - return CreateJobInternal(jobInstanceId, workflow, command, name, parameters, null, creationContext); - } - - /// - /// Create a workflow job by providing the xaml representing the workflow. - /// - /// - /// - /// - /// - /// - /// - /// - public PSWorkflowJob CreateJob(Guid jobInstanceId, string workflowXaml, string command, string name, Dictionary parameters, Dictionary creationContext) - { - Activity workflow = Microsoft.PowerShell.Commands.ImportWorkflowCommand.ConvertXamlToActivity(workflowXaml); - return CreateJobInternal(jobInstanceId, workflow, command, name, parameters, workflowXaml, creationContext); - } - - internal PSWorkflowJob CreateJobInternal(Guid jobInstanceId, Activity workflow, string command, string name, Dictionary parameters, string xaml, Dictionary creationContext) - { - AssertNotDisposed(); - - if (jobInstanceId == Guid.Empty) - throw new ArgumentNullException("jobInstanceId"); - - if (workflow == null) - throw new ArgumentNullException("workflow"); - - if (command == null) - throw new ArgumentNullException("command"); - - if (name == null) - throw new ArgumentNullException("name"); - - if (_wfJobTable.ContainsKey(jobInstanceId)) - { - ArgumentException exception = new ArgumentException(Resources.DuplicateInstanceId); - Tracer.TraceException(exception); - throw exception; - } - - lock (lockObjects.GetLockObject(jobInstanceId)) - { - - if (_wfJobTable.ContainsKey(jobInstanceId)) - { - ArgumentException exception = new ArgumentException(Resources.DuplicateInstanceId); - Tracer.TraceException(exception); - throw exception; - } - - JobDefinition definition = new JobDefinition(typeof(WorkflowJobSourceAdapter), command, name); - - var parameterDictionary = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - - if (parameters != null) - { - foreach (KeyValuePair param in parameters) - { - parameterDictionary.Add(param.Key, param.Value); - } - } - - string[] computerNames = null; - bool gotComputerNames = false; - object value; - if (parameterDictionary.Count != 0 && parameterDictionary.TryGetValue(Constants.ComputerName, out value)) - { - if (LanguagePrimitives.TryConvertTo(value, CultureInfo.InvariantCulture, out computerNames)) - gotComputerNames = computerNames != null; - } - - if (gotComputerNames) - { - if (computerNames.Length > 1) - throw new ArgumentException(Resources.OneComputerNameAllowed); - - parameterDictionary.Remove(Constants.ComputerName); - } - - var childSpecification = new JobInvocationInfo(definition, parameterDictionary); - childSpecification.Command = command; - - // If actual computernames were specified, then set the PSComputerName parameter. - if (gotComputerNames) - { - var computerNameParameter = new CommandParameter(Constants.ComputerName, computerNames); - childSpecification.Parameters[0].Add(computerNameParameter); - } - - // Job objects will be disposed of on parent job removal. - var childJob = new PSWorkflowJob(_runtime, childSpecification, jobInstanceId, creationContext); - childJob.JobMetadata = CreateJobMetadataWithNoParentDefined(childJob, computerNames); - - childJob.LoadWorkflow(childSpecification.Parameters[0], workflow, xaml); - this.AddJob(childJob); - - return childJob; - } - } - - private static Dictionary CreateJobMetadataWithNoParentDefined(Job job, string[] parentComputers) - { - var jobMetadata = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - {Constants.JobMetadataInstanceId, job.InstanceId}, - {Constants.JobMetadataSessionId, job.Id}, - {Constants.JobMetadataName, job.Name}, - {Constants.JobMetadataCommand, job.Command}, - {Constants.JobMetadataStateReason, job.JobStateInfo.Reason}, - {Constants.JobMetadataStatusMessage, job.StatusMessage}, - {Constants.JobMetadataUserName, Environment.UserName}, - {Constants.JobMetadataPid, CurrentProcessId}, - // This is needed because the jobs created by runtime APIs are not visible from the PS Console - // although they were using default store, because they don't have the parent metadata. - // Afer assigning the parent metadata these jobs will be visible from console if APIs are using - // default store. - {Constants.JobMetadataParentInstanceId, Guid.NewGuid()}, - {Constants.JobMetadataParentSessionId, 0}, // no need to worry about '0' because it will be re assigned when loaded into the session. - {Constants.JobMetadataParentName, job.Name}, - {Constants.JobMetadataParentCommand, job.Command} - }; - - return jobMetadata; - } - - - /// - /// CreateJob - /// - /// - /// - /// - internal ContainerParentJob CreateJob(JobInvocationInfo jobInvocationInfo, Activity activity) - { - if (jobInvocationInfo == null) - throw new ArgumentNullException("jobInvocationInfo"); - - if (jobInvocationInfo.Definition == null) - throw new ArgumentException(Resources.NewJobDefinitionNull, "jobInvocationInfo"); - - if (jobInvocationInfo.Command == null) - throw new ArgumentException(Resources.NewJobDefinitionNull, "jobInvocationInfo"); - - DynamicActivity dynamicActivity = activity as DynamicActivity; - - Debug.Assert(dynamicActivity != null, "Passed workflow must be a DynamicActivity"); - - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Creating Workflow job with definition: {0}", jobInvocationInfo.Definition.InstanceId)); - - // Create parent job. All PSWorkflowJob objects will be a child of some ContainerParentJob - // This job will be disposed of when RemoveJob is called. - ContainerParentJob newJob = new ContainerParentJob( - jobInvocationInfo.Command, - jobInvocationInfo.Name, - WorkflowJobSourceAdapter.AdapterTypeName); - - foreach (CommandParameterCollection commandParameterCollection in jobInvocationInfo.Parameters) - { - var parameterDictionary = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - foreach (CommandParameter param in commandParameterCollection) - { - parameterDictionary.Add(param.Name, param.Value); - } - - string[] computerNames = null; - bool gotComputerNames = false; - object value; - if (parameterDictionary.Count != 0 && parameterDictionary.TryGetValue(Constants.ComputerName, out value)) - { - if (LanguagePrimitives.TryConvertTo(value, CultureInfo.InvariantCulture, out computerNames)) - gotComputerNames = computerNames != null; - } - - StructuredTracer.ParentJobCreated(newJob.InstanceId); - - bool isComputerNameExists = false; - - if (dynamicActivity != null && dynamicActivity.Properties.Any(x => x.Name.Equals(Constants.ComputerName, StringComparison.CurrentCultureIgnoreCase))) - { - isComputerNameExists = true; - } - - dynamicActivity = null; - - if (isComputerNameExists) - { - var childSpecification = new JobInvocationInfo(jobInvocationInfo.Definition, parameterDictionary); - childSpecification.Command = newJob.Command; - - // If actual computernames were specified, then set the PSComputerName parameter. - if (gotComputerNames) - { - var computerNameParameter = new CommandParameter(Constants.ComputerName, computerNames); - childSpecification.Parameters[0].Add(computerNameParameter); - } - - // Job objects will be disposed of on parent job removal. - var childJob = new PSWorkflowJob(_runtime, childSpecification); - childJob.JobMetadata = CreateJobMetadata(childJob, newJob.InstanceId, newJob.Id, newJob.Name, newJob.Command, computerNames); - - childJob.LoadWorkflow(commandParameterCollection, activity, null); - this.AddJob(childJob); - newJob.AddChildJob(childJob); - StructuredTracer.ChildWorkflowJobAddition(childJob.InstanceId, newJob.InstanceId); - StructuredTracer.WorkflowJobCreated(newJob.InstanceId, childJob.InstanceId, childJob.WorkflowGuid); - } - else - { - // Remove array of computerNames from collection. - parameterDictionary.Remove(Constants.ComputerName); - - if (gotComputerNames) - { - foreach (var computerName in computerNames) - { - CreateChildJob(jobInvocationInfo, activity, newJob, commandParameterCollection, parameterDictionary, computerName, computerNames); - } - } - else - { - CreateChildJob(jobInvocationInfo, activity, newJob, commandParameterCollection, parameterDictionary, null, computerNames); - } - } - } - - StructuredTracer.JobCreationComplete(newJob.InstanceId, jobInvocationInfo.InstanceId); - Tracer.TraceJob(newJob); - - return newJob; - } - - private void CreateChildJob(JobInvocationInfo specification, Activity activity, ContainerParentJob newJob, CommandParameterCollection commandParameterCollection, Dictionary parameterDictionary, string computerName, string[] computerNames) - { - if (!string.IsNullOrEmpty(computerName)) - { - string[] childTargetComputerList = { computerName }; - - // Set the target computer for this child job... - parameterDictionary[Constants.ComputerName] = childTargetComputerList; - } - - var childSpecification = new JobInvocationInfo(specification.Definition, parameterDictionary); - - // Job objects will be disposed of on parent job removal. - var childJob = new PSWorkflowJob(_runtime, childSpecification); - childJob.JobMetadata = CreateJobMetadata(childJob, newJob.InstanceId, newJob.Id, newJob.Name, newJob.Command, computerNames); - - // Remove the parameter from the collection... - for (int index = 0; index < commandParameterCollection.Count; index++) - { - if (string.Equals(commandParameterCollection[index].Name, Constants.ComputerName, StringComparison.OrdinalIgnoreCase)) - { - commandParameterCollection.RemoveAt(index); - break; - } - } - - if (!string.IsNullOrEmpty(computerName)) - { - var computerNameParameter = new CommandParameter(Constants.ComputerName, computerName); - commandParameterCollection.Add(computerNameParameter); - } - - this.AddJob(childJob); - childJob.LoadWorkflow(commandParameterCollection, activity, null); - newJob.AddChildJob(childJob); - StructuredTracer.ChildWorkflowJobAddition(childJob.InstanceId, newJob.InstanceId); - Tracer.TraceJob(childJob); - StructuredTracer.WorkflowJobCreated(newJob.InstanceId, childJob.InstanceId, childJob.WorkflowGuid); - } - - private static readonly int CurrentProcessId = Process.GetCurrentProcess().Id; - private static Dictionary CreateJobMetadata(Job job, Guid parentInstanceId, int parentSessionId, - string parentName, string parentCommand, string[] parentComputers) - { - var jobMetadata = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - {Constants.JobMetadataInstanceId, job.InstanceId}, - {Constants.JobMetadataSessionId, job.Id}, - {Constants.JobMetadataName, job.Name}, - {Constants.JobMetadataCommand, job.Command}, - {Constants.JobMetadataStateReason, job.JobStateInfo.Reason}, - {Constants.JobMetadataStatusMessage, job.StatusMessage}, - {Constants.JobMetadataParentInstanceId, parentInstanceId}, - {Constants.JobMetadataParentSessionId, parentSessionId}, - {Constants.JobMetadataParentName, parentName}, - {Constants.JobMetadataParentCommand, parentCommand}, - {Constants.JobMetadataUserName, Environment.UserName}, - {Constants.JobMetadataPid, CurrentProcessId} - }; - - return jobMetadata; - } - - /// - /// ShutdownWorkflowManager is responsible suspending all the workflow and if suspend doesn't happen within timeout then calling the force suspend. - /// - /// The shutdown timeout in milliseconds. - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public void ShutdownWorkflowManager(int timeout = 500) - { - // Return if this is already disposed. - if (_isDisposed) - return; - - if (timeout <= 0) - throw new ArgumentException(Resources.ForceSuspendTimeout); - - - List forceHandles = new List(); - - foreach (var job in GetJobs()) - { - // non thread safe check to avoid straight forward exclusions - if (job.JobStateInfo.State == JobState.Running || job.JobStateInfo.State == JobState.Suspending) - { - try - { - if (job.DoAbortJob(Resources.ShutdownAbort)) // this will do the thread safe verification - forceHandles.Add(job.SuspendedOrAborted); - else - forceHandles.Add(job.Finished); - } - catch (Exception exception) - { - // To continue in aborting the remaining jobs, it is safe to discard all the exceptions during shutdown. - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Shutting down workflow manager: suspend forcefully : Exception details: {0}", - exception)); - - } - } - } - - if (forceHandles.Count > 0) - { - WaitHandle.WaitAll(forceHandles.ToArray(), timeout); - forceHandles.Clear(); - } - - - // cleanup - foreach (var job in GetJobs()) - { - UnloadJob(job.InstanceId); - } - - _wfJobTable.Clear(); - } - - #endregion WorkflowManager Management - - #region Searching Workflows - - /// - /// Returns the workflow job currently loaded in memory with provided id. - /// This function DOES NOT try load the job from the store if not found in memory - /// - /// - /// - public PSWorkflowJob GetJob(Guid instanceId) - { - return Get(instanceId, null); - } - - /// - /// Loads and returns all workflow jobs. - /// - /// Returns the collection workflow jobs. - public IEnumerable GetJobs() - { - AssertNotDisposed(); - Tracer.WriteMessage(Facility + "Getting all the workflow instances."); - return _wfJobTable.Values; - } - - /// - /// Loads and returns all workflow jobs based on provided filters. - /// - /// Represent which type of data needs to be used to apply filter. - /// Filters represents the key value pair of conditions. - /// Returns the collection of workflow instances. - internal IEnumerable GetJobs(WorkflowFilterTypes type, Dictionary filters) - { - Tracer.WriteMessage(Facility + "Getting workflow instances based on filters"); - return GetJobs(_wfJobTable.Values, type, filters); - } - - /// - /// Loads and returns all workflow jobs based on provided filters. - /// - /// PSWorkflowJob search list - /// Represent which type of data needs to be used to apply filter. - /// Filters represents the key value pair of conditions. - /// Returns the collection of workflow instances. - internal IEnumerable GetJobs(ICollection searchList, WorkflowFilterTypes type, IDictionary filters) - { - List selectedJobs = new List(); - var filters2 = new Dictionary(filters, StringComparer.CurrentCultureIgnoreCase); - - // do a quick search on the basic v2 parameters - List narrowedList = WorkflowJobSourceAdapter.SearchJobsOnV2Parameters(searchList, filters2); - - // we have already searched based on Id, InstanceId, Name and Command - // these can now be removed from the list of items to search - string[] toRemove = { - Constants.JobMetadataSessionId, - Constants.JobMetadataInstanceId, - Constants.JobMetadataName, - Constants.JobMetadataCommand - }; - - foreach (var key in toRemove.Where(filters2.ContainsKey)) - { - filters2.Remove(key); - } - - if (filters2.Count == 0) - return narrowedList; - - foreach (var job in narrowedList) - { - // we intend to find jobs that match ALL criteria in filters - // NOT ANY criteria - bool computerNameMatch = true; - bool credentialMatch = true; - object value; - bool filterMatch = true; - - foreach (KeyValuePair filter in filters2) - { - string key = filter.Key; - if (!SearchAllFilterTypes((PSWorkflowJob)job, type, key, out value)) - { - computerNameMatch = credentialMatch = filterMatch = false; - break; - } - - // if guid is passed as a string try to convert and use the same - if (value is Guid) - { - Guid filterValueAsGuid; - LanguagePrimitives.TryConvertTo(filter.Value, CultureInfo.InvariantCulture, - out filterValueAsGuid); - if (filterValueAsGuid == (Guid)value) - continue; - filterMatch = false; - break; - } - - // PSComputerName needs to be special cased because it is actually an array. - if (key.Equals(Constants.ComputerName, StringComparison.OrdinalIgnoreCase)) - { - string[] instanceComputers = value as string[]; - if (instanceComputers == null) - { - string instanceComputer = value as string; - if (instanceComputer == null) break; - - instanceComputers = new string[] { instanceComputer }; - } - - object[] filterComputers = filter.Value as object[]; - if (filterComputers == null) - { - string computer = filter.Value as string; - if (computer == null) break; - - filterComputers = new object[] { computer }; - } - - foreach(var instanceComputer in instanceComputers) - { - computerNameMatch = false; - // for every computer we want atleast one filter - // match - foreach(var filterComputer in filterComputers) - { - string stringFilter = filterComputer as string; - if (stringFilter == null) break; - WildcardPattern filterPattern = new WildcardPattern(stringFilter, WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - - if (!filterPattern.IsMatch(instanceComputer)) continue; - computerNameMatch = true; - break; - } - if (!computerNameMatch) break; - } - if (!computerNameMatch) break; - continue; - } - - if ((key.Equals(Constants.Credential, StringComparison.OrdinalIgnoreCase))) - { - credentialMatch = false; - // PSCredential is a special case because it may be stored as a PSObject. - object credentialObject = filter.Value; - PSCredential credential = credentialObject as PSCredential; - - if (credential == null) - { - PSObject credPsObject = credentialObject as PSObject; - if (credPsObject == null) break; - - credential = credPsObject.BaseObject as PSCredential; - if (credential == null) break; - } - - Debug.Assert(credential != null); - credentialMatch = WorkflowUtils.CompareCredential(credential, value as PSCredential); - continue; - } - - if ((filter.Value is string || filter.Value is WildcardPattern) && value is string) - { - // at this point we are guaranteed that the key exists somewhere - WildcardPattern pattern; - string stringValue = filter.Value as string; - if (stringValue != null) - pattern = new WildcardPattern(stringValue, - WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - else pattern = (WildcardPattern)filter.Value; - - // if the pattern is a match, matched is still true, and don't check - // to see if the selected dictionary contains the actual key value pair. - if (!pattern.IsMatch((string)value)) - { - // if the pattern is not a match, this doesn't match the given filter. - filterMatch = false; - break; - } - continue; - } - - if (value != null && value.Equals(filter.Value)) continue; - filterMatch= false; - break; - } // end of foreach - filters - - bool matched = computerNameMatch && credentialMatch && filterMatch; - - if (matched) - { - selectedJobs.Add(job); - } - } //end of outer foreach - - return selectedJobs; - } - - private static bool SearchAllFilterTypes(PSWorkflowJob job, WorkflowFilterTypes type, string key, out object value) - { - PSWorkflowContext metadata = job.PSWorkflowInstance.PSWorkflowContext; - value = null; - if (metadata == null) - return false; - var searchTable = new Dictionary> - { - {WorkflowFilterTypes.WorkflowSpecificParameters, metadata.WorkflowParameters}, - {WorkflowFilterTypes.JobMetadata, metadata.JobMetadata}, - {WorkflowFilterTypes.CommonParameters, metadata.PSWorkflowCommonParameters}, - {WorkflowFilterTypes.PrivateMetadata, metadata.PrivateMetadata} - }; - foreach(var filter in searchTable.Keys) - { - object searchResult; - if (!type.HasFlag(filter) || !SearchOneFilterType(searchTable[filter], key, out searchResult)) continue; - value = searchResult; - return true; - } - - return false; - - } - private static bool SearchOneFilterType(IDictionary tableToSearch, string key, out object value) - { - value = null; - if (tableToSearch == null) - return false; - - if (tableToSearch.ContainsKey(key)) - { - tableToSearch.TryGetValue(key, out value); - return true; - } - - return false; - } - - #endregion Searching Workflows - - #region WorkflowInstanceTable - - /// - /// The dictionary of workflow instances. - /// - private readonly ConcurrentDictionary _wfJobTable = - new ConcurrentDictionary(); - - /// - /// Add a job to the manager. - /// - /// - private void AddJob(PSWorkflowJob job) - { - Debug.Assert(job != null, "Null job passed"); - if (_wfJobTable.ContainsKey(job.InstanceId)) - { - ArgumentException exception = new ArgumentException(Resources.DuplicateInstanceId); - Tracer.TraceException(exception); - throw exception; - } - - _wfJobTable.TryAdd(job.InstanceId, job); - } - - /// - /// Retrieves job from the manager. - /// - /// The job instance Id. - /// The workflow instance Id. - /// Returns the job. - private PSWorkflowJob Get(Guid? jobInstanceId, Guid? workflowInstanceId) - { - AssertNotDisposed(); - - PSWorkflowJob jobInstance = null; - - if (jobInstanceId.HasValue) - { - _wfJobTable.TryGetValue(jobInstanceId.Value, out jobInstance); - } - - if (workflowInstanceId.HasValue) - { - jobInstance = _wfJobTable.Values.SingleOrDefault(job => job.PSWorkflowInstance.Id == workflowInstanceId.Value); - } - - if (jobInstance == null) - { - return null; - } - - return jobInstance; - } - - internal void CleanUpWorkflowJobTable() - { - List wfJobs = new List(_wfJobTable.Values); - foreach (var job in wfJobs) - { - if (job.PSWorkflowInstance.PSWorkflowContext.JobMetadata != null && - job.PSWorkflowInstance.PSWorkflowContext.JobMetadata.Count != 0) continue; - // If the job metadata is not found, this instance is not recoverable. Don't bother checking it - // and remove it from the table. - PSWorkflowJob removedJob; - _wfJobTable.TryRemove(job.InstanceId, out removedJob); - if (removedJob != null) - removedJob.Dispose(); - } - } - - internal void ClearWorkflowManagerInstanceTable() - { - foreach(var job in _wfJobTable.Values) - { - job.Dispose(); - } - _wfJobTable.Clear(); - } - - internal void RemoveChildJob(Job2 childWorkflowJob) - { - if (WorkflowJobSourceAdapter.GetInstance().GetJobManager() == this) - { - WorkflowJobSourceAdapter.GetInstance().RemoveChildJob(childWorkflowJob); - } - else - { - this.RemoveJob(childWorkflowJob.InstanceId); - } - } - - - /// - /// Remove the workflow with provided job instance id. - /// - /// The job instance id. - public void RemoveJob(Guid instanceId) - { - AssertNotDisposed(); - - Tracer.WriteMessage(Facility + "Removing job instance with id: " + instanceId); - PSWorkflowJob job; - _wfJobTable.TryGetValue(instanceId, out job); - if (job == null) return; - - lock (lockObjects.GetLockObject(instanceId)) - { - _wfJobTable.TryGetValue(instanceId, out job); - if (job == null) return; - - try - { - if (!job.IsFinishedState(job.JobStateInfo.State)) - { - try - { - job.StopJob(true, "Remove"); - } - catch (ObjectDisposedException) - { - Tracer.WriteMessage(Facility, "RemoveJob", job.PSWorkflowInstance.Id, "Workflow Job is already disposed. so removing it."); - } - } - } - finally - { - job.PSWorkflowInstance.RemoveInstance(); - job.Dispose(); - _wfJobTable.TryRemove(instanceId, out job); - - lockObjects.RemoveLockObject(instanceId); - } - } - - StructuredTracer.WorkflowDeletedFromDisk(instanceId, string.Empty); - StructuredTracer.WorkflowCleanupPerformed(instanceId); - } - - /// - /// Unload/Forget the workflow with provided job instance id. - /// This method is used to dispose unloaded idle workflows. - /// - /// The job instance id. - public void UnloadJob(Guid instanceId) - { - AssertNotDisposed(); - - lock (lockObjects.GetLockObject(instanceId)) - { - Tracer.WriteMessage(Facility + "Forgetting job instance with id: " + instanceId); - - PSWorkflowJob job = GetJob(instanceId); - - if (job == null) - return; - - job.Dispose(); // igorse: why do we call Dispose before removing from _wfJobTable? - - _wfJobTable.TryRemove(job.InstanceId, out job); - } - } - - private void DoUnloadJob(object state) - { - Collection col = state as Collection; - Debug.Assert(col != null, "The state object should be of type collection"); - - Debug.Assert(col[0].GetType() == typeof(Guid), "The first element in the collection should be of type Guid"); - Debug.Assert(col[1].GetType() == typeof(ManualResetEvent), "The second element in the collection should be of type ManualResetEvent"); - - Guid instanceId = (Guid)col[0]; - ManualResetEvent handle = (ManualResetEvent)col[1]; - - UnloadJob(instanceId); - handle.Set(); - } - - /// - /// UnloadAllJobs - /// - public void UnloadAllJobs() - { - AssertNotDisposed(); - - List waitHandles = new List(); - - foreach (KeyValuePair job in _wfJobTable) - { - ManualResetEvent handle = new ManualResetEvent(false); - ThreadPool.QueueUserWorkItem(DoUnloadJob, new Collection { job.Key, handle }); - waitHandles.Add(handle); - } - - if (waitHandles.Count > 0) - { - if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) - { - foreach (WaitHandle handle in waitHandles) - handle.WaitOne(); - } - else - { - WaitHandle.WaitAll(waitHandles.ToArray()); - } - - // now disposing all wait handles - foreach (WaitHandle handle in waitHandles) - handle.Dispose(); - } - } - - #endregion WorkflowInstanceTable - - } - - /// - /// Provides list of workflow filter types. - /// - [Flags] - internal enum WorkflowFilterTypes - { - /// - /// Empty flags - not used, would indicate to search no collections. - /// - None = 0, - - /// - /// Filters will be applicable to job metadata collection. - /// - JobMetadata = 1, - - /// - /// Filters will be applicable to caller private metadata collection. - /// - PrivateMetadata = 2, - - /// - /// Filters will be applicable to workflow specific parameters defined by the workflow author. - /// - WorkflowSpecificParameters = 4, - - /// - /// Filters will be applicable to common parameters on all workflows. - /// - CommonParameters = 8, - - /// - /// Use all filters. - /// - All = JobMetadata | PrivateMetadata | WorkflowSpecificParameters | CommonParameters - } - - internal class LockObjectsCollection - { - private object syncLock = new object(); - private Dictionary lockObjects = new Dictionary(); - - internal object GetLockObject(Guid id) - { - lock (syncLock) - { - if(lockObjects.ContainsKey(id) == false) - { - lockObjects.Add(id, new object()); - } - - return lockObjects[id]; - } - } - - internal void RemoveLockObject(Guid id) - { - if (lockObjects.ContainsKey(id) == false) - return; - - lock (syncLock) - { - if (lockObjects.ContainsKey(id) == false) - return; - - lockObjects.Remove(id); - } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowMetadatas.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowMetadatas.cs deleted file mode 100644 index d23cdda830a..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowMetadatas.cs +++ /dev/null @@ -1,105 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System.Collections.Generic; - - /// - /// Define all the metadatas. - /// - public class PSWorkflowContext - { - /// - /// The parameters, which need to be passed to the workflow engine. - /// - private Dictionary _workflowParameters; - - /// - /// The ubiquitous parameters, which are also passed to the engine. - /// - private Dictionary _psWorkflowCommonParameters; - - /// - /// The metadata, which contains all the information related to job and client like, job-id, connection-id and application-id etc. - /// - private Dictionary jobMetadata; - - /// - /// The metadata, which is specific to the caller and doesn't contain any information related to workflow execution. - /// - private Dictionary privateMetadata; - - /// - /// Default Constructor. - /// - public PSWorkflowContext() - { - this._workflowParameters = null; - this._psWorkflowCommonParameters = null; - this.jobMetadata = null; - this.privateMetadata = null; - } - - /// - /// Workflow metadatas constructor. - /// - /// The parameters, which need to be passed to the workflow engine. - /// The ubiquitous parameters, which are also passed to the engine. - /// The metadata, which contains all the information related to job and client like, job-id, connection-id and application-id etc. - /// The metadata, which is specific to the caller and doesn't contain any information related to workflow execution. - public PSWorkflowContext( - Dictionary workflowParameters, - Dictionary workflowCommonParameters, - Dictionary jobMetadata, - Dictionary privateMetadata) - { - this._workflowParameters = workflowParameters; - this._psWorkflowCommonParameters = workflowCommonParameters; - this.jobMetadata = jobMetadata; - this.privateMetadata = privateMetadata; - } - - /// - /// Gets the input to workflow. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary WorkflowParameters - { - get { return this._workflowParameters; } - set { this._workflowParameters = value; } - } - - /// - /// Gets the input to workflow. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary PSWorkflowCommonParameters - { - get { return this._psWorkflowCommonParameters; } - set { this._psWorkflowCommonParameters = value; } - } - - /// - /// Gets the input to workflow. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary JobMetadata - { - get { return this.jobMetadata; } - set { this.jobMetadata = value; } - } - - /// - /// Gets the input to workflow. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary PrivateMetadata - { - get { return this.privateMetadata; } - set { this.privateMetadata = value; } - } - - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowPerformanceCounterSetInfo.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowPerformanceCounterSetInfo.cs deleted file mode 100644 index 883a22f6e78..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowPerformanceCounterSetInfo.cs +++ /dev/null @@ -1,107 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Diagnostics.PerformanceData; -using System.Management.Automation; -using System.Management.Automation.PerformanceData; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// PSWorkflowPerformanceCounterSetInfo class contains - /// the data essential to create an - /// instance of PSCounterSetRegistrar for monitoring - /// workflow performance - /// - internal static class PSWorkflowPerformanceCounterSetInfo - { - internal static Guid ProviderId = new Guid("{5db760bc-64b2-4da7-b4ef-7dab105fbb8c}"); - /// - /// If some other assemblies (e.g. Microsoft.PowerShell.Workflow.Activities) need - /// access to the counters, then they would need to specify the CounterSetId, - /// alongwith the counterId. Hence, CounterSetId is public. - /// - internal static Guid CounterSetId = new Guid("{faa17411-9025-4b86-8b5e-ce2f32b06e13}"); - internal static CounterSetInstanceType CounterSetType = CounterSetInstanceType.Multiple; - internal static CounterInfo[] CounterInfoArray = - new CounterInfo[]{ - new CounterInfo(PSWorkflowPerformanceCounterIds.FailedWorkflowJobsCount,CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.FailedWorkflowJobsPerSec,CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.RunningWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.RunningWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.SucceededWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.SucceededWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.SuspendedWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.SuspendedWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - //new CounterInfo(PSWorkflowPerformanceCounterIds.TerminatedExceptionWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.WaitingWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrBusyProcessesCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrFailedRequestsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrFailedRequestsQueueLength, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrIncomingRequestsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrPendingRequestsQueueLength, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrCreatedProcessesCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrDisposedProcessesCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrProcessesPoolSize, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingPendingRequestsQueueLength, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingForcedToWaitRequestsQueueLength, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingConnectionsCreatedCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingConnectionsDisposedCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingConnectionsClosedReopenedCount, CounterType.RawData64) - }; - } - - /// - /// PSWorkflowPerformanceCounterIds enumerates the - /// list of valid performance counter ids related to Powershell Workflow. - /// NOTE: The prime reason for making this not an enum are as follows: - /// (1) Everytime the enum will have to be typecasted to an int before invoking any - /// of the Performance Counters API. - /// (2) In case of M3P, its possible that some of the perf counters might be updated - /// by ActivityBase assembly, in which such perf counter ids might need to have - /// public access modifier, instead of internal. - /// - internal static class PSWorkflowPerformanceCounterIds - { - internal const int FailedWorkflowJobsCount = 1; - internal const int FailedWorkflowJobsPerSec = FailedWorkflowJobsCount + 1; - internal const int ResumedWorkflowJobsCount = FailedWorkflowJobsPerSec + 1; - internal const int ResumedWorkflowJobsPerSec = ResumedWorkflowJobsCount + 1; - internal const int RunningWorkflowJobsCount = ResumedWorkflowJobsPerSec + 1; - internal const int RunningWorkflowJobsPerSec = RunningWorkflowJobsCount + 1; - internal const int StoppedWorkflowJobsCount = RunningWorkflowJobsPerSec + 1; - internal const int StoppedWorkflowJobsPerSec = StoppedWorkflowJobsCount + 1; - internal const int SucceededWorkflowJobsCount = StoppedWorkflowJobsPerSec + 1; - internal const int SucceededWorkflowJobsPerSec = SucceededWorkflowJobsCount + 1; - internal const int SuspendedWorkflowJobsCount = SucceededWorkflowJobsPerSec + 1; - internal const int SuspendedWorkflowJobsPerSec = SuspendedWorkflowJobsCount + 1; - internal const int TerminatedWorkflowJobsCount = SuspendedWorkflowJobsPerSec + 1; - internal const int TerminatedWorkflowJobsPerSec = TerminatedWorkflowJobsCount + 1; - internal const int WaitingWorkflowJobsCount = TerminatedWorkflowJobsPerSec + 1; - internal const int ActivityHostMgrBusyProcessesCount = WaitingWorkflowJobsCount + 1; - internal const int ActivityHostMgrFailedRequestsPerSec = ActivityHostMgrBusyProcessesCount + 1; - internal const int ActivityHostMgrFailedRequestsQueueLength = ActivityHostMgrFailedRequestsPerSec + 1; - internal const int ActivityHostMgrIncomingRequestsPerSec = ActivityHostMgrFailedRequestsQueueLength + 1; - internal const int ActivityHostMgrPendingRequestsQueueLength = ActivityHostMgrIncomingRequestsPerSec + 1; - internal const int ActivityHostMgrCreatedProcessesCount = ActivityHostMgrPendingRequestsQueueLength + 1; - internal const int ActivityHostMgrDisposedProcessesCount = ActivityHostMgrCreatedProcessesCount + 1; - internal const int ActivityHostMgrProcessesPoolSize = ActivityHostMgrDisposedProcessesCount + 1; - internal const int PSRemotingPendingRequestsQueueLength = ActivityHostMgrProcessesPoolSize + 1; - internal const int PSRemotingRequestsBeingServicedCount = PSRemotingPendingRequestsQueueLength + 1; - internal const int PSRemotingForcedToWaitRequestsQueueLength = PSRemotingRequestsBeingServicedCount + 1; - internal const int PSRemotingConnectionsCreatedCount = PSRemotingForcedToWaitRequestsQueueLength + 1; - internal const int PSRemotingConnectionsDisposedCount = PSRemotingConnectionsCreatedCount + 1; - internal const int PSRemotingConnectionsClosedReopenedCount = PSRemotingConnectionsDisposedCount + 1; - }; - - -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowRuntimeCompilation.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowRuntimeCompilation.cs deleted file mode 100644 index 2ef5dce6d6d..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowRuntimeCompilation.cs +++ /dev/null @@ -1,265 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Globalization; - using System.Diagnostics.CodeAnalysis; - using System.Management.Automation.Tracing; - -#if _NOTARMBUILD_ - using Microsoft.Build.Evaluation; - using Microsoft.Build.Logging; -#endif - using System.Activities.XamlIntegration; - using System.IO; - using System.Xaml; - using System.Xml; - using System.Collections.Generic; - using System.Runtime.InteropServices; - - /// - /// This class is responsible for compiling dependent workflows. - /// - internal class WorkflowRuntimeCompilation - { - private static readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// The template project which is used for default project values. - /// - private const string Template_Project = @"ReleaseAnyCPU10.02.0LibraryPropertiesv4.5512truefullfalsebin\Debug\DEBUG;TRACEprompt4pdbonlytruebin\Release\TRACEprompt4"; - - /// - /// The runtime generated project name. - /// - internal string ProjectName { get; set; } - - /// - /// The runtime generated Project folder path. - /// - internal string ProjectFolderPath { get; set; } - - /// - /// The runtime generated Project file path (.csprj file). - /// - internal string ProjectFilePath { get; set; } - - /// - /// The runtime generated build log path. - /// - internal string BuildLogPath { get; set; } - - /// - /// The runtime generated assembly name. - /// - internal string AssemblyName { get; set; } - - /// - /// The runtime generated assembly path. - /// - internal string AssemblyPath { get; set; } - - /// - /// The returned code of project.Build. - /// - internal bool BuildReturnedCode { get; set; } - - private string _projectRoot; - /// - /// Default constructor. - /// - internal WorkflowRuntimeCompilation() - { - if (IsRunningOnProcessorArchitectureARM()) - { - Tracer.WriteMessage("The workflow Calling workflow is not supported so throwing the exception."); - throw new NotSupportedException(Resources.WFCallingWFNotSupported); - } - - this.ProjectName = "Workflow_" + Guid.NewGuid().ToString("N"); - this._projectRoot = Path.Combine(Path.GetTempPath(), @"PSWorkflowCompilation\"+this.ProjectName); - this.ProjectFolderPath = Path.Combine(this._projectRoot, "Project"); - this.ProjectFilePath = Path.Combine(this.ProjectFolderPath, "RuntimeProject.csproj"); - this.BuildLogPath = Path.Combine(this.ProjectFolderPath, "Build.Log"); - this.AssemblyName = this.ProjectName; - this.AssemblyPath = Path.Combine(this._projectRoot, this.ProjectName + ".dll"); - } - - /// - /// Compiling the workflow xamls into the assembly. - /// - internal void Compile(List dependentWorkflows, Dictionary requiredAssemblies) - { - if (IsRunningOnProcessorArchitectureARM()) - { - Tracer.WriteMessage("The workflow Calling workflow is not supported so throwing the exception."); - throw new NotSupportedException(Resources.WFCallingWFNotSupported); - } - -// Note that the _NOT_ARMBUILD_ flag is not a global build flag and needs to be set in the corresponding sources.inc file as appropriate. -#if _NOTARMBUILD_ - DirectoryInfo folder = new DirectoryInfo(this.ProjectFolderPath); - folder.Create(); - - List workflowFiles = new List(); - try - { - // Dump the files - foreach (string dependentWorkflow in dependentWorkflows) - { - string newFileName = Path.Combine(this.ProjectFolderPath, Path.GetRandomFileName() + ".xaml"); - File.WriteAllText(newFileName, dependentWorkflow); - workflowFiles.Add(newFileName); - } - - File.WriteAllText(this.ProjectFilePath, Template_Project); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - using (ProjectCollection projects = new ProjectCollection()) - { - Project project = projects.LoadProject(this.ProjectFilePath); - - project.SetProperty("AssemblyName", this.AssemblyName); - - HashSet Assemblies = new HashSet(); - - foreach (string file in workflowFiles) - { - project.AddItem("XamlAppDef", file); - - XamlXmlReader reader = new XamlXmlReader(XmlReader.Create(file), new XamlSchemaContext()); - using (reader) - { - while (reader.Read()) - { - if (reader.NodeType == XamlNodeType.NamespaceDeclaration) - { - string _namespace = reader.Namespace.Namespace.ToLowerInvariant(); - - if (_namespace.IndexOf("assembly=", StringComparison.OrdinalIgnoreCase) > -1) - { - List filters = new List(); - filters.Add("assembly="); - string[] results = _namespace.Split(filters.ToArray(), StringSplitOptions.RemoveEmptyEntries); - if (results.Length > 1 && !string.IsNullOrEmpty(results[1])) - { - string requiredAssemblyLocation; - if (requiredAssemblies != null && requiredAssemblies.Count > 0 && requiredAssemblies.TryGetValue(results[1], out requiredAssemblyLocation)) - { - Assemblies.Add(requiredAssemblyLocation); - } - else - { - Assemblies.Add(results[1]); - } - } - } - } - } - } - } - - foreach (string assembly in Assemblies) - { - project.AddItem("Reference", assembly); - } - - project.Save(this.ProjectFilePath); - - FileLogger logger = new FileLogger(); - logger.Parameters = "logfile=" + this.BuildLogPath; - - this.BuildReturnedCode = false; - - // According to MSDN, http://msdn.microsoft.com/en-us/library/microsoft.build.evaluation.projectcollection.aspx - // multiple project collections can exist within an app domain. However, these must not build concurrently. - // Therefore, we need a static lock to prevent multiple threads from accessing this call. - lock (_syncObject) - { - this.BuildReturnedCode = project.Build(logger); - } - - logger.Shutdown(); - - // If compilation succeeded, delete the project files. - // - if (this.BuildReturnedCode) - { - string generatedAssemblyPath = Path.Combine(this.ProjectFolderPath, @"obj\Release\" + this.ProjectName + ".dll"); - if (File.Exists(generatedAssemblyPath)) - { - File.Move(generatedAssemblyPath, this.AssemblyPath); - } - - try - { - System.IO.Directory.Delete(this.ProjectFolderPath, true); - } - catch (Exception e) - { - Tracer.TraceException(e); - // Ignoring the exceptions from Delete of temp directory. - } - } - } -#endif - } - - static object _syncObject = new object(); - - /// - /// Return true/false to indicate whether the processor architecture is ARM - /// - /// - internal static bool IsRunningOnProcessorArchitectureARM() - { - // Important: - // this function has a clone in SMA in admin\monad\src\utils\PsUtils.cs - // if you are making any changes specific to this function then update the clone as well. - - NativeMethods.SYSTEM_INFO sysInfo = new NativeMethods.SYSTEM_INFO(); - NativeMethods.GetSystemInfo(ref sysInfo); - return sysInfo.wProcessorArchitecture == NativeMethods.PROCESSOR_ARCHITECTURE_ARM; - } - - private static class NativeMethods - { - // Important: - // this clone has a clone in SMA in admin\monad\src\utils\PsUtils.cs - // if you are making any changes specific to this class then update the clone as well. - - internal const ushort PROCESSOR_ARCHITECTURE_INTEL = 0; - internal const ushort PROCESSOR_ARCHITECTURE_ARM = 5; - internal const ushort PROCESSOR_ARCHITECTURE_IA64 = 6; - internal const ushort PROCESSOR_ARCHITECTURE_AMD64 = 9; - internal const ushort PROCESSOR_ARCHITECTURE_UNKNOWN = 0xFFFF; - - [StructLayout(LayoutKind.Sequential)] - internal struct SYSTEM_INFO - { - public ushort wProcessorArchitecture; - public ushort wReserved; - public uint dwPageSize; - public IntPtr lpMinimumApplicationAddress; - public IntPtr lpMaximumApplicationAddress; - public UIntPtr dwActiveProcessorMask; - public uint dwNumberOfProcessors; - public uint dwProcessorType; - public uint dwAllocationGranularity; - public ushort wProcessorLevel; - public ushort wProcessorRevision; - }; - - [DllImport("kernel32.dll")] - internal static extern void GetSystemInfo(ref SYSTEM_INFO lpSystemInfo); - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowStatusEventArgs.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowStatusEventArgs.cs deleted file mode 100644 index 6b0cff1c081..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowStatusEventArgs.cs +++ /dev/null @@ -1,126 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Diagnostics.CodeAnalysis; - - /// - /// Possible states of workflow instance. - /// - internal enum WorkflowInstanceState - { - /// - /// Not initialized with any workflow yet. - /// - NotStarted = 0, - - /// - /// Workflow execution is loaded but not yet started. - /// - Loaded = 1, - - /// - /// Workflow is currently executing. - /// - Executing = 2, - - /// - /// Workflow execution is completed. - /// - CompletedAndClosed = 3, - - /// - /// Faulted state, once the workflow execution is completed. - /// - Faulted = 4, - - /// - /// Canceled state, once the workflow execution is completed. - /// - Canceled = 5, - - /// - /// Aborted state, once the workflow execution is completed. - /// - Aborted = 6, - - /// - /// Un-handled exception and termination state, once the workflow execution is completed. - /// - UnhandledExceptionAndTermination = 7, - - /// - /// Workflow is currently unloaded. - /// - Unloaded = 8, - - /// - /// Workflow is currently unknown. - /// - Unknown = 9, - }; - - /// - /// Provides an event definition for the status of a workflow. - /// - internal class WorkflowStatusEventArgs : EventArgs - { - /// - /// The instance id. - /// - private Guid id; - - /// - /// The workflow instance state. - /// - private WorkflowInstanceState state; - - /// - /// The workflow unhandled exception. - /// - private Exception unhandledException; - - /// - /// Constructor with instance id and state. - /// - /// The workflow Id. - /// The state of workflow - /// The unhandled exception, occurs when streams are closed. - internal WorkflowStatusEventArgs(Guid id, WorkflowInstanceState state, Exception unhandledException) - { - this.id = id; - this.state = state; - this.unhandledException = unhandledException; - } - - /// - /// Gets instance id. - /// - internal Guid Id - { - get { return this.id; } - } - - /// - /// Gets instance state. - /// - internal WorkflowInstanceState State - { - get { return this.state; } - } - - /// - /// Gets unhandled exception. - /// - internal Exception UnhandledException - { - get { return this.unhandledException; } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTimers.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTimers.cs deleted file mode 100644 index 5f42292944b..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTimers.cs +++ /dev/null @@ -1,484 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Diagnostics; - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Management.Automation; - using System.Activities; - using System.Timers; - using System.Management.Automation.Tracing; - using System.Collections; - using System.Runtime.Serialization; - - /// - /// Workflow timer types. - /// - internal enum WorkflowTimerType - { - RunningTimer = 1, - - ElapsedTimer = 2, - } - - internal delegate void WorkflowTimerElapsedHandler(PSTimer sender, ElapsedEventArgs e); - - internal sealed class PSTimer : IDisposable - { - private bool disposed; - - private Timer timer; - private readonly object syncLock = new object(); - - internal WorkflowTimerType TimerType { get; private set; } - private bool IsRecurring; - private bool IsOneTimeTimer; - private TimeSpan Interval; - - private TimeSpan RemainingTime; - private DateTime? StartedAtForFirstTime; - private DateTime? StartedTime; - private bool IsRunning; - internal bool TimerReachedAlready = false; - - private event WorkflowTimerElapsedHandler Handler; - - internal PSTimer(WorkflowTimerType type, bool isRecurring, bool isOneTimeTimer, TimeSpan interval, WorkflowTimerElapsedHandler handler) - { - Debug.Assert(!(isRecurring == true && isOneTimeTimer == true), "Timer cannot be recurring and one-time-timer at the same time."); - - this.TimerType = type; - this.IsRecurring = isRecurring; - this.IsOneTimeTimer = isOneTimeTimer; - - this.Interval = interval; - this.RemainingTime = interval; - this.StartedAtForFirstTime = null; - this.StartedTime = null; - this.IsRunning = false; - - this.Handler = handler; - } - - internal PSTimer(Dictionary data, WorkflowTimerElapsedHandler handler) - { - this.TimerType = (WorkflowTimerType)data["TimerType"]; - this.IsRecurring = (bool)data["IsRecurring"]; - this.IsOneTimeTimer = (bool)data["IsOneTimeTimer"]; - this.Interval = (TimeSpan)data["Interval"]; - - if (IsRecurring == false && IsOneTimeTimer == false) - { - this.RemainingTime = (TimeSpan)data["RemainingTime"]; - } - else if (IsRecurring == false && IsOneTimeTimer == true) - { - DateTime tmpStartedAtForFirstTime = (DateTime)data["StartedAtForFirstTime"]; - TimeSpan diff = Interval - DateTime.UtcNow.Subtract(tmpStartedAtForFirstTime); - if (diff <= TimeSpan.FromSeconds(0)) - { - this.TimerReachedAlready = true; - this.RemainingTime = TimeSpan.FromSeconds(2); - } - else if (diff < TimeSpan.FromSeconds(2)) - this.RemainingTime = TimeSpan.FromSeconds(2); - else - this.RemainingTime = diff; - } - else - { - this.RemainingTime = Interval; - } - - this.StartedAtForFirstTime = null; - this.StartedTime = null; - this.IsRunning = false; - - this.Handler = handler; - - } - - internal Dictionary GetSerializedData() - { - Dictionary data = new Dictionary(); - - data.Add("TimerType", TimerType); - data.Add("IsRecurring", IsRecurring); - data.Add("IsOneTimeTimer", IsOneTimeTimer); - data.Add("Interval", Interval); - - if (IsRecurring == false && IsOneTimeTimer == false) - { - if (IsRunning) - { - Debug.Assert(StartedTime.HasValue, "Started time should have value."); - TimeSpan tmpRemainingTime = RemainingTime - DateTime.UtcNow.Subtract((DateTime)StartedTime); - if (tmpRemainingTime < TimeSpan.FromMilliseconds(0)) - tmpRemainingTime = TimeSpan.FromMilliseconds(0); - - data.Add("RemainingTime", tmpRemainingTime); - } - else - { - data.Add("RemainingTime", RemainingTime); - } - } - else if (IsRecurring == false && IsOneTimeTimer == true) - { - // one time timer - if (StartedAtForFirstTime.HasValue) - data.Add("StartedAtForFirstTime", StartedAtForFirstTime); - else - data.Add("StartedAtForFirstTime", DateTime.UtcNow); - } - - return data; - } - - private void timer_Elapsed(object sender, ElapsedEventArgs e) - { - if (disposed) - return; - - if (!IsRunning) - return; - - lock (syncLock) - { - if (!IsRunning) - return; - - if (Handler != null) - Handler(this, e); - - if (IsRecurring == false) - IsRunning = false; - } - } - - internal void Start() - { - if (disposed) - return; - - if (IsRunning) - return; - - if (Interval <= TimeSpan.FromMilliseconds(0)) - return; - - lock (syncLock) - { - if (IsRunning) - return; - - if (timer == null) - { - timer = new System.Timers.Timer(this.RemainingTime.TotalMilliseconds); - timer.AutoReset = IsRecurring; - timer.Elapsed += new ElapsedEventHandler(timer_Elapsed); - StartedAtForFirstTime = DateTime.UtcNow; - } - else - { - timer.Interval = this.RemainingTime.TotalMilliseconds; - } - - timer.Start(); - IsRunning = true; - StartedTime = DateTime.UtcNow; - } - } - - internal void Stop() - { - if (disposed) - return; - - if (!IsRunning) - return; - - lock (syncLock) - { - if (!IsRunning) - return; - - if (timer == null) - return; - - timer.Stop(); - IsRunning = false; - - Debug.Assert(StartedTime.HasValue, "Started time should have value."); - if (this.IsRecurring == false) - { - RemainingTime -= DateTime.UtcNow.Subtract((DateTime)StartedTime); - if (RemainingTime < TimeSpan.FromMilliseconds(0)) - RemainingTime = TimeSpan.FromMilliseconds(0); - } - } - } - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - private void Dispose(bool disposing) - { - if (this.disposed || !disposing) - return; - - lock (syncLock) - { - if (this.disposed) - return; - - Stop(); - - if (timer != null) - { - timer.Elapsed -= this.timer_Elapsed; - this.Handler = null; - this.timer.Dispose(); - } - - this.disposed = true; - } - } - } - - /// - /// Define all the workflow related timers. - /// - public sealed class PSWorkflowTimer : IDisposable - { - private readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static readonly Tracer StructuredTracer = new Tracer(); - private readonly PSWorkflowInstance _instance; - - private bool disposed; - private readonly object syncLock = new object(); - - private Dictionary _timers; - - /// - /// Default Constructor - /// - internal PSWorkflowTimer(PSWorkflowInstance instance) - { - _instance = instance; - _timers = new Dictionary(); - } - - /// - /// Creates a workflow timer for a workflow instance based on a BLOB - /// - /// - /// - public PSWorkflowTimer(PSWorkflowInstance instance, object deserializedTimers) - { - _instance = instance; - _timers = new Dictionary(); - - if (deserializedTimers == null) throw new ArgumentNullException("deserializedTimers"); - - List deserializedTimerList = (List)deserializedTimers; - foreach (object data in deserializedTimerList) - { - Debug.Assert(data != null, "Timer data should not have been null."); - if (data != null) - { - Debug.Assert(data is Dictionary, "The timer data should be of type Dictionary."); - if (data is Dictionary) - { - PSTimer timer = new PSTimer((Dictionary)data, Timer_WorkflowTimerElapsed); - _timers.Add(timer.TimerType, timer); - } - } - } - } - - /// - /// Retrieves timers as a BLOB - /// - /// - public object GetSerializedData() - { - if (disposed) - return null; - - List serializedTimers = new List(); - foreach (PSTimer timer in _timers.Values) - { - serializedTimers.Add(timer.GetSerializedData()); - } - - return serializedTimers; - } - - internal void SetupTimer(WorkflowTimerType timerType, TimeSpan interval) - { - if (disposed) - return; - - if (_timers.ContainsKey(timerType)) - return; - - if (timerType == WorkflowTimerType.ElapsedTimer) - { - _timers.Add(timerType, new PSTimer(timerType, false, true, interval, Timer_WorkflowTimerElapsed)); - } - else if (timerType == WorkflowTimerType.RunningTimer) - { - _timers.Add(timerType, new PSTimer(timerType, false, false, interval, Timer_WorkflowTimerElapsed)); - } - } - - internal bool CheckIfTimerHasReachedAlready(WorkflowTimerType timerType) - { - if (disposed) - return false; - - if (_timers.ContainsKey(timerType) && _timers[WorkflowTimerType.ElapsedTimer].TimerReachedAlready) - return true; - - return false; - } - - internal void StartTimer(WorkflowTimerType timerType) - { - if (disposed) - return; - - if (_timers.ContainsKey(timerType)) - { - _timers[timerType].Start(); - } - } - - internal void StopTimer(WorkflowTimerType timerType) - { - if (disposed) - return; - - if (_timers.ContainsKey(timerType)) - { - _timers[timerType].Stop(); - } - } - - private void Timer_WorkflowTimerElapsed(PSTimer sender, ElapsedEventArgs e) - { - if (disposed) - return; - - StructuredTracer.Correlate(); - Tracer.WriteMessage("PSWorkflowTimer Elapsed: " + sender.TimerType); - - if (disposed) - return; - - switch (sender.TimerType) - { - case WorkflowTimerType.RunningTimer: - sender.Stop(); - TerminateWorkflow(Resources.RunningTimeReached); - break; - case WorkflowTimerType.ElapsedTimer: - sender.Stop(); - TerminateAndRemoveWorkflow(Resources.ElapsedTimeReached); - break; - } - } - - private readonly object syncElapsedLock = new object(); - private bool _elapsedTimerCalled = false; - // Terminate workflow - private void TerminateWorkflow(string reason) - { - if (disposed) return; - if (_elapsedTimerCalled) return; - - lock (syncElapsedLock) - { - - if (disposed) return; - if (_elapsedTimerCalled) return; - - try - { - Debug.Assert(_instance.PSWorkflowJob != null, "PSWorkflowJob should be set before calling terminate workflow"); - _instance.PSWorkflowJob.StopJob(true, reason); - } - catch (Exception e) - { - // logging the exception in background thread - Tracer.TraceException(e); - } - } - } - - // Terminate and Remove workflow - private void TerminateAndRemoveWorkflow(string reason) - { - _elapsedTimerCalled = true; - if (disposed) return; - - lock (syncElapsedLock) - { - if (disposed) return; - try - { - _instance.PSWorkflowJob.StopJob(true, Resources.ElapsedTimeReached); - } - catch (Exception e) - { - // logging the exception in background thread - Tracer.TraceException(e); - } - } - } - - /// - /// Dispose implementation. - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Protected virtual implementation of Dispose. - /// - /// - private void Dispose(bool disposing) - { - if (this.disposed || !disposing) - return; - - lock (syncLock) - { - if (this.disposed) - return; - - foreach (PSTimer wt in _timers.Values) - { - wt.Dispose(); - } - _timers.Clear(); - - this.disposed = true; - } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTrackingParticipant.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTrackingParticipant.cs deleted file mode 100644 index a6bb12efdf3..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTrackingParticipant.cs +++ /dev/null @@ -1,204 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Activities; - using System.Activities.Statements; - using System.Activities.Validation; - using System.Collections.Generic; - using System.Globalization; - using System.Diagnostics.CodeAnalysis; - using System.Management.Automation.Tracing; - using Microsoft.PowerShell.Activities; - using System.Activities.Tracking; - using System.Text; - - /// - /// Contains members that allow the addition of custom extension to - /// the PowerShell workflow engine. - /// - public static class PSWorkflowExtensions - { - /// - /// The custom workflow extensions delegate to use in this engine - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is needed for providing custom powershell extensions dynamically.")] - static public Func> CustomHandler { get; set; } - } - - /// - /// Implementing the workflow tracking participant, this will established with communication between activities and hosting - /// engine to perform additional task like persistence and logging etc. - /// - internal class PSWorkflowTrackingParticipant : TrackingParticipant - { - private readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private readonly Tracer _structuredTracer = new Tracer(); - - private const String participantName = "WorkflowTrackingParticipant"; - - private PSWorkflowDebugger _debugger; - private const string debugBreakActivity = "PowerShellValue"; - - /// - /// Default constructor. - /// - internal PSWorkflowTrackingParticipant() - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "{0} Created", participantName)); - } - - /// - /// Constructor. - /// - /// PSWorkflowDebugger - internal PSWorkflowTrackingParticipant(PSWorkflowDebugger debugger) - { - _debugger = debugger; - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "{0} Created", participantName)); - } - - /// - /// Retrieve each type of tracking record and perform the corresponding functionality. - /// - /// Represents the tracking record. - /// Time out for the tracking to be completed. - protected override void Track(TrackingRecord record, TimeSpan timeout) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "{0} Emitted trackRecord: {1} Level: {2}, RecordNumber: {3}", participantName, record.GetType().FullName, record.Level, record.RecordNumber)); - - WorkflowInstanceRecord workflowInstanceRecord = record as WorkflowInstanceRecord; - if (workflowInstanceRecord != null) - { - if (_structuredTracer.IsEnabled) - { - if (string.Equals(WorkflowInstanceStates.Persisted, workflowInstanceRecord.State, StringComparison.OrdinalIgnoreCase)) - { - _structuredTracer.WorkflowPersisted(workflowInstanceRecord.InstanceId); - } - else if (string.Equals(WorkflowInstanceStates.UnhandledException, workflowInstanceRecord.State, StringComparison.OrdinalIgnoreCase)) - { - WorkflowInstanceUnhandledExceptionRecord unhandledRecord = workflowInstanceRecord as WorkflowInstanceUnhandledExceptionRecord; - if (unhandledRecord != null) - { - _structuredTracer.WorkflowActivityExecutionFailed(unhandledRecord.InstanceId, - unhandledRecord.FaultSource != null ? unhandledRecord.FaultSource.Name : unhandledRecord.ActivityDefinitionId, - System.Management.Automation.Tracing.Tracer.GetExceptionString(unhandledRecord.UnhandledException)); - } - } - } - this.ProcessWorkflowInstanceRecord(workflowInstanceRecord); - } - - ActivityStateRecord activityStateRecord = record as ActivityStateRecord; - if (activityStateRecord != null) - { - if (_structuredTracer.IsEnabled) - { - ActivityInstanceState activityState = ActivityInstanceState.Executing; - if (!string.IsNullOrEmpty(activityStateRecord.State) - && Enum.TryParse(activityStateRecord.State, out activityState)) - { - if (activityState == ActivityInstanceState.Executing) - { - _structuredTracer.ActivityExecutionQueued(activityStateRecord.InstanceId, activityStateRecord.Activity.Name); - } - } - } - this.ProcessActivityStateRecord(activityStateRecord); - } - - CustomTrackingRecord customTrackingRecord = record as CustomTrackingRecord; - - if ((customTrackingRecord != null) && (customTrackingRecord.Data.Count > 0)) - { - this.ProcessCustomTrackingRecord(customTrackingRecord); - } - } - - /// - /// Process the workflow instance record. - /// - /// Record representing workflow instance record. - private void ProcessWorkflowInstanceRecord(WorkflowInstanceRecord record) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, " Workflow InstanceID: {0} Workflow instance state: {1}", record.InstanceId, record.State)); - } - - /// - /// process the activity state record. - /// - /// Record representing activity state record. - private void ProcessActivityStateRecord(ActivityStateRecord record) - { - IDictionary variables = record.Variables; - StringBuilder vars = new StringBuilder(); - - if (variables.Count > 0) - { - vars.AppendLine("\n\tVariables:"); - foreach (KeyValuePair variable in variables) - { - vars.AppendLine(String.Format(CultureInfo.InvariantCulture, "\t\tName: {0} Value: {1}", variable.Key, variable.Value)); - } - } - - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, " :Activity DisplayName: {0} :ActivityInstanceState: {1} {2}", record.Activity.Name, record.State, ((variables.Count > 0) ? vars.ToString() : String.Empty))); - } - - /// - /// Process the custom tracking record. This record will contain the persistence detail. - /// - /// Record representing custom tracking record. - private void ProcessCustomTrackingRecord(CustomTrackingRecord record) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "\n\tUser Data:")); - - if (_debugger != null && - record.Data != null) - { - // Get breakpoint info if available. - object value; - string bp = null; - if (record.Data.TryGetValue("DebugSequencePoint", out value)) - { - bp = value as string; - record.Data.Remove("DebugSequencePoint"); - } - - // Update debugger variables. - _debugger.UpdateVariables(record.Data); - - // Pass breakpoint info to debugger, which will optionally stop WF activity execution here. - if (!string.IsNullOrEmpty(bp)) - { - try - { - string[] symbols = bp.Trim('\'', '\"').Split(':'); - ActivityPosition debuggerBP = new ActivityPosition( - symbols[2], // WF Name - Convert.ToInt32(symbols[0], CultureInfo.InvariantCulture), // Line number - Convert.ToInt32(symbols[1], CultureInfo.InvariantCulture)); // Col number - - // Debugger blocking call if breakpoint hit or debugger stepping is active. - _debugger.DebuggerCheck(debuggerBP); - } - catch (FormatException) - { } - catch (OverflowException) - { } - } - } - - foreach (string data in record.Data.Keys) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, " \t\t {0} : {1}", data, record.Data[data])); - } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowUtils.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowUtils.cs deleted file mode 100644 index 7711d021415..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowUtils.cs +++ /dev/null @@ -1,323 +0,0 @@ -using System; -using System.Security; -using System.Runtime.InteropServices; -using System.Management.Automation; -using System.Diagnostics; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Remoting; -using System.Management.Automation.Tracing; - -namespace Microsoft.PowerShell.Workflow -{ - static class WorkflowUtils - { - /// - /// CompareConnectionUri compares two connection URIs - /// by doing a comparison of elements. - /// - /// Connection info 1 - /// Connection info 2 - /// True if they match else false. - internal static bool CompareConnectionUri(WSManConnectionInfo connectionInfo1, WSManConnectionInfo connectionInfo2) - { - Debug.Assert(connectionInfo1 != null && connectionInfo2 != null, "Connections should be != null"); - Debug.Assert(!string.IsNullOrEmpty(connectionInfo1.Scheme) && !string.IsNullOrEmpty(connectionInfo1.ComputerName) - && !string.IsNullOrEmpty(connectionInfo1.AppName), "Connection URI elements should be != null or empty"); - Debug.Assert(!string.IsNullOrEmpty(connectionInfo2.Scheme) && !string.IsNullOrEmpty(connectionInfo2.ComputerName) - && !string.IsNullOrEmpty(connectionInfo2.AppName), "Connection URI elements should be != null or empty"); - - if (String.Compare(connectionInfo2.Scheme, connectionInfo1.Scheme, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - if (String.Compare(connectionInfo2.ComputerName, connectionInfo1.ComputerName, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - if (String.Compare(connectionInfo2.AppName, connectionInfo1.AppName, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - if (connectionInfo2.Port != connectionInfo1.Port) - { - return false; - } - - return true; - } // CompareConnectionUri ... - - /// - /// CompareShellUri compares two shell URIs - /// by doing a string of elements. - /// - /// Shell Uri 1 - /// Shell Uri 2 - /// True if they match else false. - internal static bool CompareShellUri(String shellUri1, String shellUri2) - { - Debug.Assert(!string.IsNullOrEmpty(shellUri1) && !string.IsNullOrEmpty(shellUri2), "Shell Uris should be != null or empty"); - - if (String.Compare(shellUri1, shellUri2, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - - return true; - } // CompareShellUri ... - - - /// - /// CompareAuthentication compares two authentication mechanisms. - /// - /// Authentication mechanism 1 - /// Authentication mechanism 2 - /// True if they match else false. - internal static bool CompareAuthentication(AuthenticationMechanism authentication1, AuthenticationMechanism authentication2) - { - return authentication1 == authentication2; - } // CompareAuthentication ... - - /// - /// CompareCredentials compares two PSCredential credentials - /// by doing a username and password comparison . - /// - /// Credential 1 - /// Credential 2 - /// True if they match else false. - internal static bool CompareCredential(PSCredential credential1, PSCredential credential2) - { - if (credential1 == null && credential2 == null) - { - return true; - } - - // check credentials if present - if (credential1 == null ^ credential2 == null) - { - return false; - } - - Debug.Assert(credential1 != null && credential2 != null - && credential1.UserName != null && credential2.UserName != null, "Credentials should be != null"); - - // check the username - if (string.Compare(credential1.UserName, credential2.UserName, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - - // check the password - if (!WorkflowUtils.ComparePassword(credential1.Password, credential2.Password)) - { - return false; - } - - return true; - } - - /// - /// ComparePassword uses native functions to perform a string match on two SecureString passwords - /// by doing a strict byte level comparison is done on the two strings. - /// The use of ReadByte allows the function to execute without marking the assembly as unsafe. - /// - /// Password 1 - /// Password 2 - /// True if they match else false. - internal static bool ComparePassword(SecureString secureString1, SecureString secureString2) - { - if (secureString1 == null && secureString2 == null) - { - return true; - } - - if (secureString1 == null ^ secureString2 == null) - { - return false; - } - - Debug.Assert(secureString1 != null && secureString2 != null, "SecureStrings should be != null"); - - if (secureString1.Length != secureString2.Length) - { - return false; - } - - IntPtr bstr1 = IntPtr.Zero; - IntPtr bstr2 = IntPtr.Zero; - - try - { - bstr1 = Marshal.SecureStringToBSTR(secureString1); - bstr2 = Marshal.SecureStringToBSTR(secureString2); - - int offset = 0; - Byte leftHigh, leftLow, rightHigh, rightLow; - bool notDone = true; - - do - { - leftLow = Marshal.ReadByte(bstr1, offset + 1); - rightLow = Marshal.ReadByte(bstr2, offset + 1); - leftHigh = Marshal.ReadByte(bstr1, offset); - rightHigh = Marshal.ReadByte(bstr2, offset); - offset += 2; - if (leftLow != rightLow || leftHigh != rightHigh) - { - return false; - } - notDone = leftLow != 0 || leftHigh != 0; // terminator - 2 null characters (0x00)? - leftLow = rightLow = leftHigh = rightHigh = 0; - } while (notDone); - - return true; - } - catch (Exception e) - { - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - // SecureStringToBSTR or ReadByte threw exceptions - - tracer.WriteMessage("Getting an exception while comparing credentials..."); - tracer.TraceException(e); - - return false; - } - } - finally - { - if (IntPtr.Zero != bstr1) - { - Marshal.ZeroFreeBSTR(bstr1); - } - if (IntPtr.Zero != bstr2) - { - Marshal.ZeroFreeBSTR(bstr2); - } - } - } // ComparePassword ... - - /// - /// CompareCertificateThumbprint compares two certificate thumbprints - /// by doing a string comparison. - /// - /// Certificate Thumbprint 1 - /// Certificate Thumbprint 2 - /// True if they match else false. - internal static bool CompareCertificateThumbprint(String certificateThumbprint1, String certificateThumbprint2) - { - if (certificateThumbprint1 == null && certificateThumbprint2 == null) - { - return true; - } - - if (certificateThumbprint1 == null ^ certificateThumbprint2 == null) - { - return false; - } - - Debug.Assert(certificateThumbprint1 != null && certificateThumbprint2 != null, "Certificate thumbprints should be != null"); - - if (string.Compare(certificateThumbprint1, certificateThumbprint2, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - - return true; - } // CompareCertificateThumbprint ... - - /// - /// CompareProxySettings compares the proxy settings for two wsman connections - /// by doing a comparison of elements. - /// - /// Connection info 1 - /// Connection info 2 - /// True if they match else false. - internal static bool CompareProxySettings(WSManConnectionInfo connectionInfo1, WSManConnectionInfo connectionInfo2) - { - Debug.Assert(connectionInfo1 != null && connectionInfo2 != null, "Connections should be != null"); - - if (connectionInfo1.ProxyAccessType != connectionInfo2.ProxyAccessType) - { - return false; - } - - if (connectionInfo1.ProxyAccessType == ProxyAccessType.None) - { - return true; //stop here if no proxy access type - } - - if (connectionInfo1.ProxyAuthentication != connectionInfo2.ProxyAuthentication) - { - return false; - } - - // check the proxy credentials password - if (!WorkflowUtils.CompareCredential(connectionInfo1.ProxyCredential, connectionInfo2.ProxyCredential)) - { - return false; - } - - return true; - } // CompareProxySettings ... - - /// - /// CompareOtherWSManSettings compares the rest of the wsman settings for two wsman connections - /// by doing a comparison of elements. - /// - /// Connection info 1 - /// Connection info 2 - /// True if they match else false. - internal static bool CompareOtherWSManSettings(WSManConnectionInfo connectionInfo1, WSManConnectionInfo connectionInfo2) - { - Debug.Assert(connectionInfo1 != null && connectionInfo2 != null, "Connections should be != null"); - - if (connectionInfo1.SkipCACheck != connectionInfo2.SkipCACheck) - { - return false; - } - if (connectionInfo1.SkipCNCheck != connectionInfo2.SkipCNCheck) - { - return false; - } - if (connectionInfo1.SkipRevocationCheck != connectionInfo2.SkipRevocationCheck) - { - return false; - } - if (connectionInfo1.UseCompression != connectionInfo2.UseCompression) - { - return false; - } - if (connectionInfo1.UseUTF16 != connectionInfo2.UseUTF16) - { - return false; - } - if (connectionInfo1.MaximumConnectionRedirectionCount != connectionInfo2.MaximumConnectionRedirectionCount) - { - return false; - } - if (connectionInfo1.MaximumReceivedDataSizePerCommand != connectionInfo2.MaximumReceivedDataSizePerCommand) - { - return false; - } - if (connectionInfo1.MaximumReceivedObjectSize != connectionInfo2.MaximumReceivedObjectSize) - { - return false; - } - if (connectionInfo1.NoEncryption != connectionInfo2.NoEncryption) - { - return false; - } - if (connectionInfo1.NoMachineProfile != connectionInfo2.NoMachineProfile) - { - return false; - } - if (connectionInfo1.OutputBufferingMode != connectionInfo2.OutputBufferingMode) - { - return false; - } - - return true; - } // CompareOtherWSManSettings ... - - } // WorkflowUtils ... -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowValidation.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowValidation.cs deleted file mode 100644 index d782c7324ab..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowValidation.cs +++ /dev/null @@ -1,401 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Diagnostics; - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Activities; - using System.Activities.Statements; - using System.Activities.Validation; - using System.Collections.Generic; - using System.Globalization; - using System.Diagnostics.CodeAnalysis; - using System.Management.Automation.Tracing; - using Microsoft.PowerShell.Activities; - using System.Management.Automation; - using System.Collections.Concurrent; - using System.Reflection; - - /// - /// Contains members that allow for controlling the PowerShell workflow - /// engine validation mechanism. - /// - [SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")] - public static class Validation - { - /// - /// The custom validator delegate to use in this engine - /// - static public Func CustomHandler { get; set; } - } - - internal class PSWorkflowValidationResults - { - internal PSWorkflowValidationResults() - { - this.IsWorkflowSuspendable = false; - this.Results = null; - } - - internal bool IsWorkflowSuspendable { get; set; } - internal ValidationResults Results { get; set; } - } - - /// - /// Validate all the activities in the workflow to check if they are allowed or not. - /// - public class PSWorkflowValidator - { - - #region Privates - - private static readonly string Facility = "WorkflowValidation : "; - private static readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static readonly Tracer _structuredTracer = new Tracer(); - private readonly ConcurrentDictionary _validationCache = new ConcurrentDictionary(); - - private static readonly List MandatorySystemActivities = new List { "SuspendOnError" }; - private static readonly List AllowedSystemActivities = new List - { - "DynamicActivity", - "DoWhile", - "ForEach`1", - "If", - "Parallel", - "ParallelForEach`1", - "Sequence", - "Switch`1", - "While", - "Assign", - "Assign`1", - "Delay", - "InvokeMethod", - "TerminateWorkflow", - "WriteLine", - "Rethrow", - "Throw", - "TryCatch", - "Literal`1", - "VisualBasicValue`1", - "VisualBasicReference`1", - "LocationReferenceValue`1", - "VariableValue`1", - "VariableReference`1", - "LocationReferenceReference`1", - "LambdaValue`1", - "Flowchart", - "FlowDecision", - "FlowSwitch`1", - "AddToCollection`1", - "ExistsInCollection`1", - "RemoveFromCollection`1", - "ClearCollection`1", - }; - - private static HashSet PowerShellActivitiesAssemblies = new HashSet() { - "microsoft.powershell.activities", - "microsoft.powershell.core.activities", - "microsoft.powershell.diagnostics.activities", - "microsoft.powershell.management.activities", - "microsoft.powershell.security.activities", - "microsoft.powershell.utility.activities", - "microsoft.powershell.workflow.servicecore", - "microsoft.wsman.management.activities", - }; - - private void ValidateWorkflowInternal(Activity workflow, string runtimeAssembly, PSWorkflowValidationResults validationResults) - { - Tracer.WriteMessage(Facility + "Validating a workflow."); - _structuredTracer.WorkflowValidationStarted(Guid.Empty); - - ValidationSettings validationSettings = new ValidationSettings - { - AdditionalConstraints = - { - { typeof(Activity), new List { ValidateActivitiesConstraint(runtimeAssembly, validationResults)} } - } - }; - - try - { - validationResults.Results = ActivityValidationServices.Validate(workflow, validationSettings); - } - catch (Exception e) - { - Tracer.TraceException(e); - ValidationException exception = new ValidationException(Resources.ErrorWhileValidatingWorkflow, e); - throw exception; - } - - _structuredTracer.WorkflowValidationFinished(Guid.Empty); - - } - - private Constraint ValidateActivitiesConstraint(string runtimeAssembly, PSWorkflowValidationResults validationResults) - { - DelegateInArgument activityToValidate = new DelegateInArgument(); - DelegateInArgument validationContext = new DelegateInArgument(); - - return new Constraint - { - Body = new ActivityAction - { - Argument1 = activityToValidate, - Argument2 = validationContext, - Handler = new AssertValidation - { - IsWarning = false, - - Assertion = new InArgument( - env => ValidateActivity(activityToValidate.Get(env), runtimeAssembly, validationResults)), - - Message = new InArgument( - env => string.Format(CultureInfo.CurrentCulture, Resources.InvalidActivity, activityToValidate.Get(env).GetType().FullName)) - } - } - }; - } - - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "It is called by another private method 'ValidateActivitiesConstraint'")] - private bool ValidateActivity(Activity activity, string runtimeAssembly, PSWorkflowValidationResults validationResult) - { - if (validationResult != null && validationResult.IsWorkflowSuspendable == false) - { - validationResult.IsWorkflowSuspendable = CheckIfSuspendable(activity); - } - - bool allowed = false; - Type activityType = activity.GetType(); - - // If there is a custom validator activity, then call it. If it returns true - // then just return the activity. - if (Validation.CustomHandler != null && Validation.CustomHandler.Invoke(activity)) - { - allowed = true; - } - else if (MandatorySystemActivities.Contains(activityType.Name)) - { - allowed = true; - } - else if (string.Equals(activityType.Assembly.FullName, "System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", StringComparison.OrdinalIgnoreCase)) - { - allowed = AllowedSystemActivities.Contains(activityType.Name); - } - else if (string.Equals(runtimeAssembly, activityType.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase)) - { - // Allow the activities which belong to runtime assembly containing dependent Xamls. - allowed = true; - } - else if (Configuration.PSDefaultActivitiesAreAllowed - && PowerShellActivitiesAssemblies.Contains(activityType.Assembly.GetName().Name.ToLowerInvariant())) - { - // Allow any of the activities from our product assemblies provided that product activities were - // specified as allowed activities - allowed = true; - } - else - { - // Check if the activityId matches any activity in the allowed list - // as a full name or as pattern. - foreach (string allowedActivity in Configuration.AllowedActivity ?? new string[0]) - { - // skipping * because it has already been checked above - if (string.Equals(allowedActivity, PSWorkflowConfigurationProvider.PSDefaultActivities, StringComparison.OrdinalIgnoreCase)) - continue; - - if (activity is System.Activities.Activity) - { - if (false - || IsMatched(allowedActivity, activityType.Name) - || IsMatched(allowedActivity, activityType.FullName) - || IsMatched(allowedActivity, activityType.Assembly.GetName().Name + "\\" + activityType.Name) - || IsMatched(allowedActivity, activityType.Assembly.GetName().Name + "\\" + activityType.FullName) - || IsMatched(allowedActivity, activityType.Assembly.GetName().FullName + "\\" + activityType.Name) - || IsMatched(allowedActivity, activityType.Assembly.GetName().FullName + "\\" + activityType.FullName)) - { - allowed = true; - break; - } - } - } - } - - string displayName = activity.DisplayName; - - if (string.IsNullOrEmpty(displayName)) - displayName = this.GetType().Name; - - if (allowed) - { - _structuredTracer.WorkflowActivityValidated(Guid.Empty, displayName, activityType.FullName); - } - else - { - _structuredTracer.WorkflowActivityValidationFailed(Guid.Empty, displayName, activityType.FullName); - } - - return allowed; - } - - private bool CheckIfSuspendable(Activity activity) - { - if (string.Equals(activity.GetType().ToString(), "Microsoft.PowerShell.Activities.PSPersist", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - if (string.Equals(activity.GetType().ToString(), "Microsoft.PowerShell.Activities.Suspend", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - PSActivity psactivity = activity as PSActivity; - if (psactivity != null && psactivity.PSPersist != null && psactivity.PSPersist.Expression != null) - { - return true; - } - - Sequence seqActivity = activity as Sequence; - if (seqActivity != null && seqActivity.Variables != null && seqActivity.Variables.Count > 0) - { - foreach (Variable var in seqActivity.Variables) - { - if (string.Equals(var.Name, WorkflowPreferenceVariables.PSPersistPreference, StringComparison.OrdinalIgnoreCase)) - return true; - } - } - - Parallel parActivity = activity as Parallel; - if (parActivity != null && parActivity.Variables != null && parActivity.Variables.Count > 0) - { - foreach (Variable var in parActivity.Variables) - { - if (string.Equals(var.Name, WorkflowPreferenceVariables.PSPersistPreference, StringComparison.OrdinalIgnoreCase)) - return true; - } - } - - return false; - } - - private static bool IsMatched(string allowedActivity, string match) - { - return (WildcardPattern.ContainsWildcardCharacters(allowedActivity) - ? new WildcardPattern(allowedActivity, WildcardOptions.IgnoreCase).IsMatch(match) - : string.Equals(allowedActivity, match, StringComparison.OrdinalIgnoreCase)); - } - - #endregion Privates - - #region Public methods - - /// - /// PSWorkflowValidator - /// - /// - public PSWorkflowValidator(PSWorkflowConfigurationProvider configuration) - { - if (configuration == null) - throw new ArgumentNullException("configuration"); - - if (TestMode) - { - System.Threading.Interlocked.Increment(ref ObjectCounter); - } - - this.Configuration = configuration; - } - - /// - /// Validate the workflow, if it is using the allowed activities. - /// - /// The workflow, which needs to be validated. - /// The additional runtime assembly, which is needed in validation process. - public ValidationResults ValidateWorkflow(Activity workflow, string runtimeAssembly) - { - PSWorkflowValidationResults validationResults = new PSWorkflowValidationResults(); - ValidateWorkflowInternal(workflow, runtimeAssembly, validationResults); - - return validationResults.Results; - } - - #endregion Public methods - - #region Internal Accessors - - internal PSWorkflowConfigurationProvider Configuration - { - get; - private set; - } - - internal PSWorkflowValidationResults ValidateWorkflow(Guid referenceId, Activity workflow, string runtimeAssembly) - { - PSWorkflowValidationResults validationResults = null; - - if (_validationCache.ContainsKey(referenceId)) - { - _validationCache.TryGetValue(referenceId, out validationResults); - } - else - { - validationResults = new PSWorkflowValidationResults(); - - this.ValidateWorkflowInternal(workflow, runtimeAssembly, validationResults); - - // sanity check to ensure cache isn't growing unbounded - if (_validationCache.Keys.Count == Configuration.ValidationCacheLimit) - { - _validationCache.Clear(); - } - _validationCache.TryAdd(referenceId, validationResults); - - } - - return validationResults; - } - - internal void ProcessValidationResults(ValidationResults results) - { - if (results == null) - throw new ArgumentNullException("results"); - - if (results.Errors != null && results.Errors.Count == 0) - return; - - string errorMessage = string.Empty; - - foreach (ValidationError error in results.Errors) - { - errorMessage += error.Message; - errorMessage += "\n"; - } - - ValidationException exception = new ValidationException(errorMessage); - - Tracer.TraceException(exception); - _structuredTracer.WorkflowValidationError(Guid.Empty); - throw exception; - } - - #endregion Internal Accessors - - # region Test Only Variables & Accessors - - // For testing purpose ONLY - internal static bool TestMode = false; - // For testing purpose ONLY - internal static long ObjectCounter = 0; - - - internal bool IsActivityAllowed(Activity activity, string runtimeAssembly) - { - return ValidateActivity(activity, runtimeAssembly, null); - } - # endregion Test Only Variables & Accessors - - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/map.json b/src/Microsoft.PowerShell.Workflow.ServiceCore/map.json deleted file mode 100644 index 2932c531a7a..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/map.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "monad/src/m3p/product/ServiceCore/WorkflowCore/Constants.cs": "ServiceCore/WorkflowCore/Constants.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/DefinitionCache.cs": "ServiceCore/WorkflowCore/DefinitionCache.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/ImportWorkflowCommand.cs": "ServiceCore/WorkflowCore/ImportWorkflowCommand.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowInstance.cs": "ServiceCore/WorkflowCore/WorkflowInstance.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowDebugger.cs": "ServiceCore/WorkflowCore/WorkflowDebugger.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowJob2.cs": "ServiceCore/WorkflowCore/WorkflowJob2.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowJobSourceAdapter.cs": "ServiceCore/WorkflowCore/WorkflowJobSourceAdapter.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowManager.cs": "ServiceCore/WorkflowCore/WorkflowManager.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowStatusEventArgs.cs": "ServiceCore/WorkflowCore/WorkflowStatusEventArgs.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowValidation.cs": "ServiceCore/WorkflowCore/WorkflowValidation.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowRuntimeCompilation.cs": "ServiceCore/WorkflowCore/WorkflowRuntimeCompilation.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowTrackingParticipant.cs": "ServiceCore/WorkflowCore/WorkflowTrackingParticipant.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowMetadatas.cs": "ServiceCore/WorkflowCore/WorkflowMetadatas.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowDefinition.cs": "ServiceCore/WorkflowCore/WorkflowDefinition.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowTimers.cs": "ServiceCore/WorkflowCore/WorkflowTimers.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowPerformanceCounterSetInfo.cs": "ServiceCore/WorkflowCore/WorkflowPerformanceCounterSetInfo.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/WorkflowUtils.cs": "ServiceCore/WorkflowCore/WorkflowUtils.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/FileInstanceStore/AsyncResult.cs": "ServiceCore/WorkflowCore/FileInstanceStore/AsyncResult.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/FileInstanceStore/TypedAsyncResult.cs": "ServiceCore/WorkflowCore/FileInstanceStore/TypedAsyncResult.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/FileInstanceStore/TypedCompletedAsyncResult.cs": "ServiceCore/WorkflowCore/FileInstanceStore/TypedCompletedAsyncResult.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/FileInstanceStore/WorkflowAdditionalStores.cs": "ServiceCore/WorkflowCore/FileInstanceStore/WorkflowAdditionalStores.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStoreCryptography.cs": "ServiceCore/WorkflowCore/FileInstanceStore/InstanceStoreCryptography.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStorePermission.cs": "ServiceCore/WorkflowCore/FileInstanceStore/InstanceStorePermission.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/ActivityHostManager.cs": "ServiceCore/WorkflowCore/ActivityHostManager.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/GlobalConfiguration.cs": "ServiceCore/WorkflowCore/GlobalConfiguration.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/ActivityHostProcess.cs": "ServiceCore/WorkflowCore/ActivityHostProcess.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/PowerShellWorkflowHost.cs": "ServiceCore/WorkflowCore/PowerShellWorkflowHost.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/DefaultWorkflowSessionConfiguration.cs": "ServiceCore/WorkflowCore/DefaultWorkflowSessionConfiguration.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/NewPSWorkflowExecutionOptionCommand.cs": "ServiceCore/WorkflowCore/NewPSWorkflowExecutionOptionCommand.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/ConnectionManager.cs": "ServiceCore/WorkflowCore/ConnectionManager.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/RuntimeBase.cs": "ServiceCore/WorkflowCore/RuntimeBase.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/TimeBasedCache.cs": "ServiceCore/WorkflowCore/TimeBasedCache.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/LocalRunspaceProvider.cs": "ServiceCore/WorkflowCore/LocalRunspaceProvider.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/FileInstanceStore/FileInstanceStore.cs": "ServiceCore/WorkflowCore/FileInstanceStore/FileInstanceStore.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/ActivityCategories.cs": "ServiceCore/WorkflowCore/ActivityCategories.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/ActivityUtils.cs": "ServiceCore/WorkflowCore/ActivityUtils.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/PSActivityBase.cs": "ServiceCore/WorkflowCore/PSActivityBase.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/PSRemotingActivitiesBase.cs": "ServiceCore/WorkflowCore/PSRemotingActivitiesBase.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/PSSelfRemotingActivitiesBase.cs": "ServiceCore/WorkflowCore/PSSelfRemotingActivitiesBase.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/PSActivityHostManager.cs": "ServiceCore/WorkflowCore/PSActivityHostManager.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/PSWorkflowHost.cs": "ServiceCore/WorkflowCore/PSWorkflowHost.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/PSCleanupActivity.cs": "ServiceCore/WorkflowCore/PSCleanupActivity.cs", - "monad/src/m3p/product/ServiceCore/WorkflowCore/Resources/Resources.resx": "resources/Resources.resx" -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/resources/Resources.resx b/src/Microsoft.PowerShell.Workflow.ServiceCore/resources/Resources.resx deleted file mode 100644 index 46e30c38e18..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/resources/Resources.resx +++ /dev/null @@ -1,429 +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 - - - The workflow cannot be started because the compiled activity for the workflow was not found. Cache the workflow definition by calling DefinitionCache.Instance.Add. - DefinitionCache.Instance.Add should not be localized. It is the name of the method. - - - Could not find an activity for the command name '{0}'. - - - The async result is already finished. A finished operation cannot be called twice. - - - An End operation has already been called for this async result. - - - The IAsyncResult specified is not valid. - IAsynResult should not be localized. This is a type. - - - A duplicate instance ID was found. The instance ID already exists. - - - The OwnerId of the instance is mismatched. - - - The Version of the instance is mismatched. - - - The activity is not valid: {0} - - - The async result from the file provider is not valid. - - - Workflow files must have a file name extension of .xaml or .dll, not {0}. If you are using a workflow with a file name extension of .dll, then you must pass only one assembly. - - - The job cannot be started. The job state must be NotStarted and the job can only be started once. - - - The job could not be removed because a job matching the specified criteria could not be found. - - - A parameter element specified in the configuration does not have a Name attribute. - - - The workflow job could not be created because the JobInvocationInfo does not contain a JobDefinition. Initialize the JobInvocationInfo with a JobDefinition to create a workflow job. - JobInvocationInfo and JobDefinition should not be localized. These are types. - - - The workflow job could not be created because the JobInvocationInfo specifies a type other than WorkflowJobSourceAdapter. Correct the JobSourceAdapter type or verify that the correct type is being used. - JobInvocationInfo, WorkflowJobSourceAdapter and JobSourceAdapter should not be localized. These are types. - - - No workflows were found in the specified path '{0}'. - - - A workflow instance does not exist for the specified Instance ID. - - - Null or empty workflow is provided. - - - Parameter {0} has been specified more than once. Remove multiple references to this parameter from the session configuration, and then try again. - {0} should not be localized. It is the name of a parameter - - - The workflow job could not be created because the JobInvocationInfo was not properly initialized. Initialize the JobInvocationInfo with workflow information. - JobInvocationInfo should not be localized. This is a type - - - A parameter element specified in the configuration does not have a Value attribute. - Value should not be localized. This is an attribute. - - - This workflow is already loaded. - - - The workflow instance state file does not exist. - - - The workflow job cannot be resumed, either because persistence data could not be saved completely, or saved persistence data has been corrupted. You must restart the workflow. - - - The workflow is not running. The current state of the workflow instance is {0}. - - - The workflow instance is not yet loaded. - - - The specified IAsyncResultObject is not valid. - IAsyncResult should not be localized. This is a type. - - - The specified runspace is not valid. - - - One or more workflows that are part of the specified job could not be removed. - - - Cannot remove the PSWorkflowJob without removing its parent. Please remove the ContainerParentJob that contains this PSWorkflowJob. - PSWorkflowJob and ContainerParentJob should not be localized. These are types. - - - Shutdown request is in progress, workflow is suspending. - - - The syntax of a parallel script block is 'Parallel { <commands> }' - "Parallel" should not be localized. This is syntax. - - - Suspend could not be completed. - - - Unblock is not a supported operation on a PSWorkflowJob. - PSWorkflowJob should not be localized. This is a type. - - - Workflow complete. - - - Removing workflow instance from the table. - - - An elapsed time-out occurred, and the workflow was stopped. - - - Because a running time-out has occurred, the workflow has been stopped. - - - The workflow could not be resumed. - - - Importing workflow from file '{0}'. - - - The argument to -PSParameterCollection can only contain one entry where PSComputerName is set to '*'. Remove the extra entries and try again. - PSParameterCollection and PSComputerName should not be localized. These are parameter names. - - - The workflow '{0}' could not be started: {1} - - - The Persistence store has reached its maximum size. Free up storage space by removing completed jobs. - - - Value {0} is out of the allowed range. Allowed range is from {1} to {2}. - - - "Internal Error: The object is marked for restricted construction, but another type is constructing it elsewhere. -Target Type:{0} -Allowed Creator Type: {1} -Actual Creator Type: {2} -Method: {3} - - - This PSWorkflowJob was recovered from another session in a suspended state. This can indicate that the workflow was interrupted by a process failure, or a system restart. Run Resume-Job to try resuming the workflow. - PSWorkflowJob and Resume-Job should not be localized. These are a type and a cmdlet name. - - - An exception has occurred while validating the workflow. - - - The argument to -PSParameterCollection can not contain AsJob, JobName, InputObject or PSParameterCollection as entries. Remove the extra entries and try again. - PSParameterCollection, AsJob, JobName, InputObject should not be localized. These are the names of parameters. - - - Activity-Specific Parameters - - - Cannot supply both connection URI and computer name. - - - Connectivity - - - The activity cannot continue running because an error occurred while importing dependent module(s) '{0}' specified for activity '{1}'. To fix this problem, verify that the module exists on the computer. If the module is not required, remove references to the module from the activity. - - - The value of the PersistencePath parameter {0} exceeds the maximum allowed length. The persistence path should not be greater than {1} characters. - - - Input and Output - - - {0} line:{1} char:{2} - - - Compilation error while building dependent workflows. For details see {0}. - - - Only one computer name is allowed. - - - Suspend operation is in progress. - - - Stop operation is in progress. - - - This activity does not support custom remote management. - - - The force-suspend time-out period cannot be zero or null. Specify a time-out period that is greater than zero. - - - The PSParameterCollection parameter can only be used with AsJob, JobName and InputObject. Move the additional parameters to within the PSParameterCollection argument and try again. - PSParameterCollection, AsJob, JobName, InputObject should not be localized. These are parameter names. - - - An error occurred while trying to run the activity. - - - The activity has exceeded the specified maximum running time of {0} seconds. - - - The label is not valid: The label '{0}' doesn't exist. - - - The activity failed because the connection to the remote computer '{0}' was disconnected. - {0} is a computer name and should not be localized - - - Parameters defined for workflow functions do not support ValueFromPipeline. Remove this attribute and import or define the workflow again. - - - The workflow definition has been recycled, because the workflow definition cache limit of {0} has been reached. Import the workflow or module again, and then retry the operation. - - - A credential cannot be specified when NegotiateWithImplicitCredential is specified as the value of Authentication. - - - The PSCredential parameter is supported only when the PSComputerName or PSConnectionUri parameters are used. The PSCredential parameter has been ignored. - - - A CertificateThumbprint cannot be specified when Credential is specified. - - - The '{0}' cmdlet does not implement the InputObject property. - {0} is a cmdlet name and should not be localized - - - The workflow name "{0}" is not a valid workflow. Specify a valid workflow name and then try the command again. - {0} is the name of the workflow and should not be localized - - - Completed - - - Failed - - - Running - - - The specified ActivityImplementationContext is not of type CimActivityImplementationContext. - - - This workflow job cannot be suspended because there are no checkpoints (also called persistence points) in the workflow. To make the workflow job suspendable, add checkpoints to the workflow. - -For more information about how to add checkpoints, see the help topics for Windows PowerShell Workflow. - - - The workflow job cannot be suspended because it does not include checkpoints (also called persistence points). To make the workflow suspendable, use the PSPersist parameter of the workflow. To suspend the workflow forcibly, use the Force parameter of the Suspend-Job cmdlet. - - - The provided XAML cannot be null. - - - The bookmark '{0}' is not valid; it does not exist in the workflow engine. - - - The input objects cannot be serialized. Serialized data is required to suspend and resume workflows. To resolve the error, verify that the values of all variables and parameters are of types that can be serialized. - - - The input objects cannot be serialized. This might prevent the workflow from being resumed correctly. The serialization error might indicate a problem with the data contract that is defined at the type or attribute level. To resolve the problem, verify that the values of all variables and parameters can be serialized. Serialization error is: {0}. - - - The job has been added to the queue to be resumed. - - - The '{0}' language mode is not supported. Supported language modes are FullLanguage and ConstrainedLanguage. - - - Workflows cannot call workflows on this platform. - - - The debugger Quit command has stopped the workflow job. - - - "Cannot process the debugger command until the debugger is stopped." - - - Retrying activity action: attempt {0}. - {0} is a number and should not be localized. - - - Cannot load the workflow. Only signed in-box XAML-based workflows or script-based workflows are supported in the current language mode. - - - Retrying activity connection: attempt {0} of {1}. - {0} and {1} are numbers and should not be localized. - - - Workflow variables cannot be modified inside the debugger. The command or script did not run because it attempted to modify variable '${0}'. - - - - The debugger Quit command has stopped the workflow job. - - diff --git a/src/Microsoft.WSMan.Management.Activities/AssemblyInfo.cs b/src/Microsoft.WSMan.Management.Activities/AssemblyInfo.cs deleted file mode 100644 index ade84ca56ca..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/AssemblyInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Reflection; -using System.Security.Permissions; -using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Diagnostics.CodeAnalysis; - - -[assembly: AssemblyVersion("3.0.0.0")] -[assembly: AssemblyFileVersionAttribute("3.0.0.0")] -[assembly: System.Runtime.InteropServices.ComVisible(false)] - -[assembly: AssemblyConfiguration("")] -[assembly: ReliabilityContractAttribute(Consistency.MayCorruptAppDomain, Cer.MayFail)] -[assembly: AssemblyTitle("Microsoft.WSMan.Management.Activities")] -[assembly: AssemblyDescription("Microsoft.WSMan.Management.Activities")] - -[module: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant")] - diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/ConnectWSManActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/ConnectWSManActivity.cs deleted file mode 100644 index 588f566f81c..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/ConnectWSManActivity.cs +++ /dev/null @@ -1,188 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\Connect-WSMan command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class ConnectWSMan : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public ConnectWSMan() - { - this.DisplayName = "Connect-WSMan"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\Connect-WSMan"; } } - - // Arguments - - /// - /// Provides access to the ApplicationName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ApplicationName { get; set; } - - /// - /// Provides access to the ComputerName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ComputerName { get; set; } - - /// - /// Provides access to the ConnectionURI parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ConnectionURI { get; set; } - - /// - /// Provides access to the OptionSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OptionSet { get; set; } - - /// - /// Provides access to the Port parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Port { get; set; } - - /// - /// Provides access to the SessionOption parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SessionOption { get; set; } - - /// - /// Provides access to the UseSSL parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument UseSSL { get; set; } - - /// - /// Provides access to the Credential parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Credential { get; set; } - - /// - /// Provides access to the Authentication parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Authentication { get; set; } - - /// - /// Provides access to the CertificateThumbprint parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CertificateThumbprint { get; set; } - - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ApplicationName.Expression != null) - { - targetCommand.AddParameter("ApplicationName", ApplicationName.Get(context)); - } - - if((ComputerName.Expression != null) && (PSRemotingBehavior.Get(context) != RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", ComputerName.Get(context)); - } - - if(ConnectionURI.Expression != null) - { - targetCommand.AddParameter("ConnectionURI", ConnectionURI.Get(context)); - } - - if(OptionSet.Expression != null) - { - targetCommand.AddParameter("OptionSet", OptionSet.Get(context)); - } - - if(Port.Expression != null) - { - targetCommand.AddParameter("Port", Port.Get(context)); - } - - if(SessionOption.Expression != null) - { - targetCommand.AddParameter("SessionOption", SessionOption.Get(context)); - } - - if(UseSSL.Expression != null) - { - targetCommand.AddParameter("UseSSL", UseSSL.Get(context)); - } - - if(Credential.Expression != null) - { - targetCommand.AddParameter("Credential", Credential.Get(context)); - } - - if(Authentication.Expression != null) - { - targetCommand.AddParameter("Authentication", Authentication.Get(context)); - } - - if(CertificateThumbprint.Expression != null) - { - targetCommand.AddParameter("CertificateThumbprint", CertificateThumbprint.Get(context)); - } - - if(GetIsComputerNameSpecified(context) && (PSRemotingBehavior.Get(context) == RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", PSComputerName.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/DisableWSManCredSSPActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/DisableWSManCredSSPActivity.cs deleted file mode 100644 index 85ef0b49482..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/DisableWSManCredSSPActivity.cs +++ /dev/null @@ -1,70 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\Disable-WSManCredSSP command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class DisableWSManCredSSP : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public DisableWSManCredSSP() - { - this.DisplayName = "Disable-WSManCredSSP"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\Disable-WSManCredSSP"; } } - - // Arguments - - /// - /// Provides access to the Role parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Role { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(Role.Expression != null) - { - targetCommand.AddParameter("Role", Role.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/DisconnectWSManActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/DisconnectWSManActivity.cs deleted file mode 100644 index eb58b4fca6c..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/DisconnectWSManActivity.cs +++ /dev/null @@ -1,79 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\Disconnect-WSMan command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class DisconnectWSMan : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public DisconnectWSMan() - { - this.DisplayName = "Disconnect-WSMan"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\Disconnect-WSMan"; } } - - // Arguments - - /// - /// Provides access to the ComputerName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ComputerName { get; set; } - - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if((ComputerName.Expression != null) && (PSRemotingBehavior.Get(context) != RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", ComputerName.Get(context)); - } - - if(GetIsComputerNameSpecified(context) && (PSRemotingBehavior.Get(context) == RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", PSComputerName.Get(context)); - } - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/EnableWSManCredSSPActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/EnableWSManCredSSPActivity.cs deleted file mode 100644 index f4b2650e218..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/EnableWSManCredSSPActivity.cs +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\Enable-WSManCredSSP command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class EnableWSManCredSSP : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public EnableWSManCredSSP() - { - this.DisplayName = "Enable-WSManCredSSP"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\Enable-WSManCredSSP"; } } - - // Arguments - - /// - /// Provides access to the DelegateComputer parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument DelegateComputer { get; set; } - - /// - /// Provides access to the Force parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Force { get; set; } - - /// - /// Provides access to the Role parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Role { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(DelegateComputer.Expression != null) - { - targetCommand.AddParameter("DelegateComputer", DelegateComputer.Get(context)); - } - - if(Force.Expression != null) - { - targetCommand.AddParameter("Force", Force.Get(context)); - } - - if(Role.Expression != null) - { - targetCommand.AddParameter("Role", Role.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/GetWSManCredSSPActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/GetWSManCredSSPActivity.cs deleted file mode 100644 index fbc5707cea8..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/GetWSManCredSSPActivity.cs +++ /dev/null @@ -1,58 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\Get-WSManCredSSP command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetWSManCredSSP : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetWSManCredSSP() - { - this.DisplayName = "Get-WSManCredSSP"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\Get-WSManCredSSP"; } } - - // Arguments - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/GetWSManInstanceActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/GetWSManInstanceActivity.cs deleted file mode 100644 index cd19197dde0..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/GetWSManInstanceActivity.cs +++ /dev/null @@ -1,307 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\Get-WSManInstance command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class GetWSManInstance : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public GetWSManInstance() - { - this.DisplayName = "Get-WSManInstance"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\Get-WSManInstance"; } } - - // Arguments - - /// - /// Provides access to the ApplicationName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ApplicationName { get; set; } - - /// - /// Provides access to the BasePropertiesOnly parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument BasePropertiesOnly { get; set; } - - /// - /// Provides access to the ComputerName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ComputerName { get; set; } - - /// - /// Provides access to the ConnectionURI parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ConnectionURI { get; set; } - - /// - /// Provides access to the Dialect parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Dialect { get; set; } - - /// - /// Provides access to the Enumerate parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Enumerate { get; set; } - - /// - /// Provides access to the Filter parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Filter { get; set; } - - /// - /// Provides access to the Fragment parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Fragment { get; set; } - - /// - /// Provides access to the OptionSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OptionSet { get; set; } - - /// - /// Provides access to the Port parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Port { get; set; } - - /// - /// Provides access to the Associations parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Associations { get; set; } - - /// - /// Provides access to the ResourceURI parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ResourceURI { get; set; } - - /// - /// Provides access to the ReturnType parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ReturnType { get; set; } - - /// - /// Provides access to the SelectorSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SelectorSet { get; set; } - - /// - /// Provides access to the SessionOption parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SessionOption { get; set; } - - /// - /// Provides access to the Shallow parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Shallow { get; set; } - - /// - /// Provides access to the UseSSL parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument UseSSL { get; set; } - - /// - /// Provides access to the Credential parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Credential { get; set; } - - /// - /// Provides access to the Authentication parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Authentication { get; set; } - - /// - /// Provides access to the CertificateThumbprint parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CertificateThumbprint { get; set; } - - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ApplicationName.Expression != null) - { - targetCommand.AddParameter("ApplicationName", ApplicationName.Get(context)); - } - - if(BasePropertiesOnly.Expression != null) - { - targetCommand.AddParameter("BasePropertiesOnly", BasePropertiesOnly.Get(context)); - } - - if ((ComputerName.Expression != null) && (PSRemotingBehavior.Get(context) != RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", ComputerName.Get(context)); - } - - if(ConnectionURI.Expression != null) - { - targetCommand.AddParameter("ConnectionURI", ConnectionURI.Get(context)); - } - - if(Dialect.Expression != null) - { - targetCommand.AddParameter("Dialect", Dialect.Get(context)); - } - - if(Enumerate.Expression != null) - { - targetCommand.AddParameter("Enumerate", Enumerate.Get(context)); - } - - if(Filter.Expression != null) - { - targetCommand.AddParameter("Filter", Filter.Get(context)); - } - - if(Fragment.Expression != null) - { - targetCommand.AddParameter("Fragment", Fragment.Get(context)); - } - - if(OptionSet.Expression != null) - { - targetCommand.AddParameter("OptionSet", OptionSet.Get(context)); - } - - if(Port.Expression != null) - { - targetCommand.AddParameter("Port", Port.Get(context)); - } - - if(Associations.Expression != null) - { - targetCommand.AddParameter("Associations", Associations.Get(context)); - } - - if(ResourceURI.Expression != null) - { - targetCommand.AddParameter("ResourceURI", ResourceURI.Get(context)); - } - - if(ReturnType.Expression != null) - { - targetCommand.AddParameter("ReturnType", ReturnType.Get(context)); - } - - if(SelectorSet.Expression != null) - { - targetCommand.AddParameter("SelectorSet", SelectorSet.Get(context)); - } - - if(SessionOption.Expression != null) - { - targetCommand.AddParameter("SessionOption", SessionOption.Get(context)); - } - - if(Shallow.Expression != null) - { - targetCommand.AddParameter("Shallow", Shallow.Get(context)); - } - - if(UseSSL.Expression != null) - { - targetCommand.AddParameter("UseSSL", UseSSL.Get(context)); - } - - if(Credential.Expression != null) - { - targetCommand.AddParameter("Credential", Credential.Get(context)); - } - - if(Authentication.Expression != null) - { - targetCommand.AddParameter("Authentication", Authentication.Get(context)); - } - - if(CertificateThumbprint.Expression != null) - { - targetCommand.AddParameter("CertificateThumbprint", CertificateThumbprint.Get(context)); - } - - if(GetIsComputerNameSpecified(context) && (PSRemotingBehavior.Get(context) == RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", PSComputerName.Get(context)); - } - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/InvokeWSManActionActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/InvokeWSManActionActivity.cs deleted file mode 100644 index 4f4eebb2c34..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/InvokeWSManActionActivity.cs +++ /dev/null @@ -1,248 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\Invoke-WSManAction command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class InvokeWSManAction : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public InvokeWSManAction() - { - this.DisplayName = "Invoke-WSManAction"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\Invoke-WSManAction"; } } - - // Arguments - - /// - /// Provides access to the Action parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Action { get; set; } - - /// - /// Provides access to the ApplicationName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ApplicationName { get; set; } - - /// - /// Provides access to the ComputerName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ComputerName { get; set; } - - /// - /// Provides access to the ConnectionURI parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ConnectionURI { get; set; } - - /// - /// Provides access to the FilePath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument FilePath { get; set; } - - /// - /// Provides access to the OptionSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OptionSet { get; set; } - - /// - /// Provides access to the Port parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Port { get; set; } - - /// - /// Provides access to the SelectorSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SelectorSet { get; set; } - - /// - /// Provides access to the SessionOption parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SessionOption { get; set; } - - /// - /// Provides access to the UseSSL parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument UseSSL { get; set; } - - /// - /// Provides access to the ValueSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ValueSet { get; set; } - - /// - /// Provides access to the ResourceURI parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ResourceURI { get; set; } - - /// - /// Provides access to the Credential parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Credential { get; set; } - - /// - /// Provides access to the Authentication parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Authentication { get; set; } - - /// - /// Provides access to the CertificateThumbprint parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CertificateThumbprint { get; set; } - - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(Action.Expression != null) - { - targetCommand.AddParameter("Action", Action.Get(context)); - } - - if(ApplicationName.Expression != null) - { - targetCommand.AddParameter("ApplicationName", ApplicationName.Get(context)); - } - - if((ComputerName.Expression != null) && (PSRemotingBehavior.Get(context) != RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", ComputerName.Get(context)); - } - - if(ConnectionURI.Expression != null) - { - targetCommand.AddParameter("ConnectionURI", ConnectionURI.Get(context)); - } - - if(FilePath.Expression != null) - { - targetCommand.AddParameter("FilePath", FilePath.Get(context)); - } - - if(OptionSet.Expression != null) - { - targetCommand.AddParameter("OptionSet", OptionSet.Get(context)); - } - - if(Port.Expression != null) - { - targetCommand.AddParameter("Port", Port.Get(context)); - } - - if(SelectorSet.Expression != null) - { - targetCommand.AddParameter("SelectorSet", SelectorSet.Get(context)); - } - - if(SessionOption.Expression != null) - { - targetCommand.AddParameter("SessionOption", SessionOption.Get(context)); - } - - if(UseSSL.Expression != null) - { - targetCommand.AddParameter("UseSSL", UseSSL.Get(context)); - } - - if(ValueSet.Expression != null) - { - targetCommand.AddParameter("ValueSet", ValueSet.Get(context)); - } - - if(ResourceURI.Expression != null) - { - targetCommand.AddParameter("ResourceURI", ResourceURI.Get(context)); - } - - if(Credential.Expression != null) - { - targetCommand.AddParameter("Credential", Credential.Get(context)); - } - - if(Authentication.Expression != null) - { - targetCommand.AddParameter("Authentication", Authentication.Get(context)); - } - - if(CertificateThumbprint.Expression != null) - { - targetCommand.AddParameter("CertificateThumbprint", CertificateThumbprint.Get(context)); - } - - if(GetIsComputerNameSpecified(context) && (PSRemotingBehavior.Get(context) == RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", PSComputerName.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/NewWSManInstanceActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/NewWSManInstanceActivity.cs deleted file mode 100644 index b859c91b3cf..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/NewWSManInstanceActivity.cs +++ /dev/null @@ -1,236 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\New-WSManInstance command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class NewWSManInstance : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public NewWSManInstance() - { - this.DisplayName = "New-WSManInstance"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\New-WSManInstance"; } } - - // Arguments - - /// - /// Provides access to the ApplicationName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ApplicationName { get; set; } - - /// - /// Provides access to the ComputerName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ComputerName { get; set; } - - /// - /// Provides access to the ConnectionURI parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ConnectionURI { get; set; } - - /// - /// Provides access to the FilePath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument FilePath { get; set; } - - /// - /// Provides access to the OptionSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OptionSet { get; set; } - - /// - /// Provides access to the Port parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Port { get; set; } - - /// - /// Provides access to the ResourceURI parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ResourceURI { get; set; } - - /// - /// Provides access to the SelectorSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SelectorSet { get; set; } - - /// - /// Provides access to the SessionOption parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SessionOption { get; set; } - - /// - /// Provides access to the UseSSL parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument UseSSL { get; set; } - - /// - /// Provides access to the ValueSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ValueSet { get; set; } - - /// - /// Provides access to the Credential parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Credential { get; set; } - - /// - /// Provides access to the Authentication parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Authentication { get; set; } - - /// - /// Provides access to the CertificateThumbprint parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CertificateThumbprint { get; set; } - - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ApplicationName.Expression != null) - { - targetCommand.AddParameter("ApplicationName", ApplicationName.Get(context)); - } - - if((ComputerName.Expression != null) && (PSRemotingBehavior.Get(context) != RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", ComputerName.Get(context)); - } - - if(ConnectionURI.Expression != null) - { - targetCommand.AddParameter("ConnectionURI", ConnectionURI.Get(context)); - } - - if(FilePath.Expression != null) - { - targetCommand.AddParameter("FilePath", FilePath.Get(context)); - } - - if(OptionSet.Expression != null) - { - targetCommand.AddParameter("OptionSet", OptionSet.Get(context)); - } - - if(Port.Expression != null) - { - targetCommand.AddParameter("Port", Port.Get(context)); - } - - if(ResourceURI.Expression != null) - { - targetCommand.AddParameter("ResourceURI", ResourceURI.Get(context)); - } - - if(SelectorSet.Expression != null) - { - targetCommand.AddParameter("SelectorSet", SelectorSet.Get(context)); - } - - if(SessionOption.Expression != null) - { - targetCommand.AddParameter("SessionOption", SessionOption.Get(context)); - } - - if(UseSSL.Expression != null) - { - targetCommand.AddParameter("UseSSL", UseSSL.Get(context)); - } - - if(ValueSet.Expression != null) - { - targetCommand.AddParameter("ValueSet", ValueSet.Get(context)); - } - - if(Credential.Expression != null) - { - targetCommand.AddParameter("Credential", Credential.Get(context)); - } - - if(Authentication.Expression != null) - { - targetCommand.AddParameter("Authentication", Authentication.Get(context)); - } - - if(CertificateThumbprint.Expression != null) - { - targetCommand.AddParameter("CertificateThumbprint", CertificateThumbprint.Get(context)); - } - - if(GetIsComputerNameSpecified(context) && (PSRemotingBehavior.Get(context) == RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", PSComputerName.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/NewWSManSessionOptionActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/NewWSManSessionOptionActivity.cs deleted file mode 100644 index 428290901c2..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/NewWSManSessionOptionActivity.cs +++ /dev/null @@ -1,178 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\New-WSManSessionOption command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class NewWSManSessionOption : PSActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public NewWSManSessionOption() - { - this.DisplayName = "New-WSManSessionOption"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\New-WSManSessionOption"; } } - - // Arguments - - /// - /// Provides access to the ProxyAccessType parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ProxyAccessType { get; set; } - - /// - /// Provides access to the ProxyAuthentication parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ProxyAuthentication { get; set; } - - /// - /// Provides access to the ProxyCredential parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ProxyCredential { get; set; } - - /// - /// Provides access to the SkipCACheck parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SkipCACheck { get; set; } - - /// - /// Provides access to the SkipCNCheck parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SkipCNCheck { get; set; } - - /// - /// Provides access to the SkipRevocationCheck parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SkipRevocationCheck { get; set; } - - /// - /// Provides access to the SPNPort parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SPNPort { get; set; } - - /// - /// Provides access to the OperationTimeout parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OperationTimeout { get; set; } - - /// - /// Provides access to the NoEncryption parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument NoEncryption { get; set; } - - /// - /// Provides access to the UseUTF16 parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument UseUTF16 { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ProxyAccessType.Expression != null) - { - targetCommand.AddParameter("ProxyAccessType", ProxyAccessType.Get(context)); - } - - if(ProxyAuthentication.Expression != null) - { - targetCommand.AddParameter("ProxyAuthentication", ProxyAuthentication.Get(context)); - } - - if(ProxyCredential.Expression != null) - { - targetCommand.AddParameter("ProxyCredential", ProxyCredential.Get(context)); - } - - if(SkipCACheck.Expression != null) - { - targetCommand.AddParameter("SkipCACheck", SkipCACheck.Get(context)); - } - - if(SkipCNCheck.Expression != null) - { - targetCommand.AddParameter("SkipCNCheck", SkipCNCheck.Get(context)); - } - - if(SkipRevocationCheck.Expression != null) - { - targetCommand.AddParameter("SkipRevocationCheck", SkipRevocationCheck.Get(context)); - } - - if(SPNPort.Expression != null) - { - targetCommand.AddParameter("SPNPort", SPNPort.Get(context)); - } - - if(OperationTimeout.Expression != null) - { - targetCommand.AddParameter("OperationTimeout", OperationTimeout.Get(context)); - } - - if(NoEncryption.Expression != null) - { - targetCommand.AddParameter("NoEncryption", NoEncryption.Get(context)); - } - - if(UseUTF16.Expression != null) - { - targetCommand.AddParameter("UseUTF16", UseUTF16.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/RemoveWSManInstanceActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/RemoveWSManInstanceActivity.cs deleted file mode 100644 index 6881e8fe4b2..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/RemoveWSManInstanceActivity.cs +++ /dev/null @@ -1,212 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\Remove-WSManInstance command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class RemoveWSManInstance : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public RemoveWSManInstance() - { - this.DisplayName = "Remove-WSManInstance"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\Remove-WSManInstance"; } } - - // Arguments - - /// - /// Provides access to the ApplicationName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ApplicationName { get; set; } - - /// - /// Provides access to the ComputerName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ComputerName { get; set; } - - /// - /// Provides access to the ConnectionURI parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ConnectionURI { get; set; } - - /// - /// Provides access to the OptionSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OptionSet { get; set; } - - /// - /// Provides access to the Port parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Port { get; set; } - - /// - /// Provides access to the ResourceURI parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ResourceURI { get; set; } - - /// - /// Provides access to the SelectorSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SelectorSet { get; set; } - - /// - /// Provides access to the SessionOption parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SessionOption { get; set; } - - /// - /// Provides access to the UseSSL parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument UseSSL { get; set; } - - /// - /// Provides access to the Credential parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Credential { get; set; } - - /// - /// Provides access to the Authentication parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Authentication { get; set; } - - /// - /// Provides access to the CertificateThumbprint parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CertificateThumbprint { get; set; } - - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ApplicationName.Expression != null) - { - targetCommand.AddParameter("ApplicationName", ApplicationName.Get(context)); - } - - if((ComputerName.Expression != null) && (PSRemotingBehavior.Get(context) != RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", ComputerName.Get(context)); - } - - if(ConnectionURI.Expression != null) - { - targetCommand.AddParameter("ConnectionURI", ConnectionURI.Get(context)); - } - - if(OptionSet.Expression != null) - { - targetCommand.AddParameter("OptionSet", OptionSet.Get(context)); - } - - if(Port.Expression != null) - { - targetCommand.AddParameter("Port", Port.Get(context)); - } - - if(ResourceURI.Expression != null) - { - targetCommand.AddParameter("ResourceURI", ResourceURI.Get(context)); - } - - if(SelectorSet.Expression != null) - { - targetCommand.AddParameter("SelectorSet", SelectorSet.Get(context)); - } - - if(SessionOption.Expression != null) - { - targetCommand.AddParameter("SessionOption", SessionOption.Get(context)); - } - - if(UseSSL.Expression != null) - { - targetCommand.AddParameter("UseSSL", UseSSL.Get(context)); - } - - if(Credential.Expression != null) - { - targetCommand.AddParameter("Credential", Credential.Get(context)); - } - - if(Authentication.Expression != null) - { - targetCommand.AddParameter("Authentication", Authentication.Get(context)); - } - - if(CertificateThumbprint.Expression != null) - { - targetCommand.AddParameter("CertificateThumbprint", CertificateThumbprint.Get(context)); - } - - if(GetIsComputerNameSpecified(context) && (PSRemotingBehavior.Get(context) == RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", PSComputerName.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/SetWSManInstanceActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/SetWSManInstanceActivity.cs deleted file mode 100644 index 7d6a39b0c57..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/SetWSManInstanceActivity.cs +++ /dev/null @@ -1,259 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\Set-WSManInstance command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class SetWSManInstance : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public SetWSManInstance() - { - this.DisplayName = "Set-WSManInstance"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\Set-WSManInstance"; } } - - // Arguments - - /// - /// Provides access to the ApplicationName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ApplicationName { get; set; } - - /// - /// Provides access to the ComputerName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ComputerName { get; set; } - - /// - /// Provides access to the ConnectionURI parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ConnectionURI { get; set; } - - /// - /// Provides access to the Dialect parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Dialect { get; set; } - - /// - /// Provides access to the FilePath parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument FilePath { get; set; } - - /// - /// Provides access to the Fragment parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Fragment { get; set; } - - /// - /// Provides access to the OptionSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument OptionSet { get; set; } - - /// - /// Provides access to the Port parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Port { get; set; } - - /// - /// Provides access to the ResourceURI parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ResourceURI { get; set; } - - /// - /// Provides access to the SelectorSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SelectorSet { get; set; } - - /// - /// Provides access to the SessionOption parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SessionOption { get; set; } - - /// - /// Provides access to the UseSSL parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument UseSSL { get; set; } - - /// - /// Provides access to the ValueSet parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ValueSet { get; set; } - - /// - /// Provides access to the Credential parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Credential { get; set; } - - /// - /// Provides access to the Authentication parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Authentication { get; set; } - - /// - /// Provides access to the CertificateThumbprint parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CertificateThumbprint { get; set; } - - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(ApplicationName.Expression != null) - { - targetCommand.AddParameter("ApplicationName", ApplicationName.Get(context)); - } - - if((ComputerName.Expression != null) && (PSRemotingBehavior.Get(context) != RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", ComputerName.Get(context)); - } - - if(ConnectionURI.Expression != null) - { - targetCommand.AddParameter("ConnectionURI", ConnectionURI.Get(context)); - } - - if(Dialect.Expression != null) - { - targetCommand.AddParameter("Dialect", Dialect.Get(context)); - } - - if(FilePath.Expression != null) - { - targetCommand.AddParameter("FilePath", FilePath.Get(context)); - } - - if(Fragment.Expression != null) - { - targetCommand.AddParameter("Fragment", Fragment.Get(context)); - } - - if(OptionSet.Expression != null) - { - targetCommand.AddParameter("OptionSet", OptionSet.Get(context)); - } - - if(Port.Expression != null) - { - targetCommand.AddParameter("Port", Port.Get(context)); - } - - if(ResourceURI.Expression != null) - { - targetCommand.AddParameter("ResourceURI", ResourceURI.Get(context)); - } - - if(SelectorSet.Expression != null) - { - targetCommand.AddParameter("SelectorSet", SelectorSet.Get(context)); - } - - if(SessionOption.Expression != null) - { - targetCommand.AddParameter("SessionOption", SessionOption.Get(context)); - } - - if(UseSSL.Expression != null) - { - targetCommand.AddParameter("UseSSL", UseSSL.Get(context)); - } - - if(ValueSet.Expression != null) - { - targetCommand.AddParameter("ValueSet", ValueSet.Get(context)); - } - - if(Credential.Expression != null) - { - targetCommand.AddParameter("Credential", Credential.Get(context)); - } - - if(Authentication.Expression != null) - { - targetCommand.AddParameter("Authentication", Authentication.Get(context)); - } - - if(CertificateThumbprint.Expression != null) - { - targetCommand.AddParameter("CertificateThumbprint", CertificateThumbprint.Get(context)); - } - - if(GetIsComputerNameSpecified(context) && (PSRemotingBehavior.Get(context) == RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", PSComputerName.Get(context)); - } - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/SetWSManQuickConfigActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/SetWSManQuickConfigActivity.cs deleted file mode 100644 index 72adebafc96..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/SetWSManQuickConfigActivity.cs +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\Set-WSManQuickConfig command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class SetWSManQuickConfig : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public SetWSManQuickConfig() - { - this.DisplayName = "Set-WSManQuickConfig"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\Set-WSManQuickConfig"; } } - - // Arguments - - /// - /// Provides access to the UseSSL parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument UseSSL { get; set; } - - /// - /// Provides access to the Force parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Force { get; set; } - - /// - /// Provides access to the SkipNetworkProfileCheck parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument SkipNetworkProfileCheck { get; set; } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(UseSSL.Expression != null) - { - targetCommand.AddParameter("UseSSL", UseSSL.Get(context)); - } - - if(Force.Expression != null) - { - targetCommand.AddParameter("Force", Force.Get(context)); - } - - if(SkipNetworkProfileCheck.Expression != null) - { - targetCommand.AddParameter("SkipNetworkProfileCheck", SkipNetworkProfileCheck.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/Generated/TestWSManActivity.cs b/src/Microsoft.WSMan.Management.Activities/Generated/TestWSManActivity.cs deleted file mode 100644 index 558c1e1d6f6..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/Generated/TestWSManActivity.cs +++ /dev/null @@ -1,152 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using Microsoft.PowerShell.Activities; -using System.Management.Automation; -using System.Activities; -using System.Collections.Generic; -using System.ComponentModel; - - -namespace Microsoft.WSMan.Management.Activities -{ - /// - /// Activity to invoke the Microsoft.WSMan.Management\Test-WSMan command in a Workflow. - /// - [System.CodeDom.Compiler.GeneratedCode("Microsoft.PowerShell.Activities.ActivityGenerator.GenerateFromName", "3.0")] - public sealed class TestWSMan : PSRemotingActivity - { - /// - /// Gets the display name of the command invoked by this activity. - /// - public TestWSMan() - { - this.DisplayName = "Test-WSMan"; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public override string PSCommandName { get { return "Microsoft.WSMan.Management\\Test-WSMan"; } } - - // Arguments - - /// - /// Provides access to the Authentication parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Authentication { get; set; } - - /// - /// Provides access to the ComputerName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ComputerName { get; set; } - - /// - /// Provides access to the Port parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Port { get; set; } - - /// - /// Provides access to the UseSSL parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument UseSSL { get; set; } - - /// - /// Provides access to the ApplicationName parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument ApplicationName { get; set; } - - /// - /// Provides access to the Credential parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument Credential { get; set; } - - /// - /// Provides access to the CertificateThumbprint parameter. - /// - [ParameterSpecificCategory] - [DefaultValue(null)] - public InArgument CertificateThumbprint { get; set; } - - /// - /// Declares that this activity supports its own remoting. - /// - protected override bool SupportsCustomRemoting { get { return true; } } - - - // Module defining this command - - - // Optional custom code for this activity - - - /// - /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); - System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); - - // Initialize the arguments - - if(Authentication.Expression != null) - { - targetCommand.AddParameter("Authentication", Authentication.Get(context)); - } - - if((ComputerName.Expression != null) && (PSRemotingBehavior.Get(context) != RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", ComputerName.Get(context)); - } - - if(Port.Expression != null) - { - targetCommand.AddParameter("Port", Port.Get(context)); - } - - if(UseSSL.Expression != null) - { - targetCommand.AddParameter("UseSSL", UseSSL.Get(context)); - } - - if(ApplicationName.Expression != null) - { - targetCommand.AddParameter("ApplicationName", ApplicationName.Get(context)); - } - - if(Credential.Expression != null) - { - targetCommand.AddParameter("Credential", Credential.Get(context)); - } - - if(CertificateThumbprint.Expression != null) - { - targetCommand.AddParameter("CertificateThumbprint", CertificateThumbprint.Get(context)); - } - - if(GetIsComputerNameSpecified(context) && (PSRemotingBehavior.Get(context) == RemotingBehavior.Custom)) - { - targetCommand.AddParameter("ComputerName", PSComputerName.Get(context)); - } - - - return new ActivityImplementationContext() { PowerShellInstance = invoker }; - } - } -} diff --git a/src/Microsoft.WSMan.Management.Activities/map.json b/src/Microsoft.WSMan.Management.Activities/map.json deleted file mode 100644 index 1cb2358b2fd..00000000000 --- a/src/Microsoft.WSMan.Management.Activities/map.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/ConnectWSManActivity.cs": "Generated/ConnectWSManActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/DisableWSManCredSSPActivity.cs": "Generated/DisableWSManCredSSPActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/DisconnectWSManActivity.cs": "Generated/DisconnectWSManActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/EnableWSManCredSSPActivity.cs": "Generated/EnableWSManCredSSPActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/GetWSManCredSSPActivity.cs": "Generated/GetWSManCredSSPActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/GetWSManInstanceActivity.cs": "Generated/GetWSManInstanceActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/InvokeWSManActionActivity.cs": "Generated/InvokeWSManActionActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/NewWSManInstanceActivity.cs": "Generated/NewWSManInstanceActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/NewWSManSessionOptionActivity.cs": "Generated/NewWSManSessionOptionActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/RemoveWSManInstanceActivity.cs": "Generated/RemoveWSManInstanceActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/SetWSManInstanceActivity.cs": "Generated/SetWSManInstanceActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/SetWSManQuickConfigActivity.cs": "Generated/SetWSManQuickConfigActivity.cs", - "monad/src/Activities/Generated/Microsoft_WSMan_Management_Activities/TestWSManActivity.cs": "Generated/TestWSManActivity.cs" -} diff --git a/src/Microsoft.WSMan.Management/AssemblyInfo.cs b/src/Microsoft.WSMan.Management/AssemblyInfo.cs deleted file mode 100644 index e55ec824d47..00000000000 --- a/src/Microsoft.WSMan.Management/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Reflection; - -[assembly:AssemblyFileVersionAttribute("3.0.0.0")] -[assembly:AssemblyVersion("3.0.0.0")] diff --git a/src/Microsoft.WSMan.Management/ConfigProvider.cs b/src/Microsoft.WSMan.Management/ConfigProvider.cs index cd43da6239c..55141379faa 100644 --- a/src/Microsoft.WSMan.Management/ConfigProvider.cs +++ b/src/Microsoft.WSMan.Management/ConfigProvider.cs @@ -1,55 +1,54 @@ -/********************************************************************++ -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.Text; -using System.Management.Automation; -using System.Management.Automation.Provider; +using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Xml; -using System.Xml.XPath; -using System.Text.RegularExpressions; -using System.IO; -using System.Runtime.InteropServices; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.IO; +using System.Management.Automation; +using System.Management.Automation.Provider; +using System.Runtime.InteropServices; using System.Security; -#if !CORECLR using System.ServiceProcess; -#endif +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.XPath; namespace Microsoft.WSMan.Management { - /// /// WsMan Provider. /// [CmdletProvider(WSManStringLiterals.ProviderName, ProviderCapabilities.Credentials)] - public sealed partial class WSManConfigProvider : NavigationCmdletProvider, ICmdletProviderSupportsHelp + public sealed class WSManConfigProvider : NavigationCmdletProvider, ICmdletProviderSupportsHelp { - //Plugin Name Storage + // Plugin Name Storage private PSObject objPluginNames = null; + private ServiceController winrmServiceController; + /// /// Determines if Set-Item user input type validation is required or not. /// It is True by default, Clear-Item will set it to false so that it can - /// pass Empty String as value for Set-Item. + /// pass Empty string as value for Set-Item. /// private bool clearItemIsCalled = false; - WSManHelper helper = new WSManHelper(); + private WSManHelper helper = new WSManHelper(); /// /// Object contains the cache of the enumerate results for the cmdlet to execute. /// - Dictionary enumerateMapping = new Dictionary(); + private readonly Dictionary enumerateMapping = new Dictionary(); /// /// Mapping of ResourceURI with the XML returned by the Get call. /// - Dictionary getMapping = new Dictionary(); + private readonly Dictionary getMapping = new Dictionary(); #region ICmdletProviderSupportsHelp Members @@ -62,16 +61,17 @@ public sealed partial class WSManConfigProvider : NavigationCmdletProvider, ICmd /// string ICmdletProviderSupportsHelp.GetHelpMaml(string helpItemName, string path) { - //Get the leaf node from the path for which help is requested. - int ChildIndex = path.LastIndexOf("\\", StringComparison.OrdinalIgnoreCase); + // Get the leaf node from the path for which help is requested. + int ChildIndex = path.LastIndexOf('\\'); if (ChildIndex == -1) { - //Means we are at host level, where no new-item is supported. Return empty string. - return String.Empty; + // Means we are at host level, where no new-item is supported. Return empty string. + return string.Empty; } - String child = path.Substring(ChildIndex + 1); - //We only return help for the below set of 5 commands, not for any other case. + string child = path.Substring(ChildIndex + 1); + + // We only return help for the below set of 5 commands, not for any other case. switch (helpItemName) { case "New-Item": @@ -81,14 +81,14 @@ string ICmdletProviderSupportsHelp.GetHelpMaml(string helpItemName, string path) case "Remove-Item": break; default: - return String.Empty; + return string.Empty; } // Load the help file from the current UI culture subfolder of the module's root folder XmlDocument document = new XmlDocument(); CultureInfo culture = Host.CurrentUICulture; - String providerBase = this.ProviderInfo.PSSnapIn != null ? this.ProviderInfo.PSSnapIn.ApplicationBase : this.ProviderInfo.Module.ModuleBase; // "\windows\system32\WindowsPowerShell\v1.0" - String helpFile = null; + string providerBase = this.ProviderInfo.PSSnapIn != null ? this.ProviderInfo.PSSnapIn.ApplicationBase : this.ProviderInfo.Module.ModuleBase; // "\windows\system32\WindowsPowerShell\v1.0" + string helpFile = null; do { @@ -102,56 +102,54 @@ string ICmdletProviderSupportsHelp.GetHelpMaml(string helpItemName, string path) break; } } + culture = culture.Parent; } while (culture != culture.Parent); if (helpFile == null) { - //Can't find help file. Return empty string - return String.Empty; + // Can't find help file. Return empty string + return string.Empty; } try { - //XmlDocument in CoreCLR does not have file path parameter, use XmlReader XmlReaderSettings readerSettings = new XmlReaderSettings(); -#if !CORECLR readerSettings.XmlResolver = null; -#endif using (XmlReader reader = XmlReader.Create(helpFile, readerSettings)) { document.Load(reader); } } - catch(XmlException) + catch (XmlException) { - return String.Empty; + return string.Empty; } - catch(PathTooLongException) + catch (PathTooLongException) { - return String.Empty; + return string.Empty; } - catch(IOException) + catch (IOException) { - return String.Empty; + return string.Empty; } - catch(UnauthorizedAccessException) + catch (UnauthorizedAccessException) { - return String.Empty; + return string.Empty; } - catch(NotSupportedException) + catch (NotSupportedException) { - return String.Empty; + return string.Empty; } - catch(SecurityException) + catch (SecurityException) { - return String.Empty; + return string.Empty; } // Add the "msh" and "command" namespaces from the MAML schema XmlNamespaceManager nsMgr = new XmlNamespaceManager(document.NameTable); // XPath 1.0 associates empty prefix with "null" namespace; must use non-empty prefix for default namespace. - // This will not work: nsMgr.AddNamespace("", "http://msh"); + // This will not work: nsMgr.AddNamespace(string.Empty, "http://msh"); nsMgr.AddNamespace("msh", "http://msh"); nsMgr.AddNamespace("command", "http://schemas.microsoft.com/maml/dev/command/2004/10"); @@ -159,7 +157,7 @@ string ICmdletProviderSupportsHelp.GetHelpMaml(string helpItemName, string path) string verb = helpItemName.Split('-')[0]; string noun = helpItemName.Substring(helpItemName.IndexOf('-') + 1); - //Compose XPath query to select the appropriate node based on the verb, noun and id + // Compose XPath query to select the appropriate node based on the verb, noun and id string xpathQuery = "/msh:helpItems/msh:providerHelp/msh:CmdletHelpPaths/msh:CmdletHelpPath[@id='" + child + "' or @ID='" + child + "']/command:command/command:details[command:verb='" + verb + "' and command:noun='" + noun + "']"; // Execute the XPath query and if the command was found, return its MAML snippet @@ -168,23 +166,23 @@ string ICmdletProviderSupportsHelp.GetHelpMaml(string helpItemName, string path) { result = document.SelectSingleNode(xpathQuery, nsMgr); } - catch(XPathException) + catch (XPathException) { - return String.Empty; + return string.Empty; } + if (result != null) { return result.ParentNode.OuterXml; } - return String.Empty; + + return string.Empty; } #endregion - #region DriveCmdletProvider /// - /// /// /// /// @@ -195,7 +193,7 @@ protected override PSDriveInfo NewDrive(PSDriveInfo drive) return null; } - if (String.IsNullOrEmpty(drive.Root) == false) + if (!string.IsNullOrEmpty(drive.Root)) { AssertError(helper.GetResourceMsgFromResourcetext("NewDriveRootDoesNotExist"), false); return null; @@ -205,19 +203,19 @@ protected override PSDriveInfo NewDrive(PSDriveInfo drive) } /// - /// Adds the required drive + /// Adds the required drive. /// /// protected override Collection InitializeDefaultDrives() { Collection drives = new Collection(); - drives.Add(new PSDriveInfo(WSManStringLiterals.rootpath, ProviderInfo, String.Empty, + drives.Add(new PSDriveInfo(WSManStringLiterals.rootpath, ProviderInfo, string.Empty, helper.GetResourceMsgFromResourcetext("ConfigStorage"), null)); return drives; } /// - /// Removes the required drive + /// Removes the required drive. /// /// protected override PSDriveInfo RemoveDrive(PSDriveInfo drive) @@ -226,7 +224,6 @@ protected override PSDriveInfo RemoveDrive(PSDriveInfo drive) return drive; } - #endregion #region ItemCmdletProvider @@ -240,10 +237,9 @@ protected override PSDriveInfo RemoveDrive(PSDriveInfo drive) /// protected override string GetChildName(string path) { - - string result = String.Empty; + string result = string.Empty; int separatorIndex = path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator); - string hostname = String.Empty; + string hostname = string.Empty; if (separatorIndex == -1) { result = path; @@ -256,7 +252,6 @@ protected override string GetChildName(string path) } return GetCorrectCaseOfName(result, hostname, path); - } /// @@ -275,7 +270,7 @@ protected override string MakePath(string parent, string child) child = child.Remove(child.LastIndexOf(WSManStringLiterals.DefaultPathSeparator)); } - //For Listeners only ... should remove Listener from listener\listener but not from listener_[Hashcode] + // For Listeners only ... should remove Listener from listener\listener but not from listener_[Hashcode] if (parent.Equals(WSManStringLiterals.containerListener, StringComparison.OrdinalIgnoreCase) && child.StartsWith(parent, StringComparison.OrdinalIgnoreCase)) { if (!child.StartsWith(parent + "_", StringComparison.OrdinalIgnoreCase)) @@ -283,6 +278,7 @@ protected override string MakePath(string parent, string child) child = child.Remove(0, parent.Length); } } + string path = string.Empty; string ChildName = string.Empty; string CorrectCaseChildName = string.Empty; @@ -294,11 +290,13 @@ protected override string MakePath(string parent, string child) { path = child; } + if (path.Length != 0) { ChildName = path.Substring(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator) + 1); CorrectCaseChildName = GetChildName(path); } + if (ChildName.Equals(CorrectCaseChildName, StringComparison.OrdinalIgnoreCase)) { if (child.Contains(WSManStringLiterals.DefaultPathSeparator.ToString())) @@ -311,13 +309,14 @@ protected override string MakePath(string parent, string child) child = CorrectCaseChildName; } } - String basepath = base.MakePath(parent, child); + + string basepath = base.MakePath(parent, child); return GetCorrectCaseOfPath(basepath); } /// /// Checks whether the path is Valid. - /// eg. winrm/config/client + /// eg. winrm/config/client. /// /// /// @@ -326,11 +325,10 @@ protected override bool IsValidPath(string path) bool result = false; result = CheckValidContainerOrPath(path); return result; - } /// - /// Check whether an Item Exist in the winrm configuration + /// Check whether an Item Exist in the winrm configuration. /// /// /// @@ -350,15 +348,15 @@ protected override bool ItemExists(string path) /// protected override bool HasChildItems(string path) { - string childname = String.Empty; - string strPathCheck = String.Empty; + string childname = string.Empty; + string strPathCheck = string.Empty; - if (path.Length == 0 && String.IsNullOrEmpty(childname)) + if (path.Length == 0 && string.IsNullOrEmpty(childname)) { return true; } - //if endswith '\', removes it. + // if endswith '\', removes it. if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator.ToString(), StringComparison.OrdinalIgnoreCase)) { path = path.Remove(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator)); @@ -366,19 +364,20 @@ protected override bool HasChildItems(string path) if (path.Contains(WSManStringLiterals.DefaultPathSeparator.ToString())) { - //Get the ChildName + // Get the ChildName childname = path.Substring(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator) + 1); } + Dictionary SessionObjCache = WSManHelper.GetSessionObjCache(); if (SessionObjCache.ContainsKey(path)) { return true; } - //Get the wsman host name to find the session object + // Get the wsman host name to find the session object string host = GetHostName(path); - //Chks the WinRM Service + // Chks the WinRM Service if (IsPathLocalMachine(host)) { if (!IsWSManServiceRunning()) @@ -387,16 +386,15 @@ protected override bool HasChildItems(string path) StartWSManService(Force); } } + string WsManURI = NormalizePath(path, host); - lock(WSManHelper.AutoSession) + lock (WSManHelper.AutoSession) { - //Gets the session object from the cache. + // Gets the session object from the cache. object sessionobj; SessionObjCache.TryGetValue(host, out sessionobj); - - /* WsMan Config Can be divided in to Four Fixed Regions to Check Whether it has Child Items. @@ -431,6 +429,7 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether it has Chi { return true; } + ProcessCertMappingObjects(xmlCertificates, out CertificatesObjCache, out KeyCache); if (CertificatesObjCache.Count > 0) { @@ -440,9 +439,8 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether it has Chi // 3. Plugin and its internal structure Checks else if (WsManURI.Contains(WSManStringLiterals.containerPlugin)) { - - strPathCheck = strPathCheck + WSManStringLiterals.containerPlugin; - //Check for Plugin path + strPathCheck += WSManStringLiterals.containerPlugin; + // Check for Plugin path XmlDocument xmlPlugins = FindResourceValue(sessionobj, WsManURI, null); string currentpluginname = string.Empty; int PluginCount = GetPluginNames(xmlPlugins, out objPluginNames, out currentpluginname, path); @@ -456,8 +454,8 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether it has Chi { return false; } - } + strPathCheck = strPathCheck + WSManStringLiterals.DefaultPathSeparator + currentpluginname; if (path.EndsWith(strPathCheck, StringComparison.OrdinalIgnoreCase)) { @@ -473,19 +471,21 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether it has Chi } } } + string filter = WsManURI + "?Name=" + currentpluginname; XmlDocument CurrentPluginXML = GetResourceValue(sessionobj, filter, null); ArrayList arrSecurities = null; ArrayList arrResources = ProcessPluginResourceLevel(CurrentPluginXML, out arrSecurities); ArrayList arrInitParams = ProcessPluginInitParamLevel(CurrentPluginXML); - strPathCheck = strPathCheck + WSManStringLiterals.DefaultPathSeparator; + strPathCheck += WSManStringLiterals.DefaultPathSeparator; if (path.EndsWith(strPathCheck + WSManStringLiterals.containerResources, StringComparison.OrdinalIgnoreCase)) { - if (null != arrResources && arrResources.Count > 0) + if (arrResources != null && arrResources.Count > 0) { return true; } } + if (path.EndsWith(strPathCheck + WSManStringLiterals.containerInitParameters, StringComparison.OrdinalIgnoreCase)) { if (arrInitParams != null && arrInitParams.Count > 0) @@ -493,6 +493,7 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether it has Chi return true; } } + if (path.EndsWith(strPathCheck + WSManStringLiterals.containerQuotasParameters, StringComparison.OrdinalIgnoreCase)) { XmlNodeList nodeListForQuotas = CurrentPluginXML.GetElementsByTagName(WSManStringLiterals.containerQuotasParameters); @@ -504,6 +505,7 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether it has Chi return false; } + if (arrResources != null) { foreach (PSObject objresource in arrResources) @@ -516,29 +518,31 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether it has Chi { return true; } + strPathCheck = strPathCheck + sResourceDirName + WSManStringLiterals.DefaultPathSeparator; if (path.Contains(strPathCheck + WSManStringLiterals.containerSecurity)) { if (path.EndsWith(strPathCheck + WSManStringLiterals.containerSecurity, StringComparison.OrdinalIgnoreCase)) { - if (null != arrSecurities && arrSecurities.Count > 0) + if (arrSecurities != null && arrSecurities.Count > 0) { return true; } } + strPathCheck = strPathCheck + WSManStringLiterals.containerSecurity + WSManStringLiterals.DefaultPathSeparator; if (path.Contains(strPathCheck + WSManStringLiterals.containerSecurity + "_")) { - if (null == arrSecurities) + if (arrSecurities == null) { return false; } + foreach (PSObject security in arrSecurities) { string sSecurity = security.Properties["SecurityDIR"].Value.ToString(); if (path.EndsWith(sSecurity, StringComparison.OrdinalIgnoreCase)) return true; - } } } @@ -558,31 +562,30 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether it has Chi return IsItemContainer(nodes); } } + return false; } } /// /// This cmdlet is used to get a particular item. - /// cd wsman:\localhost\client> Get-Item .\Auth + /// cd wsman:\localhost\client> Get-Item .\Auth. /// /// [SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")] protected override void GetItem(string path) { - string childname = string.Empty; - if (path.Length == 0 && String.IsNullOrEmpty(childname)) + if (path.Length == 0 && string.IsNullOrEmpty(childname)) { WriteItemObject(GetItemPSObjectWithTypeName(WSManStringLiterals.rootpath, WSManStringLiterals.ContainerChildValue, null, null, null, WsManElementObjectTypes.WSManConfigElement), WSManStringLiterals.rootpath, true); return; } - if (path.Contains(WSManStringLiterals.DefaultPathSeparator.ToString())) { - //Get the ChildName + // Get the ChildName childname = path.Substring(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator) + 1); } else @@ -597,25 +600,29 @@ protected override void GetItem(string path) { WriteItemObject(GetItemPSObjectWithTypeName(childname, WSManStringLiterals.ContainerChildValue, null, null, "ComputerLevel", WsManElementObjectTypes.WSManConfigContainerElement), WSManStringLiterals.rootpath + WSManStringLiterals.DefaultPathSeparator + childname, true); } + return; } path = path.Substring(0, path.LastIndexOf(childname, StringComparison.OrdinalIgnoreCase)); - //Get the wsman host name to find the session object + // Get the wsman host name to find the session object string host = GetHostName(path); string uri = NormalizePath(path, host); - lock(WSManHelper.AutoSession) + lock (WSManHelper.AutoSession) { - //Gets the session object from the cache. + // Gets the session object from the cache. object sessionobj; SessionObjCache.TryGetValue(host, out sessionobj); XmlDocument xmlResource = FindResourceValue(sessionobj, uri, null); - if (xmlResource == null) { return; } + if (xmlResource == null) + { + return; + } - //if endswith '\', removes it. + // if endswith '\', removes it. if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator.ToString(), StringComparison.OrdinalIgnoreCase)) { path = path.Remove(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator)); @@ -650,10 +657,11 @@ protected override void GetItem(string path) strPathChk = strPathChk + WSManStringLiterals.containerPlugin + WSManStringLiterals.DefaultPathSeparator; string filter = uri + "?Name=" + currentpluginname; XmlDocument CurrentPluginXML = GetResourceValue(sessionobj, filter, null); - if (null == CurrentPluginXML) + if (CurrentPluginXML == null) { return; } + PSObject objPluginlevel = ProcessPluginConfigurationLevel(CurrentPluginXML, true); ArrayList arrSecurity = null; ArrayList arrResources = ProcessPluginResourceLevel(CurrentPluginXML, out arrSecurity); @@ -673,13 +681,15 @@ protected override void GetItem(string path) WriteItemObject(GetItemPSObjectWithTypeName(objPluginlevel.Properties[childname].Name, objPluginlevel.Properties[childname].Value.ToString(), null, null, null, WsManElementObjectTypes.WSManConfigLeafElement), path + WSManStringLiterals.DefaultPathSeparator + objPluginlevel.Properties[childname].Name, true); } } + strPathChk = strPathChk + currentpluginname + WSManStringLiterals.DefaultPathSeparator; if (path.Contains(strPathChk + WSManStringLiterals.containerResources)) { - if (null == arrResources) + if (arrResources == null) { return; } + if (path.EndsWith(strPathChk + WSManStringLiterals.containerResources, StringComparison.OrdinalIgnoreCase)) { foreach (PSObject p in arrResources) @@ -689,8 +699,10 @@ protected override void GetItem(string path) WriteItemObject(GetItemPSObjectWithTypeName(childname, WSManStringLiterals.ContainerChildValue, null, new string[] { "ResourceURI=" + p.Properties["ResourceUri"].Value.ToString() }, null, WsManElementObjectTypes.WSManConfigContainerElement), path + WSManStringLiterals.DefaultPathSeparator + childname, true); } } + return; } + strPathChk = strPathChk + WSManStringLiterals.containerResources + WSManStringLiterals.DefaultPathSeparator; int Sepindex = path.IndexOf(WSManStringLiterals.DefaultPathSeparator, strPathChk.Length); string sResourceDirName = string.Empty; @@ -702,6 +714,7 @@ protected override void GetItem(string path) { sResourceDirName = path.Substring(strPathChk.Length, path.IndexOf(WSManStringLiterals.DefaultPathSeparator, strPathChk.Length) - (strPathChk.Length)); } + if (path.Contains(strPathChk + sResourceDirName)) { if (path.EndsWith(strPathChk + sResourceDirName, StringComparison.OrdinalIgnoreCase)) @@ -719,18 +732,22 @@ protected override void GetItem(string path) { WriteItemObject(GetItemPSObjectWithTypeName(p.Properties[childname].Name, p.Properties[childname].TypeNameOfValue, p.Properties[childname].Value, null, null, WsManElementObjectTypes.WSManConfigLeafElement), path + WSManStringLiterals.DefaultPathSeparator + p.Properties[childname].Name, false); } + break; } } + return; } + strPathChk = strPathChk + sResourceDirName + WSManStringLiterals.DefaultPathSeparator; if (path.Contains(strPathChk + WSManStringLiterals.containerSecurity)) { - if (null == arrSecurity) + if (arrSecurity == null) { return; } + foreach (PSObject p in arrSecurity) { if (path.EndsWith(WSManStringLiterals.containerSecurity, StringComparison.OrdinalIgnoreCase)) @@ -748,13 +765,14 @@ protected override void GetItem(string path) } } } + return; } } } else if (path.EndsWith(host + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerPlugin + WSManStringLiterals.DefaultPathSeparator + currentpluginname + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerInitParameters, StringComparison.OrdinalIgnoreCase)) { - if (null != arrInitParams) + if (arrInitParams != null) { foreach (PSObject p in arrInitParams) { @@ -783,8 +801,8 @@ protected override void GetItem(string path) null, WsManElementObjectTypes.WSManConfigLeafElement); - String pathToAdd = - String.Format( + string pathToAdd = + string.Format( CultureInfo.InvariantCulture, "{0}{1}{2}", path, @@ -808,7 +826,7 @@ protected override void GetItem(string path) try { PSObject mshObject = null; - if (!uri.Equals(WinrmRootName[0].ToString(), StringComparison.OrdinalIgnoreCase)) + if (!uri.Equals(WinrmRootName[0], StringComparison.OrdinalIgnoreCase)) { foreach (XmlNode innerResourceNodes in xmlResource.ChildNodes) { @@ -819,6 +837,7 @@ protected override void GetItem(string path) { mshObject = BuildHostLevelPSObjectArrayList(sessionobj, uri, false); } + if (mshObject != null) { if (mshObject.Properties[childname].Value.ToString().Equals(WSManStringLiterals.ContainerChildValue)) @@ -840,7 +859,7 @@ protected override void GetItem(string path) } } } - catch (PSArgumentNullException) { return;/*Leaving this known exception for no value found. Not Throwing error.*/} + catch (PSArgumentNullException) { return; /*Leaving this known exception for no value found. Not Throwing error.*/} catch (NullReferenceException) { return; /*Leaving this known exception for no value found. Not Throwing error.*/} } } @@ -857,24 +876,22 @@ protected override void GetItem(string path) [SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")] protected override void SetItem(string path, object value) { - - if (null == value) + if (value == null) { throw new ArgumentException(helper.GetResourceMsgFromResourcetext("value")); } string ChildName = string.Empty; - if (path.Length == 0 && String.IsNullOrEmpty(ChildName)) + if (path.Length == 0 && string.IsNullOrEmpty(ChildName)) { AssertError(helper.GetResourceMsgFromResourcetext("SetItemNotSupported"), false); return; } - if (path.Contains(WSManStringLiterals.DefaultPathSeparator.ToString())) { - //Get the ChildName + // Get the ChildName ChildName = path.Substring(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator) + 1); } else @@ -904,26 +921,26 @@ protected override void SetItem(string path, object value) // If validation is not required, that means Clear-Item cmdlet is called. // Clear-Item is not allowed on RunAsPassword, Admin should call Clear-Item RunAsUser // if he intends to disable RunAs on the Plugin. - if(String.Equals(ChildName, WSManStringLiterals.ConfigRunAsPasswordName, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(ChildName, WSManStringLiterals.ConfigRunAsPasswordName, StringComparison.OrdinalIgnoreCase)) { AssertError(helper.GetResourceMsgFromResourcetext("ClearItemOnRunAsPassword"), false); return; } } - string whatIfMessage = String.Format(CultureInfo.CurrentUICulture, helper.GetResourceMsgFromResourcetext("SetItemWhatIfAndConfirmText"), path, value); - if (!ShouldProcess(whatIfMessage, "", "")) + string whatIfMessage = string.Format(CultureInfo.CurrentUICulture, helper.GetResourceMsgFromResourcetext("SetItemWhatIfAndConfirmText"), path, value); + if (!ShouldProcess(whatIfMessage, string.Empty, string.Empty)) { return; } path = path.Substring(0, path.LastIndexOf(ChildName, StringComparison.OrdinalIgnoreCase)); - //Get the wsman host name to find the session object + // Get the wsman host name to find the session object string host = GetHostName(path); string uri = NormalizePath(path, host); - //Chk for Winrm Service + // Chk for Winrm Service if (IsPathLocalMachine(host)) { if (!IsWSManServiceRunning()) @@ -935,20 +952,21 @@ protected override void SetItem(string path, object value) bool settingPickedUpDynamically = false; - lock(WSManHelper.AutoSession) + lock (WSManHelper.AutoSession) { - //Gets the session object from the cache. + // Gets the session object from the cache. object sessionobj; Dictionary SessionObjCache = WSManHelper.GetSessionObjCache(); SessionObjCache.TryGetValue(host, out sessionobj); - List warningMessage = new List(); + List warningMessage = new List(); - //if endswith '\', removes it. + // if endswith '\', removes it. if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator.ToString(), StringComparison.OrdinalIgnoreCase)) { path = path.Remove(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator)); } + string strPathChk = host + WSManStringLiterals.DefaultPathSeparator; if (path.Contains(strPathChk + WSManStringLiterals.containerListener)) { @@ -964,20 +982,23 @@ protected override void SetItem(string path, object value) { AssertError(helper.GetResourceMsgFromResourcetext("SetItemNotSupported"), false); } + try { XmlDocument xmlPlugins = FindResourceValue(sessionobj, uri, null); string currentpluginname = string.Empty; GetPluginNames(xmlPlugins, out objPluginNames, out currentpluginname, path); - if (String.IsNullOrEmpty(currentpluginname)) + if (string.IsNullOrEmpty(currentpluginname)) { if (!this.clearItemIsCalled) { // Don't need an error if ClearItem is called. AssertError(helper.GetResourceMsgFromResourcetext("ItemDoesNotExist"), false); } + return; } + string filter = uri + "?Name=" + currentpluginname; CurrentConfigurations pluginConfiguration = new CurrentConfigurations((IWSManSession)sessionobj); @@ -1003,27 +1024,22 @@ protected override void SetItem(string path, object value) strPathChk = strPathChk + WSManStringLiterals.containerPlugin + WSManStringLiterals.DefaultPathSeparator; if (path.EndsWith(strPathChk + currentpluginname, StringComparison.OrdinalIgnoreCase)) { - if (WSManStringLiterals.ConfigRunAsUserName.Equals(ChildName, StringComparison.OrdinalIgnoreCase)) + if (WSManStringLiterals.ConfigRunAsUserName.Equals(ChildName, StringComparison.OrdinalIgnoreCase) && value is PSCredential runAsCredentials) { - PSCredential runAsCredentials = value as PSCredential; - - if (runAsCredentials != null) - { - // UserName - value = runAsCredentials.UserName; + // UserName + value = runAsCredentials.UserName; - pluginConfiguration.UpdateOneConfiguration( - ".", - WSManStringLiterals.ConfigRunAsPasswordName, - this.GetStringFromSecureString(runAsCredentials.Password)); - } + pluginConfiguration.UpdateOneConfiguration( + ".", + WSManStringLiterals.ConfigRunAsPasswordName, + GetStringFromSecureString(runAsCredentials.Password)); } if (WSManStringLiterals.ConfigRunAsPasswordName.Equals(ChildName, StringComparison.OrdinalIgnoreCase)) { - if(String.IsNullOrEmpty( + if (string.IsNullOrEmpty( pluginConfiguration.GetOneConfiguration( - String.Format( + string.Format( CultureInfo.InvariantCulture, "./attribute::{0}", WSManStringLiterals.ConfigRunAsUserName)))) @@ -1032,11 +1048,12 @@ protected override void SetItem(string path, object value) AssertError(helper.GetResourceMsgFromResourcetext("SetItemOnRunAsPasswordNoRunAsUser"), false); } - value = this.GetStringFromSecureString(value); + value = GetStringFromSecureString(value); } pluginConfiguration.UpdateOneConfiguration(".", ChildName, value.ToString()); } + strPathChk = strPathChk + currentpluginname + WSManStringLiterals.DefaultPathSeparator; if (path.Contains(strPathChk + WSManStringLiterals.containerResources)) { @@ -1044,8 +1061,8 @@ protected override void SetItem(string path, object value) { AssertError(helper.GetResourceMsgFromResourcetext("SetItemNotSupported"), false); return; - } + strPathChk = strPathChk + WSManStringLiterals.containerResources + WSManStringLiterals.DefaultPathSeparator; int Sepindex = path.IndexOf(WSManStringLiterals.DefaultPathSeparator, strPathChk.Length); string sResourceDirName = string.Empty; @@ -1057,11 +1074,12 @@ protected override void SetItem(string path, object value) { sResourceDirName = path.Substring(strPathChk.Length, path.IndexOf(WSManStringLiterals.DefaultPathSeparator, strPathChk.Length) - (strPathChk.Length)); } + if (path.Contains(strPathChk + sResourceDirName)) { if (path.EndsWith(strPathChk + sResourceDirName, StringComparison.OrdinalIgnoreCase)) { - if (null == arrResources) + if (arrResources == null) { return; } @@ -1076,7 +1094,7 @@ protected override void SetItem(string path, object value) { if (sResourceDirName.Equals(p.Properties["ResourceDir"].Value.ToString(), StringComparison.OrdinalIgnoreCase)) { - string xpathToUse = String.Format( + string xpathToUse = string.Format( CultureInfo.InvariantCulture, "{0}:{1}/{0}:{2}[attribute::{3}='{4}']", CurrentConfigurations.DefaultNameSpacePrefix, @@ -1097,6 +1115,7 @@ protected override void SetItem(string path, object value) } } } + strPathChk = strPathChk + sResourceDirName + WSManStringLiterals.DefaultPathSeparator; if (path.Contains(strPathChk + WSManStringLiterals.containerSecurity)) { @@ -1106,7 +1125,7 @@ protected override void SetItem(string path, object value) return; } - if (null == arrSecurity) + if (arrSecurity == null) { return; } @@ -1124,15 +1143,15 @@ protected override void SetItem(string path, object value) if (!Force) { string query = helper.GetResourceMsgFromResourcetext("ShouldContinueSecurityQuery"); - query = String.Format(CultureInfo.CurrentCulture, query, currentpluginname); + query = string.Format(CultureInfo.CurrentCulture, query, currentpluginname); if (!ShouldContinue(query, helper.GetResourceMsgFromResourcetext("ShouldContinueSecurityCaption"))) { return; } } - //NameSpace:Resources/NameSpace:Resource[@ResourceUri={''}]/NameSpace:Security[@Uri='{2}'] - string xpathToUse = String.Format( + // NameSpace:Resources/NameSpace:Resource[@ResourceUri={''}]/NameSpace:Security[@Uri='{2}'] + string xpathToUse = string.Format( CultureInfo.InvariantCulture, "{0}:{1}/{0}:{2}[@{6}='{7}']/{0}:{3}[@{4}='{5}']", CurrentConfigurations.DefaultNameSpacePrefix, @@ -1157,7 +1176,7 @@ protected override void SetItem(string path, object value) { if (p.Properties[ChildName] != null) { - string xpathToUse = String.Format( + string xpathToUse = string.Format( CultureInfo.InvariantCulture, "{0}:{1}/{0}:{2}[@{3}='{4}']", CurrentConfigurations.DefaultNameSpacePrefix, @@ -1174,7 +1193,7 @@ protected override void SetItem(string path, object value) } else if (path.EndsWith(strPathChk + WSManStringLiterals.containerQuotasParameters, StringComparison.OrdinalIgnoreCase)) { - string xpathToUse = String.Format( + string xpathToUse = string.Format( CultureInfo.InvariantCulture, "{0}:{1}", CurrentConfigurations.DefaultNameSpacePrefix, @@ -1192,7 +1211,7 @@ protected override void SetItem(string path, object value) } string pathForGlobalQuota = - String.Format( + string.Format( CultureInfo.InvariantCulture, @"{0}:\{1}\{2}\{3}", WSManStringLiterals.rootpath, @@ -1200,12 +1219,12 @@ protected override void SetItem(string path, object value) WSManStringLiterals.containerShell, adjustedChileName); - warningMessage.Add(String.Format(helper.GetResourceMsgFromResourcetext("SetItemWarnigForPPQ"), pathForGlobalQuota)); + warningMessage.Add(string.Format(helper.GetResourceMsgFromResourcetext("SetItemWarnigForPPQ"), pathForGlobalQuota)); } } SessionObjCache.TryGetValue(host, out sessionobj); - string resourceUri = String.Format( + string resourceUri = string.Format( CultureInfo.InvariantCulture, "{0}?Name={1}", uri, @@ -1216,7 +1235,7 @@ protected override void SetItem(string path, object value) pluginConfiguration.PutConfigurationOnServer(resourceUri); // Show Win RM service restart warning only when the changed setting is not picked up dynamically - if (settingPickedUpDynamically == false) + if (!settingPickedUpDynamically) { if (IsPathLocalMachine(host)) { @@ -1224,13 +1243,13 @@ protected override void SetItem(string path, object value) } else { - warningMessage.Add(String.Format(helper.GetResourceMsgFromResourcetext("SetItemServiceRestartWarningRemote"), host)); + warningMessage.Add(string.Format(helper.GetResourceMsgFromResourcetext("SetItemServiceRestartWarningRemote"), host)); } } } finally { - if (!String.IsNullOrEmpty(pluginConfiguration.ServerSession.Error)) + if (!string.IsNullOrEmpty(pluginConfiguration.ServerSession.Error)) { AssertError(pluginConfiguration.ServerSession.Error, true); } @@ -1263,7 +1282,7 @@ protected override void SetItem(string path, object value) { if (!Force) { - string query=""; + string query = string.Empty; string caption = helper.GetResourceMsgFromResourcetext("SetItemGeneralSecurityCaption"); if (ChildName.Equals("TrustedHosts", StringComparison.OrdinalIgnoreCase)) { @@ -1273,23 +1292,25 @@ protected override void SetItem(string path, object value) { query = helper.GetResourceMsgFromResourcetext("SetItemRootSDDLWarningQuery"); } + if (!ShouldContinue(query, caption)) { return; } } - WSManProviderSetItemDynamicParameters dynParams = DynamicParameters as WSManProviderSetItemDynamicParameters; - if (dynParams != null) + + if (DynamicParameters is WSManProviderSetItemDynamicParameters dynParams) { if (dynParams.Concatenate) { - if (!String.IsNullOrEmpty(value.ToString())) + if (!string.IsNullOrEmpty(value.ToString())) { // ',' is used as the delimiter in WSMan for TrustedHosts. value = SplitAndUpdateStringUsingDelimiter(sessionobj, uri, ChildName, value.ToString(), ","); } } } + cmdlinevalue.Add(ChildName, value); } else @@ -1303,9 +1324,10 @@ protected override void SetItem(string path, object value) if (globalWarningUris.Contains(uri) && globalWarningConfigurations.Contains(ChildName.ToLowerInvariant())) { - warningMessage.Add(String.Format(helper.GetResourceMsgFromResourcetext("SetItemWarningForGlobalQuota"), value)); + warningMessage.Add(string.Format(helper.GetResourceMsgFromResourcetext("SetItemWarningForGlobalQuota"), value)); } } + PutResourceValue(sessionobj, uri, cmdlinevalue, host); } catch (COMException e) @@ -1315,7 +1337,7 @@ protected override void SetItem(string path, object value) } } - foreach(String warnings in warningMessage) + foreach (string warnings in warningMessage) { WriteWarning(warnings); } @@ -1334,7 +1356,7 @@ protected override void ClearItem(string path) } /// - /// This is method which create the dynamic or runtime parameter for set-item + /// This is method which create the dynamic or runtime parameter for set-item. /// /// /// @@ -1346,31 +1368,30 @@ protected override object SetItemDynamicParameters(string path, object value) string hostname = GetHostName(path); if (path.EndsWith(hostname + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerClient, StringComparison.OrdinalIgnoreCase)) { - //To have Tab completion. + // To have Tab completion. return new WSManProviderSetItemDynamicParameters(); } else if (path.EndsWith(hostname + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerClient + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerTrustedHosts, StringComparison.OrdinalIgnoreCase)) { - //To Support Concatenate parameter for trustedhosts. + // To Support Concatenate parameter for trustedhosts. return new WSManProviderSetItemDynamicParameters(); } } + return null; } - #endregion #region ContainerCmdletProvider /// /// Gets the Child items. dir functionality - /// wsman:\localhost\client> dir + /// wsman:\localhost\client> dir. /// /// /// protected override void GetChildItems(string path, bool recurse) { - GetChildItemsOrNames(path, ProviderMethods.GetChildItems, recurse); } @@ -1381,7 +1402,6 @@ protected override void GetChildItems(string path, bool recurse) /// protected override void GetChildNames(string path, ReturnContainers returnContainers) { - GetChildItemsOrNames(path, ProviderMethods.GetChildNames, false); } #endregion @@ -1389,22 +1409,21 @@ protected override void GetChildNames(string path, ReturnContainers returnContai #region NavigationalCmdletProvider /// - /// Checks whether the specified path is a container path + /// Checks whether the specified path is a container path. /// /// /// protected override bool IsItemContainer(string path) { + string childname = string.Empty; + string strPathCheck = string.Empty; - string childname = String.Empty; - string strPathCheck = String.Empty; - - if (path.Length == 0 && String.IsNullOrEmpty(childname)) + if (path.Length == 0 && string.IsNullOrEmpty(childname)) { return true; } - //if endswith '\', removes it. + // if endswith '\', removes it. if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator.ToString(), StringComparison.OrdinalIgnoreCase)) { path = path.Remove(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator)); @@ -1412,27 +1431,31 @@ protected override bool IsItemContainer(string path) if (path.Contains(WSManStringLiterals.DefaultPathSeparator.ToString())) { - //Get the ChildName + // Get the ChildName childname = path.Substring(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator) + 1); } + Dictionary SessionObjCache = WSManHelper.GetSessionObjCache(); if (SessionObjCache.ContainsKey(path)) { return true; } - //Get the wsman host name to find the session object + // Get the wsman host name to find the session object string host = GetHostName(path); + if (string.IsNullOrEmpty(host)) + { + return false; + } + string WsManURI = NormalizePath(path, host); - lock(WSManHelper.AutoSession) + lock (WSManHelper.AutoSession) { - //Gets the session object from the cache. + // Gets the session object from the cache. object sessionobj; SessionObjCache.TryGetValue(host, out sessionobj); - - /* WsMan Config Can be divided in to Four Fixed Regions to Check Whether Item is Container @@ -1447,11 +1470,12 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether Item is Co strPathCheck = host + WSManStringLiterals.DefaultPathSeparator; if (WsManURI.Contains(WSManStringLiterals.containerListener)) { - strPathCheck = strPathCheck + WSManStringLiterals.containerListener; + strPathCheck += WSManStringLiterals.containerListener; if (path.EndsWith(strPathCheck, StringComparison.OrdinalIgnoreCase)) { return true; } + XmlDocument xmlListeners = EnumerateResourceValue(sessionobj, WsManURI); if (xmlListeners != null) @@ -1467,11 +1491,12 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether Item is Co // 2. Client Certificate Checks else if (WsManURI.Contains(WSManStringLiterals.containerCertMapping)) { - strPathCheck = strPathCheck + WSManStringLiterals.containerClientCertificate; + strPathCheck += WSManStringLiterals.containerClientCertificate; if (path.EndsWith(strPathCheck, StringComparison.OrdinalIgnoreCase)) { return true; } + XmlDocument xmlCertificates = EnumerateResourceValue(sessionobj, WsManURI); if (xmlCertificates != null) { @@ -1486,24 +1511,24 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether Item is Co // 3. Plugin and its internal structure Checks else if (WsManURI.Contains(WSManStringLiterals.containerPlugin)) { - - strPathCheck = strPathCheck + WSManStringLiterals.containerPlugin; - //Check for Plugin path + strPathCheck += WSManStringLiterals.containerPlugin; + // Check for Plugin path if (path.EndsWith(strPathCheck, StringComparison.OrdinalIgnoreCase)) { return true; } - strPathCheck = strPathCheck + WSManStringLiterals.DefaultPathSeparator; - XmlDocument xmlPlugins = FindResourceValue(sessionobj, WsManURI, null); + strPathCheck += WSManStringLiterals.DefaultPathSeparator; + XmlDocument xmlPlugins = FindResourceValue(sessionobj, WsManURI, null); string currentpluginname = string.Empty; GetPluginNames(xmlPlugins, out objPluginNames, out currentpluginname, path); - strPathCheck = strPathCheck + currentpluginname; + strPathCheck += currentpluginname; if (path.EndsWith(currentpluginname, StringComparison.OrdinalIgnoreCase)) { return true; } + string filter = WsManURI + "?Name=" + currentpluginname; XmlDocument CurrentPluginXML = GetResourceValue(sessionobj, filter, null); ArrayList arrSecurities = null; @@ -1512,24 +1537,28 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether Item is Co { return true; } - strPathCheck = strPathCheck + WSManStringLiterals.DefaultPathSeparator; + + strPathCheck += WSManStringLiterals.DefaultPathSeparator; if (path.EndsWith(strPathCheck + WSManStringLiterals.containerResources, StringComparison.OrdinalIgnoreCase)) { return true; } + if (path.EndsWith(strPathCheck + WSManStringLiterals.containerInitParameters, StringComparison.OrdinalIgnoreCase)) { return true; } + if (path.EndsWith(strPathCheck + WSManStringLiterals.containerQuotasParameters, StringComparison.OrdinalIgnoreCase)) { return true; } - if (null == arrResources || arrResources.Count == 0) + if (arrResources == null || arrResources.Count == 0) { return false; } + foreach (PSObject objresource in arrResources) { string sResourceDirName = objresource.Properties["ResourceDir"].Value.ToString(); @@ -1540,6 +1569,7 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether Item is Co { return true; } + strPathCheck = strPathCheck + sResourceDirName + WSManStringLiterals.DefaultPathSeparator; if (path.Contains(strPathCheck + WSManStringLiterals.containerSecurity)) { @@ -1547,19 +1577,20 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether Item is Co { return true; } + strPathCheck = strPathCheck + WSManStringLiterals.containerSecurity + WSManStringLiterals.DefaultPathSeparator; if (path.Contains(strPathCheck + WSManStringLiterals.containerSecurity + "_")) { - if (null == arrSecurities) + if (arrSecurities == null) { return false; } + foreach (PSObject security in arrSecurities) { string sSecurity = security.Properties["SecurityDIR"].Value.ToString(); if (path.EndsWith(sSecurity, StringComparison.OrdinalIgnoreCase)) return true; - } } } @@ -1576,6 +1607,7 @@ WsMan Config Can be divided in to Four Fixed Regions to Check Whether Item is Co bool result = IsItemContainer(nodes); return result; } + return false; } } @@ -1593,11 +1625,12 @@ protected override void RemoveItem(string path, bool recurse) AssertError(helper.GetResourceMsgFromResourcetext("RemoveItemNotSupported"), false); return; } - String ChildName = String.Empty; + + string ChildName = string.Empty; if (path.Contains(WSManStringLiterals.DefaultPathSeparator.ToString())) { - //Get the ChildName + // Get the ChildName ChildName = path.Substring(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator) + 1); } else @@ -1611,18 +1644,18 @@ protected override void RemoveItem(string path, bool recurse) { AssertError(helper.GetResourceMsgFromResourcetext("LocalHost"), false); } + helper.RemoveFromDictionary(ChildName); return; } path = path.Substring(0, path.LastIndexOf(ChildName, StringComparison.OrdinalIgnoreCase)); - //Get the wsman host name to find the session object + // Get the wsman host name to find the session object string host = GetHostName(path); string uri = NormalizePath(path, host); - - //Chk for Winrm Service + // Chk for Winrm Service if (IsPathLocalMachine(host)) { if (!IsWSManServiceRunning()) @@ -1632,35 +1665,34 @@ protected override void RemoveItem(string path, bool recurse) } } - lock(WSManHelper.AutoSession) + lock (WSManHelper.AutoSession) { - //Gets the session object from the cache. + // Gets the session object from the cache. object sessionobj; Dictionary SessionObjCache = WSManHelper.GetSessionObjCache(); SessionObjCache.TryGetValue(host, out sessionobj); - //if endswith '\', removes it. + // if endswith '\', removes it. if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator.ToString(), StringComparison.OrdinalIgnoreCase)) { path = path.Remove(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator)); } - string inputStr = String.Empty; - string strPathCheck = String.Empty; + string inputStr = string.Empty; + string strPathCheck = string.Empty; strPathCheck = host + WSManStringLiterals.DefaultPathSeparator; - if (path.Contains(strPathCheck + WSManStringLiterals.containerPlugin))//(path.Contains(@"\plugin")) { if (path.EndsWith(strPathCheck + WSManStringLiterals.containerPlugin, StringComparison.OrdinalIgnoreCase)) { - //Deletes all the Plugin when user is in WsMan:\Localhost\Plugin. + // Deletes all the Plugin when user is in WsMan:\Localhost\Plugin. string pluginUri = uri + "?Name=" + ChildName; DeleteResourceValue(sessionobj, pluginUri, null, recurse); return; } - strPathCheck = strPathCheck + WSManStringLiterals.containerPlugin; + strPathCheck += WSManStringLiterals.containerPlugin; int pos = 0; string pName = null; pos = path.LastIndexOf(strPathCheck + WSManStringLiterals.DefaultPathSeparator, StringComparison.OrdinalIgnoreCase) + strPathCheck.Length + 1; @@ -1673,6 +1705,7 @@ protected override void RemoveItem(string path, bool recurse) { pName = path.Substring(pos); } + string filter1 = uri + "?Name=" + pName; XmlDocument pxml = GetResourceValue(sessionobj, filter1, null); @@ -1688,8 +1721,9 @@ protected override void RemoveItem(string path, bool recurse) if (path.EndsWith(strPathCheck + WSManStringLiterals.containerResources, StringComparison.OrdinalIgnoreCase)) { throwerror = false; - ResourceArray = RemoveItemfromResourceArray(ResourceArray, ChildName, "", "ResourceDir"); + ResourceArray = RemoveItemfromResourceArray(ResourceArray, ChildName, string.Empty, "ResourceDir"); } + if (throwerror) { strPathCheck = strPathCheck + WSManStringLiterals.containerResources + WSManStringLiterals.DefaultPathSeparator; @@ -1703,11 +1737,12 @@ protected override void RemoveItem(string path, bool recurse) { sResourceDirName = path.Substring(strPathCheck.Length, path.IndexOf(WSManStringLiterals.DefaultPathSeparator, strPathCheck.Length) - (strPathCheck.Length)); } + strPathCheck = strPathCheck + sResourceDirName + WSManStringLiterals.DefaultPathSeparator; if (path.EndsWith(strPathCheck + WSManStringLiterals.containerSecurity, StringComparison.OrdinalIgnoreCase)) { throwerror = false; - SecurityArray = RemoveItemfromResourceArray(SecurityArray, ChildName, "", "SecurityDIR"); + SecurityArray = RemoveItemfromResourceArray(SecurityArray, ChildName, string.Empty, "SecurityDIR"); } } } @@ -1715,13 +1750,15 @@ protected override void RemoveItem(string path, bool recurse) { // Remove-Item is called for one of the initialization Parameters. throwerror = false; - InitParamArray = RemoveItemfromResourceArray(InitParamArray, ChildName, "InitParams", ""); + InitParamArray = RemoveItemfromResourceArray(InitParamArray, ChildName, "InitParams", string.Empty); } + if (throwerror) { AssertError(helper.GetResourceMsgFromResourcetext("RemoveItemNotSupported"), false); return; } + inputStr = ConstructPluginXml(ps, uri, host, "Set", ResourceArray, SecurityArray, InitParamArray); try { @@ -1729,7 +1766,7 @@ protected override void RemoveItem(string path, bool recurse) } finally { - if (!String.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) + if (!string.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) { AssertError(((IWSManSession)sessionobj).Error, true); } @@ -1745,6 +1782,7 @@ protected override void RemoveItem(string path, bool recurse) throwerror = false; RemoveListenerOrCertMapping(sessionobj, uri, ChildName, PKeyCertMapping, false); } + if (throwerror) { AssertError(helper.GetResourceMsgFromResourcetext("RemoveItemNotSupported"), false); @@ -1761,24 +1799,23 @@ protected override void RemoveItem(string path, bool recurse) /// protected override void NewItem(string path, string itemTypeName, object newItemValue) { - string inputStr = String.Empty; + string inputStr = string.Empty; object sessionobj; - //if endswith '\', removes it. + // if endswith '\', removes it. if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator.ToString(), StringComparison.OrdinalIgnoreCase)) { path = path.Remove(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator)); return; } - if (path.Length == 0 || !path.Contains(WSManStringLiterals.DefaultPathSeparator.ToString())) { NewItemCreateComputerConnection(path); return; } - //Get the wsman host name to find the session object + // Get the wsman host name to find the session object string host = GetHostName(path); if (string.IsNullOrEmpty(host)) @@ -1786,7 +1823,7 @@ protected override void NewItem(string path, string itemTypeName, object newItem throw new ArgumentException(helper.GetResourceMsgFromResourcetext("InvalidPath")); } - //Chk for Winrm Service + // Chk for Winrm Service if (IsPathLocalMachine(host)) { if (!IsWSManServiceRunning()) @@ -1795,14 +1832,14 @@ protected override void NewItem(string path, string itemTypeName, object newItem StartWSManService(this.Force); } } + string uri = NormalizePath(path, host); - lock(WSManHelper.AutoSession) + lock (WSManHelper.AutoSession) { Dictionary SessionObjCache = WSManHelper.GetSessionObjCache(); SessionObjCache.TryGetValue(host, out sessionobj); - string strPathChk = host + WSManStringLiterals.DefaultPathSeparator; if (path.Contains(strPathChk + WSManStringLiterals.containerPlugin))//(path.Contains(@"\plugin")) { @@ -1819,18 +1856,22 @@ protected override void NewItem(string path, string itemTypeName, object newItem { listenerparams.Add("Hostname", niParams.HostName); } + if (niParams.URLPrefix != null) { listenerparams.Add("URLPrefix", niParams.URLPrefix); } + if (niParams.IsPortSpecified) { listenerparams.Add("Port", niParams.Port); } + if (niParams.CertificateThumbPrint != null) { listenerparams.Add("CertificateThumbPrint", niParams.CertificateThumbPrint); } + NewItemContainerListenerOrCertMapping(sessionobj, path, uri, host, listenerparams, WSManStringLiterals.containerListener, helper.GetResourceMsgFromResourcetext("NewItemShouldContinueListenerQuery"), helper.GetResourceMsgFromResourcetext("NewItemShouldContinueListenerCaption")); } else if (path.EndsWith(strPathChk + WSManStringLiterals.containerClientCertificate, StringComparison.OrdinalIgnoreCase)) @@ -1847,6 +1888,7 @@ protected override void NewItem(string path, string itemTypeName, object newItem Certparams.Add("UserName", nwCredentials.UserName); Certparams.Add("Password", nwCredentials.Password); } + Certparams.Add("Enabled", dynParams.Enabled); NewItemContainerListenerOrCertMapping(sessionobj, path, uri, host, Certparams, WSManStringLiterals.containerClientCertificate, helper.GetResourceMsgFromResourcetext("NewItemShouldContinueClientCertQuery"), helper.GetResourceMsgFromResourcetext("NewItemShouldContinueClientCertCaption")); } @@ -1860,7 +1902,7 @@ protected override void NewItem(string path, string itemTypeName, object newItem /// /// Dynamic parameter used by New-Item. According to different path. This method return the - /// required dynamic parameters + /// required dynamic parameters. /// /// /// @@ -1872,32 +1914,39 @@ protected override object NewItemDynamicParameters(string path, string itemTypeN { return new WSManProviderNewItemComputerParameters(); } + string hostname = GetHostName(path); string strpathchk = hostname + WSManStringLiterals.DefaultPathSeparator; if (path.EndsWith(strpathchk + WSManStringLiterals.containerPlugin, StringComparison.OrdinalIgnoreCase)) { return new WSManProviderNewItemPluginParameters(); } + if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerInitParameters, StringComparison.OrdinalIgnoreCase)) { return new WSManProviderInitializeParameters(); } + if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerResources, StringComparison.OrdinalIgnoreCase)) { return new WSManProviderNewItemResourceParameters(); } + if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerSecurity, StringComparison.OrdinalIgnoreCase)) { return new WSManProviderNewItemSecurityParameters(); } + if (path.EndsWith(strpathchk + WSManStringLiterals.containerListener, StringComparison.OrdinalIgnoreCase)) { return new WSManProvidersListenerParameters(); } + if (path.EndsWith(strpathchk + WSManStringLiterals.containerClientCertificate, StringComparison.OrdinalIgnoreCase)) { return new WSManProviderClientCertificateParameters(); } + return null; } @@ -1906,29 +1955,29 @@ protected override object NewItemDynamicParameters(string path, string itemTypeN #region Private /// - /// this method creates the connection to new machine in wsman provider. + /// This method creates the connection to new machine in wsman provider. /// This is called from New-Item. /// /// private void NewItemCreateComputerConnection(string Name) { helper = new WSManHelper(this); - WSManProviderNewItemComputerParameters dynParams = DynamicParameters as WSManProviderNewItemComputerParameters; string parametersetName = "ComputerName"; - if (dynParams != null) + if (DynamicParameters is WSManProviderNewItemComputerParameters dynParams) { if (dynParams.ConnectionURI != null) { parametersetName = "URI"; - } + helper.CreateWsManConnection(parametersetName, dynParams.ConnectionURI, dynParams.Port, Name, dynParams.ApplicationName, dynParams.UseSSL, dynParams.Authentication, dynParams.SessionOption, this.Credential, dynParams.CertificateThumbprint); if (dynParams.ConnectionURI != null) { - string[] constrsplit = dynParams.ConnectionURI.OriginalString.Split(new string[] { ":" + dynParams.Port + "/" + dynParams.ApplicationName }, StringSplitOptions.None); - string[] constrsplit1 = constrsplit[0].Split(new string[] { "//" }, StringSplitOptions.None); + string[] constrsplit = dynParams.ConnectionURI.OriginalString.Split(":" + dynParams.Port + "/" + dynParams.ApplicationName, StringSplitOptions.None); + string[] constrsplit1 = constrsplit[0].Split("//", StringSplitOptions.None); Name = constrsplit1[1].Trim(); } + WriteItemObject(GetItemPSObjectWithTypeName(Name, WSManStringLiterals.ContainerChildValue, null, null, "ComputerLevel", WsManElementObjectTypes.WSManConfigContainerElement), WSManStringLiterals.rootpath + WSManStringLiterals.DefaultPathSeparator + Name, true); } else @@ -1940,7 +1989,7 @@ private void NewItemCreateComputerConnection(string Name) } /// - /// this method creates the Listener or ClientCertificate in wsman provider. + /// This method creates the Listener or ClientCertificate in wsman provider. /// This is called from New-Item. /// /// @@ -1960,6 +2009,7 @@ private void NewItemContainerListenerOrCertMapping(object sessionobj, string pat return; } } + string inputstr = GetInputStringForCreate(uri, InputParams, host); CreateResourceValue(sessionobj, uri, inputstr, InputParams); XmlDocument xmlResource = GetResourceValue(sessionobj, uri, InputParams); @@ -1973,6 +2023,7 @@ private void NewItemContainerListenerOrCertMapping(object sessionobj, string pat { ProcessListenerObjects(xmlResource, out CCache, out kCache); } + if (CCache != null && CCache.Count > 0) { foreach (DictionaryEntry resource in CCache) @@ -1980,11 +2031,10 @@ private void NewItemContainerListenerOrCertMapping(object sessionobj, string pat WriteItemObject(GetItemPSObjectWithTypeName(resource.Key.ToString(), WSManStringLiterals.ContainerChildValue, null, (string[])kCache[resource.Key], string.Empty, WsManElementObjectTypes.WSManConfigContainerElement), path + WSManStringLiterals.DefaultPathSeparator + resource.Key.ToString(), true); } } - } /// - /// this method creates the Plugin and its child items in wsman provider. + /// This method creates the Plugin and its child items in wsman provider. /// This is called from New-Item. /// /// @@ -1995,7 +2045,7 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h { PSObject mshObj = new PSObject(); string PluginName = string.Empty; - string inputStr = String.Empty; + string inputStr = string.Empty; string strPathChk = host + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerPlugin; if (!path.Equals(strPathChk)) @@ -2010,10 +2060,9 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h // to create a new plugin if (path.EndsWith(strPathChk, StringComparison.OrdinalIgnoreCase)) { - WSManProviderNewItemPluginParameters niParams = DynamicParameters as WSManProviderNewItemPluginParameters; - if (niParams != null) + if (DynamicParameters is WSManProviderNewItemPluginParameters niParams) { - if (String.IsNullOrEmpty(niParams.File)) + if (string.IsNullOrEmpty(niParams.File)) { mshObj.Properties.Add(new PSNoteProperty("Name", niParams.Plugin)); mshObj.Properties.Add(new PSNoteProperty("Filename", niParams.FileName)); @@ -2050,6 +2099,7 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h { mshObj.Properties.Add(new PSNoteProperty("XmlRenderingType", "Text")); } + inputStr = ConstructPluginXml(mshObj, uri, host, "New", null, null, null); PluginName = niParams.Plugin; } @@ -2064,6 +2114,7 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h WriteError(er); return; } + string filter = uri + "?Name=" + PluginName; CreateResourceValue(sessionobj, filter, inputStr, null); WriteItemObject(GetItemPSObjectWithTypeName(PluginName, WSManStringLiterals.ContainerChildValue, null, new string[] { "Name=" + PluginName }, string.Empty, WsManElementObjectTypes.WSManConfigContainerElement), path + WSManStringLiterals.DefaultPathSeparator + PluginName, true); @@ -2071,10 +2122,9 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h else {// to create an internal item of as plugin string pName = string.Empty; - string NewItem = String.Empty; + string NewItem = string.Empty; string[] Keys = null; - int pos = path.LastIndexOf(strPathChk + WSManStringLiterals.DefaultPathSeparator, StringComparison.OrdinalIgnoreCase) + strPathChk.Length + 1; int pindex = path.IndexOf(WSManStringLiterals.DefaultPathSeparator, pos); if (pindex != -1) @@ -2094,15 +2144,13 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h ArrayList ResourceArray = ProcessPluginResourceLevel(pxml, out SecurityArray); ArrayList InitParamArray = ProcessPluginInitParamLevel(pxml); - strPathChk = strPathChk + WSManStringLiterals.DefaultPathSeparator + pName + WSManStringLiterals.DefaultPathSeparator; if (path.Contains(strPathChk + WSManStringLiterals.containerResources)) { - strPathChk = strPathChk + WSManStringLiterals.containerResources; + strPathChk += WSManStringLiterals.containerResources; if (path.EndsWith(strPathChk, StringComparison.OrdinalIgnoreCase)) { - WSManProviderNewItemResourceParameters niParams = DynamicParameters as WSManProviderNewItemResourceParameters; - if (niParams != null) + if (DynamicParameters is WSManProviderNewItemResourceParameters niParams) { mshObj.Properties.Add(new PSNoteProperty("Resource", niParams.ResourceUri)); mshObj.Properties.Add(new PSNoteProperty("Capability", niParams.Capability)); @@ -2127,12 +2175,14 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h } } } + int Sepindex = path.IndexOf(WSManStringLiterals.DefaultPathSeparator, strPathChk.Length); string sResourceDirName = string.Empty; if (Sepindex != -1) { sResourceDirName = path.Substring(strPathChk.Length + 1, path.IndexOf(WSManStringLiterals.DefaultPathSeparator, strPathChk.Length + 1) - (strPathChk.Length + 1)); } + strPathChk = strPathChk + WSManStringLiterals.DefaultPathSeparator + sResourceDirName; if (path.EndsWith(strPathChk + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerSecurity, StringComparison.OrdinalIgnoreCase)) @@ -2140,7 +2190,7 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h if (!Force) { string query = helper.GetResourceMsgFromResourcetext("ShouldContinueSecurityQuery"); - query = String.Format(CultureInfo.CurrentCulture, query, pName); + query = string.Format(CultureInfo.CurrentCulture, query, pName); if (!ShouldContinue(query, helper.GetResourceMsgFromResourcetext("ShouldContinueSecurityCaption"))) { return; @@ -2148,7 +2198,7 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h } // Construct the Uri from Resource_XXXX resource dir. PSObject resourceDirProperties = GetItemValue(strPathChk); - if ((null == resourceDirProperties) || (null == resourceDirProperties.Properties["ResourceUri"])) + if ((resourceDirProperties == null) || (resourceDirProperties.Properties["ResourceUri"] == null)) { string message = helper.FormatResourceMsgFromResourcetext("ResourceURIMissingInResourceDir", "ResourceUri", strPathChk); @@ -2179,11 +2229,10 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h SecurityArray = newSecurity; } } - } + if (path.EndsWith(strPathChk + WSManStringLiterals.containerInitParameters, StringComparison.OrdinalIgnoreCase)) { - WSManProviderInitializeParameters niParams = DynamicParameters as WSManProviderInitializeParameters; mshObj.Properties.Add(new PSNoteProperty(niParams.ParamName, niParams.ParamValue)); inputStr = ConstructInitParamsXml(mshObj, null); @@ -2200,10 +2249,11 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h InitParamArray = ProcessPluginInitParamLevel(xdoc); } } + inputStr = ConstructPluginXml(ps, uri, host, "Set", ResourceArray, SecurityArray, InitParamArray); try { - ((IWSManSession)sessionobj).Put(uri + "?" + "Name=" + pName, inputStr.ToString(), 0); + ((IWSManSession)sessionobj).Put(uri + "?" + "Name=" + pName, inputStr, 0); if (path.EndsWith(strPathChk + WSManStringLiterals.containerInitParameters, StringComparison.OrdinalIgnoreCase)) { WriteItemObject(GetItemPSObjectWithTypeName(mshObj.Properties[NewItem].Name, mshObj.Properties[NewItem].TypeNameOfValue, mshObj.Properties[NewItem].Value, null, "InitParams", WsManElementObjectTypes.WSManConfigLeafElement), path + WSManStringLiterals.DefaultPathSeparator + mshObj.Properties[NewItem].Name, false); @@ -2215,7 +2265,7 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h } finally { - if (!String.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) + if (!string.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) { AssertError(((IWSManSession)sessionobj).Error, true); } @@ -2234,7 +2284,7 @@ private void NewItemPluginOrPluginChild(object sessionobj, string path, string h /// /// /// - private PSObject GetItemPSObjectWithTypeName(string Name, string TypeNameOfElement, object Value, string[] keys, string ExtendedTypeName, WsManElementObjectTypes WSManElementObjectType, PSObject input = null) + private static PSObject GetItemPSObjectWithTypeName(string Name, string TypeNameOfElement, object Value, string[] keys, string ExtendedTypeName, WsManElementObjectTypes WSManElementObjectType, PSObject input = null) { PSObject mshObject = null; if (WSManElementObjectType.Equals(WsManElementObjectTypes.WSManConfigElement)) @@ -2242,11 +2292,13 @@ private PSObject GetItemPSObjectWithTypeName(string Name, string TypeNameOfEleme WSManConfigElement element = new WSManConfigElement(Name, TypeNameOfElement); mshObject = new PSObject(element); } + if (WSManElementObjectType.Equals(WsManElementObjectTypes.WSManConfigContainerElement)) { WSManConfigContainerElement element = new WSManConfigContainerElement(Name, TypeNameOfElement, keys); mshObject = new PSObject(element); } + if (WSManElementObjectType.Equals(WsManElementObjectTypes.WSManConfigLeafElement)) { object source = null; @@ -2263,17 +2315,19 @@ private PSObject GetItemPSObjectWithTypeName(string Name, string TypeNameOfEleme WSManConfigLeafElement element = new WSManConfigLeafElement(Name, Value, TypeNameOfElement, source); mshObject = new PSObject(element); } - if (!String.IsNullOrEmpty(ExtendedTypeName)) + + if (!string.IsNullOrEmpty(ExtendedTypeName)) { - StringBuilder types = new StringBuilder(""); + StringBuilder types = new StringBuilder(string.Empty); if (mshObject != null) { types.Append(mshObject.ImmediateBaseObject.GetType().FullName); - types.Append("#"); + types.Append('#'); types.Append(ExtendedTypeName); mshObject.TypeNames.Insert(0, types.ToString()); } } + return mshObject; } @@ -2307,6 +2361,7 @@ private void SetItemListenerOrClientCertificate(object sessionObj, string Resour { ProcessCertMappingObjects(xmlResource, out objcache, out Keyscache); } + if (path.EndsWith(host + WSManStringLiterals.DefaultPathSeparator + parent, StringComparison.OrdinalIgnoreCase)) { AssertError(helper.GetResourceMsgFromResourcetext("SetItemNotSupported"), false); @@ -2320,6 +2375,7 @@ private void SetItemListenerOrClientCertificate(object sessionObj, string Resour return; } } + string item = path.Substring(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator) + 1); try { @@ -2329,6 +2385,7 @@ private void SetItemListenerOrClientCertificate(object sessionObj, string Resour { cmdlinevalue.Add(key, ((PSObject)objcache[item]).Properties[key].Value); } + PutResourceValue(sessionObj, ResourceURI, cmdlinevalue, host); } catch (COMException e) @@ -2341,17 +2398,16 @@ private void SetItemListenerOrClientCertificate(object sessionObj, string Resour } /// - /// Get the input string for create + /// Get the input string for create. /// /// /// /// /// - private string GetInputStringForCreate(string ResourceURI, Hashtable value, string host) + private static string GetInputStringForCreate(string ResourceURI, Hashtable value, string host) { - - string putstr = String.Empty; - string nilns = String.Empty; + string putstr = string.Empty; + string nilns = string.Empty; StringBuilder sbvalues = new StringBuilder(); if (value.Count > 0) @@ -2364,18 +2420,20 @@ private string GetInputStringForCreate(string ResourceURI, Hashtable value, stri sbvalues.Append(key); if (value[key] == null) { - sbvalues.Append(" "); + sbvalues.Append(' '); sbvalues.Append(WSManStringLiterals.ATTR_NIL); nilns = " " + WSManStringLiterals.NS_XSI; } - sbvalues.Append(">"); + + sbvalues.Append('>'); sbvalues.Append(EscapeValuesForXML(((Hashtable)value)[key].ToString())); sbvalues.Append(""); + sbvalues.Append('>'); } } } + string root = GetRootNodeName(ResourceURI); putstr = "" + sbvalues.ToString() + ""; return putstr; @@ -2388,13 +2446,13 @@ private string GetInputStringForCreate(string ResourceURI, Hashtable value, stri /// private string ReadFile(string path) { - string putstr = String.Empty; + string putstr = string.Empty; try { string filePath = this.SessionState.Path.GetUnresolvedProviderPathFromPSPath(path); putstr = File.ReadAllText(filePath, System.Text.Encoding.UTF8); } - //known exceptions + // known exceptions catch (ArgumentNullException e) { ErrorRecord er = new ErrorRecord(e, "ArgumentNullException", ErrorCategory.InvalidOperation, null); @@ -2425,6 +2483,7 @@ private string ReadFile(string path) ErrorRecord er = new ErrorRecord(e, "SecurityException", ErrorCategory.InvalidOperation, null); WriteError(er); } + return putstr; } @@ -2439,42 +2498,45 @@ private string GetHostName(string path) string sHostname = path; try { - //HostName is always followed by root name + // HostName is always followed by root name if (path.Contains(WSManStringLiterals.DefaultPathSeparator.ToString())) { sHostname = path.Substring(0, path.IndexOf(WSManStringLiterals.DefaultPathSeparator)); } + Dictionary SessionObjCache = WSManHelper.GetSessionObjCache(); if (!SessionObjCache.ContainsKey(sHostname)) sHostname = null; - } catch (ArgumentNullException e) { ErrorRecord er = new ErrorRecord(e, "ArgumentNullException", ErrorCategory.InvalidArgument, null); WriteError(er); } + return sHostname; } - private string GetRootNodeName(string ResourceURI) + private static string GetRootNodeName(string ResourceURI) { - string tempuri = ""; - if (ResourceURI.Contains("?")) + string tempuri = string.Empty; + if (ResourceURI.Contains('?')) { - ResourceURI = ResourceURI.Split(new char[] { '?' }).GetValue(0).ToString(); + ResourceURI = ResourceURI.Split('?').GetValue(0).ToString(); } - string PTRN_URI_LAST = "([a-z_][-a-z0-9._]*)$"; + + const string PTRN_URI_LAST = "([a-z_][-a-z0-9._]*)$"; Regex objregex = new Regex(PTRN_URI_LAST, RegexOptions.IgnoreCase); MatchCollection regexmatch = objregex.Matches(ResourceURI); if (regexmatch.Count > 0) { - tempuri = regexmatch[0].Value.ToString(); + tempuri = regexmatch[0].Value; } + return tempuri; } - private string EscapeValuesForXML(string value) + private static string EscapeValuesForXML(string value) { StringBuilder esc_str = new StringBuilder(); for (int i = 0; i <= value.Length - 1; i++) @@ -2513,10 +2575,11 @@ private string EscapeValuesForXML(string value) } } } + return esc_str.ToString(); } - private bool IsItemContainer(XmlNodeList nodes) + private static bool IsItemContainer(XmlNodeList nodes) { bool result = false; if (nodes.Count != 0) @@ -2529,6 +2592,7 @@ private bool IsItemContainer(XmlNodeList nodes) } } } + return result; } @@ -2537,7 +2601,7 @@ private XmlNodeList SearchXml(XmlDocument resourcexmldocument, string searchitem XmlNodeList nodes = null; try { - string xpathString = String.Empty; + string xpathString = string.Empty; if (ResourceURI.EndsWith(WSManStringLiterals.containerWinrs, StringComparison.OrdinalIgnoreCase)) { if (path.Equals(host + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerShell, StringComparison.OrdinalIgnoreCase)) @@ -2545,6 +2609,7 @@ private XmlNodeList SearchXml(XmlDocument resourcexmldocument, string searchitem searchitem = WSManStringLiterals.containerWinrs.ToLowerInvariant(); } } + if (ResourceURI.EndsWith("Config", StringComparison.OrdinalIgnoreCase) || !ResourceURI.EndsWith(searchitem, StringComparison.OrdinalIgnoreCase)) { xpathString = @"/*/*[local-name()=""" + searchitem.ToLowerInvariant() + @"""]"; @@ -2553,20 +2618,21 @@ private XmlNodeList SearchXml(XmlDocument resourcexmldocument, string searchitem { xpathString = @"/*[local-name()=""" + searchitem.ToLowerInvariant() + @"""]"; } - nodes = resourcexmldocument.SelectNodes(xpathString); + nodes = resourcexmldocument.SelectNodes(xpathString); } catch (System.Xml.XPath.XPathException ex) { ErrorRecord er = new ErrorRecord(ex, "XPathException", ErrorCategory.InvalidArgument, null); WriteError(er); } + return nodes; } #region "WsMan linking Operations" /// - /// To put a resource value. Wsman put operation + /// To put a resource value. Wsman put operation. /// /// /// @@ -2586,19 +2652,19 @@ private void PutResourceValue(object sessionobj, string ResourceURI, Hashtable v nsmgr.AddNamespace("cfg", uri_schema); string xpath = SetXPathString(ResourceURI); XmlNodeList nodelist = inputxml.SelectNodes(xpath, nsmgr); - if (nodelist.Count == 1 && nodelist != null) + if (nodelist != null && nodelist.Count == 1) { XmlNode node = (XmlNode)nodelist.Item(0); if (node.HasChildNodes) { for (int i = 0; i < node.ChildNodes.Count; i++) { - if ( (node.ChildNodes[i].ChildNodes.Count == 0) || node.ChildNodes[i].FirstChild.Name.Equals("#text", StringComparison.OrdinalIgnoreCase)) + if ((node.ChildNodes[i].ChildNodes.Count == 0) || node.ChildNodes[i].FirstChild.Name.Equals("#text", StringComparison.OrdinalIgnoreCase)) { foreach (string key in value.Keys) { - //to make sure we dont set values at inner level. - //for eg: when set-item at winrm/config we dont take input at below level + // to make sure we dont set values at inner level. + // for eg: when set-item at winrm/config we dont take input at below level if (!IsPKey(key, ResourceURI)) { if (node.ChildNodes[i].LocalName.Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2610,10 +2676,11 @@ private void PutResourceValue(object sessionobj, string ResourceURI, Hashtable v } } } + if (Itemfound) { ResourceURI = GetURIWithFilter(ResourceURI, value); - ((IWSManSession)sessionobj).Put(ResourceURI, node.OuterXml.ToString(), 0); + ((IWSManSession)sessionobj).Put(ResourceURI, node.OuterXml, 0); } else { @@ -2626,7 +2693,7 @@ private void PutResourceValue(object sessionobj, string ResourceURI, Hashtable v } finally { - if (!String.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) + if (!string.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) { AssertError(((IWSManSession)sessionobj).Error, true); } @@ -2639,7 +2706,7 @@ private string GetResourceValueInXml(object sessionobj, string ResourceURI, Hash { ResourceURI = GetURIWithFilter(ResourceURI, cmdlinevalues); - string returnValue = String.Empty; + string returnValue = string.Empty; if (!this.getMapping.TryGetValue(ResourceURI, out returnValue)) { @@ -2651,7 +2718,7 @@ private string GetResourceValueInXml(object sessionobj, string ResourceURI, Hash } finally { - if (!String.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) + if (!string.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) { AssertError(((IWSManSession)sessionobj).Error, true); } @@ -2677,7 +2744,7 @@ private XmlDocument GetResourceValue(object sessionobj, string ResourceURI, Hash } /// - /// WsMan Enumerate operation + /// WsMan Enumerate operation. /// /// /// @@ -2690,39 +2757,38 @@ private XmlDocument EnumerateResourceValue(object sessionobj, string ResourceURI { try { - object value = ((IWSManSession)sessionobj).Enumerate(ResourceURI, "", "", 0); + object value = ((IWSManSession)sessionobj).Enumerate(ResourceURI, string.Empty, string.Empty, 0); string strXmlValue = string.Empty; while (!((IWSManEnumerator)value).AtEndOfStream) { - strXmlValue = strXmlValue + ((IWSManEnumerator)value).ReadItem(); + strXmlValue += ((IWSManEnumerator)value).ReadItem(); } Marshal.ReleaseComObject(value); - if (!String.IsNullOrEmpty(strXmlValue)) + if (!string.IsNullOrEmpty(strXmlValue)) { xmlEnumResources = new XmlDocument(); strXmlValue = "" + strXmlValue + ""; xmlEnumResources.LoadXml(strXmlValue); this.enumerateMapping.Add(ResourceURI, xmlEnumResources); } - } finally { - if (!String.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) + if (!string.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) { AssertError(((IWSManSession)sessionobj).Error, true); } } } - return xmlEnumResources; + return xmlEnumResources; } /// - /// WsMan Delete Operation + /// WsMan Delete Operation. /// /// /// @@ -2732,20 +2798,20 @@ private void DeleteResourceValue(object sessionobj, string ResourceURI, Hashtabl { try { - //Support only for Listener,plugin and ClientCertificate. + // Support only for Listener,plugin and ClientCertificate. if (ResourceURI.Contains(WSManStringLiterals.containerPlugin) || ResourceURI.Contains(WSManStringLiterals.containerListener) || ResourceURI.Contains(WSManStringLiterals.containerCertMapping)) { if (cmdlinevalues != null) { ResourceURI = GetURIWithFilter(ResourceURI, cmdlinevalues); } - ((IWSManSession)sessionobj).Delete(ResourceURI, 0); + ((IWSManSession)sessionobj).Delete(ResourceURI, 0); } } finally { - if (!String.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) + if (!string.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) { AssertError(((IWSManSession)sessionobj).Error, true); } @@ -2753,7 +2819,7 @@ private void DeleteResourceValue(object sessionobj, string ResourceURI, Hashtabl } /// - /// WsMan Create Operation + /// WsMan Create Operation. /// /// /// @@ -2771,12 +2837,11 @@ private void CreateResourceValue(object sessionobj, string ResourceURI, string r } finally { - if (!String.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) + if (!string.IsNullOrEmpty(((IWSManSession)sessionobj).Error)) { AssertError(((IWSManSession)sessionobj).Error, true); } } - } /// @@ -2791,7 +2856,7 @@ private XmlDocument FindResourceValue(object sessionobj, string ResourceURI, Has XmlDocument outval = null; if (ResourceURI.Contains(WSManStringLiterals.containerListener) || ResourceURI.Contains(WSManStringLiterals.containerPlugin) || ResourceURI.Contains(WSManStringLiterals.containerCertMapping)) { - if (null == cmdlinevalues || cmdlinevalues.Count == 0) + if (cmdlinevalues == null || cmdlinevalues.Count == 0) { outval = EnumerateResourceValue(sessionobj, ResourceURI); } @@ -2804,11 +2869,12 @@ private XmlDocument FindResourceValue(object sessionobj, string ResourceURI, Has { outval = GetResourceValue(sessionobj, ResourceURI, cmdlinevalues); } + return outval; } /// - /// Checks whether a value is present in Wsman config + /// Checks whether a value is present in Wsman config. /// /// /// @@ -2819,31 +2885,35 @@ private XmlDocument FindResourceValue(object sessionobj, string ResourceURI, Has private bool ContainResourceValue(object sessionobj, string ResourceURI, string childname, string path, string host) { bool result = false; - string valuexml = String.Empty; + string valuexml = string.Empty; try { if (ResourceURI.Contains(WSManStringLiterals.containerListener) || ResourceURI.Contains(WSManStringLiterals.containerPlugin) || ResourceURI.Contains(WSManStringLiterals.containerCertMapping)) { - object value = ((IWSManSession)sessionobj).Enumerate(ResourceURI, "", "", 0); + object value = ((IWSManSession)sessionobj).Enumerate(ResourceURI, string.Empty, string.Empty, 0); while (!((IWSManEnumerator)value).AtEndOfStream) { - valuexml = valuexml + ((IWSManEnumerator)value).ReadItem(); + valuexml += ((IWSManEnumerator)value).ReadItem(); } - if ((valuexml != "") && !(String.IsNullOrEmpty(valuexml))) + + if ((valuexml != string.Empty) && !(string.IsNullOrEmpty(valuexml))) { valuexml = "" + valuexml + ""; } + Marshal.ReleaseComObject(value); } else { valuexml = this.GetResourceValueInXml(((IWSManSession)sessionobj), ResourceURI, null); } + if (string.IsNullOrEmpty(valuexml)) { return false; } + XmlDocument xmlResourceValues = new XmlDocument(); xmlResourceValues.LoadXml(valuexml.ToLowerInvariant()); XmlNodeList nodes = SearchXml(xmlResourceValues, childname, ResourceURI, path, host); @@ -2853,36 +2923,38 @@ private bool ContainResourceValue(object sessionobj, string ResourceURI, string } } catch (COMException) { result = false; } + return result; } #endregion "WsMan linking Operations" - private string GetURIWithFilter(string uri, Hashtable cmdlinevalues) + private static string GetURIWithFilter(string uri, Hashtable cmdlinevalues) { StringBuilder sburi = new StringBuilder(uri); if (cmdlinevalues != null) { if (uri.Contains("Config/Listener")) { - sburi.Append("?"); + sburi.Append('?'); sburi.Append(GetFilterString(cmdlinevalues, PKeyListener)); } else if (uri.Contains("Config/Service/certmapping")) { - sburi.Append("?"); + sburi.Append('?'); sburi.Append(GetFilterString(cmdlinevalues, PKeyCertMapping)); } else if (uri.Contains("Config/Plugin")) { - sburi.Append("?"); + sburi.Append('?'); sburi.Append(GetFilterString(cmdlinevalues, PKeyPlugin)); } } + return sburi.ToString(); } - private string GetFilterString(Hashtable cmdlinevalues, string[] pkey) + private static string GetFilterString(Hashtable cmdlinevalues, string[] pkey) { StringBuilder filter = new StringBuilder(); foreach (string key in pkey) @@ -2890,17 +2962,18 @@ private string GetFilterString(Hashtable cmdlinevalues, string[] pkey) if (cmdlinevalues.Contains(key)) { filter.Append(key); - filter.Append("="); + filter.Append('='); filter.Append(cmdlinevalues[key].ToString()); - filter.Append("+"); + filter.Append('+'); } } - if (filter.ToString().EndsWith("+", StringComparison.OrdinalIgnoreCase)) + + if (filter.ToString().EndsWith('+')) filter.Remove(filter.ToString().Length - 1, 1); return filter.ToString(); } - private bool IsPKey(string value, string ResourceURI) + private static bool IsPKey(string value, string ResourceURI) { bool result = false; if (ResourceURI.Contains(WSManStringLiterals.containerListener)) @@ -2915,10 +2988,11 @@ private bool IsPKey(string value, string ResourceURI) { result = CheckPkeysArray(null, value, PKeyCertMapping); } + return result; } - private bool CheckPkeysArray(Hashtable values, string value, string[] pkeys) + private static bool CheckPkeysArray(Hashtable values, string value, string[] pkeys) { bool result = false; if (values != null) @@ -2929,7 +3003,7 @@ private bool CheckPkeysArray(Hashtable values, string value, string[] pkeys) result = true; } } - else if (!String.IsNullOrEmpty(value)) + else if (!string.IsNullOrEmpty(value)) { foreach (string key in pkeys) { @@ -2940,6 +3014,7 @@ private bool CheckPkeysArray(Hashtable values, string value, string[] pkeys) } } } + return result; } @@ -2983,11 +3058,13 @@ private void WritePSObjectPropertiesAsWSManElementObjects(PSObject psobject, str WSManConfigElement element = new WSManConfigElement(prop.Name, prop.Value.ToString()); mshObject = new PSObject(element); } + if (WSManElementObjectType.Equals(WsManElementObjectTypes.WSManConfigContainerElement)) { WSManConfigContainerElement element = new WSManConfigContainerElement(prop.Name, prop.Value.ToString(), keys); mshObject = new PSObject(element); } + if (WSManElementObjectType.Equals(WsManElementObjectTypes.WSManConfigLeafElement)) { string sourceProp = prop.Name + WSManStringLiterals.HiddenSuffixForSourceOfValue; @@ -3006,22 +3083,25 @@ private void WritePSObjectPropertiesAsWSManElementObjects(PSObject psobject, str { element = new WSManConfigLeafElement(prop.Name, null, prop.Value.ToString()); } + if (element != null) { mshObject = new PSObject(element); } } - if (!String.IsNullOrEmpty(ExtendedTypeName)) + + if (!string.IsNullOrEmpty(ExtendedTypeName)) { - StringBuilder types = new StringBuilder(""); + StringBuilder types = new StringBuilder(string.Empty); if (mshObject != null) { types.Append(mshObject.ImmediateBaseObject.GetType().FullName); - types.Append("#"); + types.Append('#'); types.Append(ExtendedTypeName); mshObject.TypeNames.Insert(0, types.ToString()); } } + if (!prop.Value.ToString().Equals(WSManStringLiterals.ContainerChildValue)) { // This path is used by WriteItemObject to construct PSPath. @@ -3049,6 +3129,7 @@ private void WritePSObjectPropertiesAsWSManElementObjects(PSObject psobject, str } } } + if (recurse) { foreach (string dir in directory) @@ -3066,6 +3147,7 @@ private string SplitAndUpdateStringUsingDelimiter(object sessionobj, string uri, { mshObject = ConvertToPSObject(innerResourceNodes); } + string existingvalue = string.Empty; try { @@ -3074,10 +3156,10 @@ private string SplitAndUpdateStringUsingDelimiter(object sessionobj, string uri, existingvalue = mshObject.Properties[childname].Value.ToString(); } - if (!String.IsNullOrEmpty(existingvalue)) + if (!string.IsNullOrEmpty(existingvalue)) { - string[] existingsplitvalues = existingvalue.Split(new string[] { Delimiter }, StringSplitOptions.None); - string[] newvalues = value.Split(new string[] { Delimiter }, StringSplitOptions.None); + string[] existingsplitvalues = existingvalue.Split(Delimiter, StringSplitOptions.None); + string[] newvalues = value.Split(Delimiter, StringSplitOptions.None); foreach (string val in newvalues) { if (Array.IndexOf(existingsplitvalues, val) == -1) @@ -3093,8 +3175,8 @@ private string SplitAndUpdateStringUsingDelimiter(object sessionobj, string uri, } catch (PSArgumentException) { - } + return existingvalue; } @@ -3120,44 +3202,44 @@ private PSObject BuildHostLevelPSObjectArrayList(object objSessionObject, string } else { - if (null != objSessionObject) + if (objSessionObject != null) { XmlDocument ConfigXml = GetResourceValue(objSessionObject, uri, null); - //Moving in to + // Moving in to foreach (XmlNode node in ConfigXml.ChildNodes) { foreach (XmlNode node1 in node.ChildNodes) { - //Getting Top Element in - if ( (node1.ChildNodes.Count == 0) || node1.FirstChild.Name.Equals("#text", StringComparison.OrdinalIgnoreCase)) + // Getting Top Element in + if ((node1.ChildNodes.Count == 0) || node1.FirstChild.Name.Equals("#text", StringComparison.OrdinalIgnoreCase)) { mshobject.Properties.Add(new PSNoteProperty(node1.LocalName, node1.InnerText)); } } } } - //Getting the Fixed root nodes. + // Getting the Fixed root nodes. foreach (string root in WinRmRootConfigs) { mshobject.Properties.Add(new PSNoteProperty(root, WSManStringLiterals.ContainerChildValue)); - } } + return mshobject; } /// - /// Converts XmlNodes ChildNodes to Properties of PSObject + /// Converts XmlNodes ChildNodes to Properties of PSObject. /// /// /// - private PSObject ConvertToPSObject(XmlNode xmlnode) + private static PSObject ConvertToPSObject(XmlNode xmlnode) { PSObject mshObject = new PSObject(); foreach (XmlNode node in xmlnode.ChildNodes) { - //If node contains 0 child-nodes, it is empty node, if it's name = "#text" then it's a simple node. - if ( (node.ChildNodes.Count == 0) || node.FirstChild.Name.Equals("#text", StringComparison.OrdinalIgnoreCase)) + // If node contains 0 child-nodes, it is empty node, if it's name = "#text" then it's a simple node. + if ((node.ChildNodes.Count == 0) || node.FirstChild.Name.Equals("#text", StringComparison.OrdinalIgnoreCase)) { XmlAttribute attrSource = null; foreach (XmlAttribute attr in node.Attributes) @@ -3173,7 +3255,7 @@ private PSObject ConvertToPSObject(XmlNode xmlnode) if (attrSource != null) { - String propName = node.LocalName + WSManStringLiterals.HiddenSuffixForSourceOfValue; + string propName = node.LocalName + WSManStringLiterals.HiddenSuffixForSourceOfValue; mshObject.Properties.Remove(propName); mshObject.Properties.Add(new PSNoteProperty(propName, attrSource.Value)); } @@ -3183,11 +3265,11 @@ private PSObject ConvertToPSObject(XmlNode xmlnode) mshObject.Properties.Add(new PSNoteProperty(node.LocalName, WSManStringLiterals.ContainerChildValue)); } } - return mshObject; + return mshObject; } - private string SetXPathString(string uri) + private static string SetXPathString(string uri) { string parent = uri.Substring(uri.LastIndexOf(WSManStringLiterals.WinrmPathSeparator.ToString(), StringComparison.OrdinalIgnoreCase) + 1); if (parent.Equals(WSManStringLiterals.containerWinrs, StringComparison.OrdinalIgnoreCase)) @@ -3214,13 +3296,14 @@ private string SetXPathString(string uri) { parent = WSManStringLiterals.containerPlugin; } + parent = "/cfg:" + parent; return parent; } - private string SetSchemaPath(string uri) + private static string SetSchemaPath(string uri) { - string schemapath = String.Empty; + string schemapath = string.Empty; uri = uri.Remove(0, WinrmRootName[0].Length); if (uri.Contains(WSManStringLiterals.containerPlugin)) { @@ -3245,9 +3328,9 @@ private string SetSchemaPath(string uri) /// /// /// - private string NormalizePath(string path, string host) + private static string NormalizePath(string path, string host) { - string uri = string.Empty; ; + string uri = string.Empty; if (path.StartsWith(host, StringComparison.OrdinalIgnoreCase)) { if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator.ToString(), StringComparison.OrdinalIgnoreCase)) @@ -3255,7 +3338,7 @@ private string NormalizePath(string path, string host) if (path.Equals(host, StringComparison.OrdinalIgnoreCase)) { - uri = WinrmRootName[0].ToString(); + uri = WinrmRootName[0]; return uri; } @@ -3264,19 +3347,19 @@ private string NormalizePath(string path, string host) string host_prefix = host + WSManStringLiterals.DefaultPathSeparator; if (path.StartsWith(host_prefix + WSManStringLiterals.containerClientCertificate, StringComparison.OrdinalIgnoreCase)) { - uri = WinrmRootName[0].ToString() + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerCertMapping; + uri = WinrmRootName[0] + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerCertMapping; } else if (path.StartsWith(host_prefix + WSManStringLiterals.containerPlugin, StringComparison.OrdinalIgnoreCase)) { - uri = WinrmRootName[0].ToString() + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerPlugin; + uri = WinrmRootName[0] + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerPlugin; } else if (path.StartsWith(host_prefix + WSManStringLiterals.containerShell, StringComparison.OrdinalIgnoreCase)) { - uri = WinrmRootName[0].ToString() + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerWinrs; + uri = WinrmRootName[0] + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerWinrs; } else if (path.StartsWith(host_prefix + WSManStringLiterals.containerListener, StringComparison.OrdinalIgnoreCase)) { - uri = WinrmRootName[0].ToString() + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerListener; + uri = WinrmRootName[0] + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerListener; } else { @@ -3290,10 +3373,13 @@ private string NormalizePath(string path, string host) uri = uri.Remove(index); } } - uri = WinrmRootName[0].ToString() + uri; + + uri = WinrmRootName[0] + uri; } + return uri; } + return uri; } @@ -3303,7 +3389,7 @@ private string NormalizePath(string path, string host) /// /// Currently this supports only retrieving Resource_XXXX dir contents. /// if you need support at other levels implement them. - /// Example resource dir: WSMan:\localhost\Plugin\someplugin\Resources\Resource_XXXXXXX + /// Example resource dir: WSMan:\localhost\Plugin\someplugin\Resources\Resource_XXXXXXX. /// /// /// @@ -3321,38 +3407,35 @@ private string NormalizePath(string path, string host) /// private PSObject GetItemValue(string path) { - if (string.IsNullOrEmpty(path) || (path.Length == 0)) - { - throw new ArgumentNullException(path); - } + ArgumentException.ThrowIfNullOrEmpty(path); - //if endswith '\', removes it. + // if endswith '\', removes it. if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator.ToString(), StringComparison.OrdinalIgnoreCase)) { path = path.Remove(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator)); } - //Get the wsman host name to find the session object + // Get the wsman host name to find the session object string host = GetHostName(path); - if (String.IsNullOrEmpty(host)) + if (string.IsNullOrEmpty(host)) { throw new InvalidOperationException("InvalidPath"); } - //Chks the WinRM Service + // Chks the WinRM Service if (IsPathLocalMachine(host) && (!IsWSManServiceRunning())) { AssertError("WinRMServiceError", false); } - lock(WSManHelper.AutoSession) + lock (WSManHelper.AutoSession) { object sessionobj; - //gets the sessionobject + // gets the sessionobject Dictionary SessionObjCache = WSManHelper.GetSessionObjCache(); SessionObjCache.TryGetValue(host, out sessionobj); - //Normalize to the required uri + // Normalize to the required uri string uri = NormalizePath(path, host); string strPathchk = host + WSManStringLiterals.DefaultPathSeparator; @@ -3382,7 +3465,7 @@ private PSObject GetItemValue(string path) } else if (path.Contains(strPathchk + WSManStringLiterals.containerPlugin)) { - string currentpluginname = String.Empty; + string currentpluginname = string.Empty; GetPluginNames(outxml, out objPluginNames, out currentpluginname, path); if (path.EndsWith(strPathchk + WSManStringLiterals.containerPlugin, StringComparison.OrdinalIgnoreCase)) { @@ -3395,10 +3478,11 @@ private PSObject GetItemValue(string path) // Example resource dir: WSMan:\localhost\Plugin\someplugin\Resources\Resource_67830040 string filter = uri + "?Name=" + currentpluginname; XmlDocument CurrentPluginXML = GetResourceValue(sessionobj, filter, null); - if (null == CurrentPluginXML) + if (CurrentPluginXML == null) { return null; } + PSObject objPluginlevel = ProcessPluginConfigurationLevel(CurrentPluginXML); ArrayList arrSecurity = null; ArrayList arrResources = ProcessPluginResourceLevel(CurrentPluginXML, out arrSecurity); @@ -3408,13 +3492,14 @@ private PSObject GetItemValue(string path) // We support only retrieving Resource_XXX dir properties only. // other directory support can be added as needed. - if (null == arrResources) + if (arrResources == null) { return null; } + strPathchk = strPathchk + WSManStringLiterals.containerResources + WSManStringLiterals.DefaultPathSeparator; int Sepindex = path.IndexOf(WSManStringLiterals.DefaultPathSeparator, strPathchk.Length); - string sResourceDirName = String.Empty; + string sResourceDirName = string.Empty; if (Sepindex == -1) { sResourceDirName = path.Substring(strPathchk.Length); @@ -3439,6 +3524,7 @@ private PSObject GetItemValue(string path) } } } + return null; } @@ -3469,6 +3555,7 @@ private string GetCorrectCaseOfPath(string path) sbPath.Append(GetChildName(tempPath.ToString())); } } + return sbPath.ToString(); } @@ -3477,7 +3564,7 @@ private string GetCorrectCaseOfName(string ChildName, string hostname, string pa string result = ChildName; if (ChildName != null) { - if (!ChildName.Contains("_")) + if (!ChildName.Contains('_')) { if (ChildName.Equals(WSManStringLiterals.containerQuotasParameters, StringComparison.OrdinalIgnoreCase)) result = WSManStringLiterals.containerQuotasParameters; @@ -3507,7 +3594,7 @@ private string GetCorrectCaseOfName(string ChildName, string hostname, string pa result = WSManStringLiterals.containerListener; else { - if (!String.IsNullOrEmpty(hostname)) + if (!string.IsNullOrEmpty(hostname)) { Dictionary SessionObjCache = WSManHelper.GetSessionObjCache(); if (ChildName.Equals(hostname, StringComparison.OrdinalIgnoreCase)) @@ -3526,43 +3613,45 @@ private string GetCorrectCaseOfName(string ChildName, string hostname, string pa StartWSManService(this.Force); } } + string uri = NormalizePath(path, hostname); - lock(WSManHelper.AutoSession) + lock (WSManHelper.AutoSession) { object sessionobj; SessionObjCache.TryGetValue(hostname, out sessionobj); XmlDocument outxml = FindResourceValue(sessionobj, uri, null); if (outxml != null) { - string currentPluginName = String.Empty; - GetPluginNames(outxml, out objPluginNames, out currentPluginName, path); - if (path.EndsWith(hostname + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerPlugin + WSManStringLiterals.DefaultPathSeparator + currentPluginName, StringComparison.OrdinalIgnoreCase)) - { - result = currentPluginName; + string currentPluginName = string.Empty; + GetPluginNames(outxml, out objPluginNames, out currentPluginName, path); + if (path.EndsWith(hostname + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerPlugin + WSManStringLiterals.DefaultPathSeparator + currentPluginName, StringComparison.OrdinalIgnoreCase)) + { + result = currentPluginName; + } } } } } } } - } else { if (ChildName.StartsWith(WSManStringLiterals.containerListener, StringComparison.OrdinalIgnoreCase)) - result = WSManStringLiterals.containerListener + "_" + ChildName.Substring(ChildName.IndexOf('_') + 1); + result = string.Concat(WSManStringLiterals.containerListener, "_", ChildName.AsSpan(ChildName.IndexOf('_') + 1)); if (ChildName.StartsWith(WSManStringLiterals.containerSingleResource, StringComparison.OrdinalIgnoreCase)) - result = WSManStringLiterals.containerSingleResource + "_" + ChildName.Substring(ChildName.IndexOf('_') + 1); + result = string.Concat(WSManStringLiterals.containerSingleResource, "_", ChildName.AsSpan(ChildName.IndexOf('_') + 1)); if (ChildName.StartsWith(WSManStringLiterals.containerSecurity, StringComparison.OrdinalIgnoreCase)) - result = WSManStringLiterals.containerSecurity + "_" + ChildName.Substring(ChildName.IndexOf('_') + 1); + result = string.Concat(WSManStringLiterals.containerSecurity, "_", ChildName.AsSpan(ChildName.IndexOf('_') + 1)); if (ChildName.StartsWith(WSManStringLiterals.containerClientCertificate, StringComparison.OrdinalIgnoreCase)) - result = WSManStringLiterals.containerClientCertificate + "_" + ChildName.Substring(ChildName.IndexOf('_') + 1); + result = string.Concat(WSManStringLiterals.containerClientCertificate, "_", ChildName.AsSpan(ChildName.IndexOf('_') + 1)); } } + return result; } - private ArrayList RemoveItemfromResourceArray(ArrayList resourceArray, string ChildName, string type, string property) + private static ArrayList RemoveItemfromResourceArray(ArrayList resourceArray, string ChildName, string type, string property) { if (resourceArray != null) { @@ -3586,11 +3675,14 @@ private ArrayList RemoveItemfromResourceArray(ArrayList resourceArray, string Ch break; } } + index++; } + if (itemfound) resourceArray.RemoveAt(index); } + return resourceArray; } @@ -3606,7 +3698,7 @@ private ArrayList RemoveItemfromResourceArray(ArrayList resourceArray, string Ch private void GetChildItemOrNamesForListenerOrCertMapping(XmlDocument xmlResource, string ListenerOrCerMapping, string path, string host, ProviderMethods methodname, bool recurse) { Hashtable Objcache, Keyscache; - string PathEnd = String.Empty; + string PathEnd = string.Empty; if (ListenerOrCerMapping.Equals(WSManStringLiterals.containerClientCertificate)) { ProcessCertMappingObjects(xmlResource, out Objcache, out Keyscache); @@ -3617,29 +3709,32 @@ private void GetChildItemOrNamesForListenerOrCertMapping(XmlDocument xmlResource } else { return; } - if (null == Objcache || null == Keyscache) + + if (Objcache == null || Keyscache == null) { return; } + if (path.EndsWith(host + WSManStringLiterals.DefaultPathSeparator + ListenerOrCerMapping, StringComparison.OrdinalIgnoreCase)) { foreach (string key in Keyscache.Keys) { switch (methodname) { - //Get the items at Config level + // Get the items at Config level case ProviderMethods.GetChildItems: PSObject obj = new PSObject(); obj.Properties.Add(new PSNoteProperty(key, WSManStringLiterals.ContainerChildValue)); WritePSObjectPropertiesAsWSManElementObjects(obj, path, (string[])Keyscache[key], null, WsManElementObjectTypes.WSManConfigContainerElement, recurse); - //WriteItemObject(new WSManConfigContainerElement(key, WSManStringLiterals.ContainerChildValue, (string[])Keyscache[key]), path, true); + // WriteItemObject(new WSManConfigContainerElement(key, WSManStringLiterals.ContainerChildValue, (string[])Keyscache[key]), path, true); break; - //Get the names of container at config level + // Get the names of container at config level case ProviderMethods.GetChildNames: WriteItemObject(key, path, true); break; } } + return; } else @@ -3656,9 +3751,9 @@ private void GetChildItemOrNamesForListenerOrCertMapping(XmlDocument xmlResource WriteItemObject(prop.Name, path + WSManStringLiterals.DefaultPathSeparator + prop.Name, false); } } + return; } - } /// @@ -3682,14 +3777,15 @@ private void GetItemListenerOrCertMapping(string path, XmlDocument xmlResource, } else { return; } - if (null == Objcache || null == Keyscache) + + if (Objcache == null || Keyscache == null) { return; } + if (path.EndsWith(host + WSManStringLiterals.DefaultPathSeparator + ContainerListenerOrClientCert, StringComparison.OrdinalIgnoreCase)) { if (Objcache.ContainsKey(childname)) - WriteItemObject(GetItemPSObjectWithTypeName(childname, WSManStringLiterals.ContainerChildValue, null, (string[])Keyscache[childname], null, WsManElementObjectTypes.WSManConfigContainerElement), path + WSManStringLiterals.DefaultPathSeparator + childname, true); } else @@ -3726,6 +3822,7 @@ private void RemoveListenerOrCertMapping(object sessionobj, string WsManUri, str { ProcessListenerObjects(xmlresources, out ResourcesCache, out KeysCache); } + if (KeysCache.Contains(childname)) { PSObject objResource = (PSObject)ResourcesCache[childname]; @@ -3734,14 +3831,14 @@ private void RemoveListenerOrCertMapping(object sessionobj, string WsManUri, str { SelectorParams.Add(pKey, objResource.Properties[pKey].Value); } + DeleteResourceValue(sessionobj, WsManUri, SelectorParams, false); } } } - /// - /// Used By ItemExists, HasChildItem,IsValidPath, IsItemContainer + /// Used By ItemExists, HasChildItem,IsValidPath, IsItemContainer. /// /// /// @@ -3751,12 +3848,12 @@ private bool CheckValidContainerOrPath(string path) { return true; } - //if endswith '\', removes it. + // if endswith '\', removes it. if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator.ToString(), StringComparison.OrdinalIgnoreCase)) path = path.Remove(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator)); - string ChildName = String.Empty; + string ChildName = string.Empty; string strpathChk = string.Empty; if (path.Contains(WSManStringLiterals.DefaultPathSeparator.ToString())) @@ -3768,17 +3865,15 @@ private bool CheckValidContainerOrPath(string path) ChildName = path; } - - //Get the wsman host name to find the session object + // Get the wsman host name to find the session object string host = GetHostName(path); - if (String.IsNullOrEmpty(host)) + if (string.IsNullOrEmpty(host)) { return false; - } - //Chks the WinRM Service + // Chks the WinRM Service if (IsPathLocalMachine(host)) { if (!IsWSManServiceRunning()) @@ -3788,41 +3883,43 @@ private bool CheckValidContainerOrPath(string path) } } - //Get URI to pass to WsMan Automation API + // Get URI to pass to WsMan Automation API string uri = NormalizePath(path, host); - if (String.IsNullOrEmpty(uri)) + if (string.IsNullOrEmpty(uri)) { return false; } - lock(WSManHelper.AutoSession) + lock (WSManHelper.AutoSession) { object sessionobj = null; Dictionary SessionObjCache = WSManHelper.GetSessionObjCache(); SessionObjCache.TryGetValue(host, out sessionobj); strpathChk = host + WSManStringLiterals.DefaultPathSeparator; - //Check for host path + // Check for host path if (path.Equals(host, StringComparison.OrdinalIgnoreCase)) { return true; } + if (path.StartsWith(strpathChk + WSManStringLiterals.containerPlugin, StringComparison.OrdinalIgnoreCase)) { - //Check for Plugin path + // Check for Plugin path if (path.Equals(strpathChk + WSManStringLiterals.containerPlugin, StringComparison.OrdinalIgnoreCase)) { return true; } XmlDocument outxml = FindResourceValue(sessionobj, uri, null); - string currentpluginname = String.Empty; + string currentpluginname = string.Empty; GetPluginNames(outxml, out objPluginNames, out currentpluginname, path); - if (String.IsNullOrEmpty(currentpluginname)) + if (string.IsNullOrEmpty(currentpluginname)) { return false; } + string filter = uri + "?Name=" + currentpluginname; XmlDocument CurrentPluginXML = GetResourceValue(sessionobj, filter, null); PSObject mshPluginLvl = ProcessPluginConfigurationLevel(CurrentPluginXML); @@ -3839,13 +3936,15 @@ private bool CheckValidContainerOrPath(string path) { return true; } + if (path.StartsWith(strpathChk + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerResources, StringComparison.OrdinalIgnoreCase)) { if (path.Equals(strpathChk + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerResources, StringComparison.OrdinalIgnoreCase)) { return true; } - if (null != arrResources) + + if (arrResources != null) { strpathChk = strpathChk + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerResources; foreach (PSObject objresource in arrResources) @@ -3864,17 +3963,20 @@ private bool CheckValidContainerOrPath(string path) return true; } } + if (path.StartsWith(strpathChk + WSManStringLiterals.DefaultPathSeparator + sResourceDirName + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerSecurity, StringComparison.OrdinalIgnoreCase)) { if (path.Equals(strpathChk + WSManStringLiterals.DefaultPathSeparator + sResourceDirName + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerSecurity, StringComparison.OrdinalIgnoreCase)) { return true; } + strpathChk = strpathChk + WSManStringLiterals.DefaultPathSeparator + sResourceDirName + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerSecurity + WSManStringLiterals.DefaultPathSeparator; - if (null == arrSecurities) + if (arrSecurities == null) { return false; } + foreach (PSObject security in arrSecurities) { string sSecurity = security.Properties["SecurityDIR"].Value.ToString(); @@ -3891,7 +3993,6 @@ private bool CheckValidContainerOrPath(string path) } } } - } } } @@ -3905,7 +4006,7 @@ private bool CheckValidContainerOrPath(string path) } else { - if (null != arrInitParams) + if (arrInitParams != null) { foreach (PSObject obj in arrInitParams) { @@ -3937,6 +4038,7 @@ private bool CheckValidContainerOrPath(string path) { return (ContainResourceValue(sessionobj, uri, ChildName, path, host)); } + return false; } } @@ -3948,11 +4050,13 @@ private bool ItemExistListenerOrClientCertificate(object sessionobj, string Reso { return true; } - if (null == outxml) + + if (outxml == null) { return false; } - Hashtable KeysCache=null, objcache=null; + + Hashtable KeysCache = null, objcache = null; if (parentListenerOrCert.Equals(WSManStringLiterals.containerClientCertificate, StringComparison.OrdinalIgnoreCase)) { ProcessCertMappingObjects(outxml, out objcache, out KeysCache); @@ -3962,10 +4066,10 @@ private bool ItemExistListenerOrClientCertificate(object sessionobj, string Reso ProcessListenerObjects(outxml, out objcache, out KeysCache); } - String PathChecked = host + WSManStringLiterals.DefaultPathSeparator + parentListenerOrCert; + string PathChecked = host + WSManStringLiterals.DefaultPathSeparator + parentListenerOrCert; int pos = PathChecked.Length + 1; - String RemainingPath = path.Substring(pos); - String CurrentNode = null; + string RemainingPath = path.Substring(pos); + string CurrentNode = null; pos = RemainingPath.IndexOf(WSManStringLiterals.DefaultPathSeparator); if (pos == -1) { @@ -3976,24 +4080,24 @@ private bool ItemExistListenerOrClientCertificate(object sessionobj, string Reso CurrentNode = RemainingPath.Substring(0, pos); } - if (objcache.Contains(CurrentNode) == false) + if (!objcache.Contains(CurrentNode)) { return false; } if (pos == -1) { - //means the path was only till the CurrentNode. Nothing ahead + // means the path was only till the CurrentNode. Nothing ahead return true; } - //Get the object cache from the listener object + // Get the object cache from the listener object PSObject obj = (PSObject)objcache[CurrentNode]; - CurrentNode = RemainingPath.Substring(pos+1); - if (CurrentNode.IndexOf(WSManStringLiterals.DefaultPathSeparator) != -1) + CurrentNode = RemainingPath.Substring(pos + 1); + if (CurrentNode.Contains(WSManStringLiterals.DefaultPathSeparator)) { - //No more directories allowed after listeners objects + // No more directories allowed after listeners objects return false; } @@ -4001,11 +4105,12 @@ private bool ItemExistListenerOrClientCertificate(object sessionobj, string Reso { return true; } + return false; } /// - /// for the recurse operation of Get-ChildItems. + /// For the recurse operation of Get-ChildItems. /// /// /// @@ -4021,11 +4126,11 @@ private void GetChildItemsRecurse(string path, string childname, ProviderMethods { path = path + WSManStringLiterals.DefaultPathSeparator + childname; } + if (HasChildItems(path)) { GetChildItemsOrNames(path, ProviderMethods.GetChildItems, recurse); } - } /// @@ -4043,7 +4148,7 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool switch (methodname) { case ProviderMethods.GetChildItems: - PSObject obj = BuildHostLevelPSObjectArrayList(null, "", true); + PSObject obj = BuildHostLevelPSObjectArrayList(null, string.Empty, true); WritePSObjectPropertiesAsWSManElementObjects(obj, WSManStringLiterals.rootpath, null, "ComputerLevel", WsManElementObjectTypes.WSManConfigContainerElement, recurse); break; @@ -4052,26 +4157,27 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool { WriteItemObject(hostname, WSManStringLiterals.rootpath, true); } + break; } + return; } - //if endswith '\', removes it. + // if endswith '\', removes it. if (path.EndsWith(WSManStringLiterals.DefaultPathSeparator.ToString(), StringComparison.OrdinalIgnoreCase)) { path = path.Remove(path.LastIndexOf(WSManStringLiterals.DefaultPathSeparator)); } - //Get the wsman host name to find the session object + // Get the wsman host name to find the session object string host = GetHostName(path); - if (String.IsNullOrEmpty(host)) + if (string.IsNullOrEmpty(host)) { throw new InvalidOperationException("InvalidPath"); } - - //Checks the WinRM Service + // Checks the WinRM Service if (IsPathLocalMachine(host)) { if (!IsWSManServiceRunning()) @@ -4087,13 +4193,14 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool } } } - lock(WSManHelper.AutoSession) + + lock (WSManHelper.AutoSession) { object sessionobj; - //gets the sessionobject + // gets the sessionobject SessionObjCache.TryGetValue(host, out sessionobj); - //Normalize to the required uri + // Normalize to the required uri string uri = NormalizePath(path, host); string strPathchk = host + WSManStringLiterals.DefaultPathSeparator; @@ -4103,22 +4210,25 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool PSObject obj = BuildHostLevelPSObjectArrayList(sessionobj, uri, false); switch (methodname) { - //Get the items at Config level + // Get the items at Config level case ProviderMethods.GetChildItems: WritePSObjectPropertiesAsWSManElementObjects(obj, path, null, null, WsManElementObjectTypes.WSManConfigLeafElement, recurse); break; - //Get the names of container at config level + // Get the names of container at config level case ProviderMethods.GetChildNames: WritePSObjectPropertyNames(obj, path); break; } + return; } + XmlDocument outxml = FindResourceValue(sessionobj, uri, null); if (outxml == null || !outxml.HasChildNodes) { return; } + if (path.Contains(strPathchk + WSManStringLiterals.containerListener)) { GetChildItemOrNamesForListenerOrCertMapping(outxml, WSManStringLiterals.containerListener, path, host, methodname, recurse); @@ -4129,37 +4239,40 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool } else if (path.Contains(strPathchk + WSManStringLiterals.containerPlugin)) { - string currentpluginname = String.Empty; + string currentpluginname = string.Empty; GetPluginNames(outxml, out objPluginNames, out currentpluginname, path); if (path.EndsWith(strPathchk + WSManStringLiterals.containerPlugin, StringComparison.OrdinalIgnoreCase)) { switch (methodname) { - //Get the items at Plugin level + // Get the items at Plugin level case ProviderMethods.GetChildItems: foreach (PSPropertyInfo p in objPluginNames.Properties) { PSObject obj = new PSObject(); obj.Properties.Add(new PSNoteProperty(p.Name, p.Value)); WritePSObjectPropertiesAsWSManElementObjects(obj, path, new string[] { "Name=" + p.Name }, null, WsManElementObjectTypes.WSManConfigContainerElement, recurse); - //WriteItemObject(new PSObject(new WSManConfigContainerElement(p.Name, p.Value.ToString(), new string[] { "Name=" + p.Name })), path + WSManStringLiterals.DefaultPathSeparator + p.Name, true); + // WriteItemObject(new PSObject(new WSManConfigContainerElement(p.Name, p.Value.ToString(), new string[] { "Name=" + p.Name })), path + WSManStringLiterals.DefaultPathSeparator + p.Name, true); } + break; - //Get the names of container at Plugin level + // Get the names of container at Plugin level case ProviderMethods.GetChildNames: WritePSObjectPropertyNames(objPluginNames, path); break; } + return; } else { string filter = uri + "?Name=" + currentpluginname; XmlDocument CurrentPluginXML = GetResourceValue(sessionobj, filter, null); - if (null == CurrentPluginXML) + if (CurrentPluginXML == null) { return; } + PSObject objPluginlevel = ProcessPluginConfigurationLevel(CurrentPluginXML, true); ArrayList arrSecurity = null; ArrayList arrResources = ProcessPluginResourceLevel(CurrentPluginXML, out arrSecurity); @@ -4169,19 +4282,19 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool { switch (methodname) { - //Get the items at Plugin level + // Get the items at Plugin level case ProviderMethods.GetChildItems: WritePSObjectPropertiesAsWSManElementObjects(objPluginlevel, path, null, null, WsManElementObjectTypes.WSManConfigLeafElement, recurse); break; - //Get the names of container at Plugin level + // Get the names of container at Plugin level case ProviderMethods.GetChildNames: WritePSObjectPropertyNames(objPluginlevel, path); break; } - return; + return; } - else if(path.EndsWith(WSManStringLiterals.containerQuotasParameters, StringComparison.OrdinalIgnoreCase)) + else if (path.EndsWith(WSManStringLiterals.containerQuotasParameters, StringComparison.OrdinalIgnoreCase)) { // Get the Quotas element from the config XML. XmlNodeList nodeListForQuotas = CurrentPluginXML.GetElementsByTagName(WSManStringLiterals.containerQuotasParameters); @@ -4190,8 +4303,8 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool XmlNode pluginQuotas = nodeListForQuotas[0]; foreach (XmlAttribute attrOfQuotas in pluginQuotas.Attributes) { - String pathToAdd = - String.Format( + string pathToAdd = + string.Format( CultureInfo.InvariantCulture, "{0}{1}{2}", path, @@ -4222,34 +4335,34 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool strPathchk = strPathchk + currentpluginname + WSManStringLiterals.DefaultPathSeparator; if (path.EndsWith(strPathchk + WSManStringLiterals.containerResources, StringComparison.OrdinalIgnoreCase)) { - if (null != arrResources) + if (arrResources != null) { foreach (PSObject p in arrResources) { switch (methodname) { - //Get the items at Plugin level + // Get the items at Plugin level case ProviderMethods.GetChildItems: string[] key = new string[] { "Uri" + WSManStringLiterals.Equalto + p.Properties["ResourceURI"].Value.ToString() }; PSObject obj = new PSObject(); obj.Properties.Add(new PSNoteProperty(p.Properties["ResourceDir"].Value.ToString(), WSManStringLiterals.ContainerChildValue)); WritePSObjectPropertiesAsWSManElementObjects(obj, path, key, null, WsManElementObjectTypes.WSManConfigContainerElement, recurse); - - - //WriteItemObject(new WSManConfigContainerElement(p.Properties["ResourceDir"].Value.ToString(), WSManStringLiterals.ContainerChildValue, key), path + WSManStringLiterals.DefaultPathSeparator + p.Properties["ResourceDir"].Value.ToString(), true); + // WriteItemObject(new WSManConfigContainerElement(p.Properties["ResourceDir"].Value.ToString(), WSManStringLiterals.ContainerChildValue, key), path + WSManStringLiterals.DefaultPathSeparator + p.Properties["ResourceDir"].Value.ToString(), true); break; case ProviderMethods.GetChildNames: WriteItemObject(p.Properties["ResourceDir"].Value.ToString(), path, true); break; } } + return; } } + strPathchk = strPathchk + WSManStringLiterals.containerResources + WSManStringLiterals.DefaultPathSeparator; int Sepindex = path.IndexOf(WSManStringLiterals.DefaultPathSeparator, strPathchk.Length); - string sResourceDirName = String.Empty; + string sResourceDirName = string.Empty; if (Sepindex == -1) { sResourceDirName = path.Substring(strPathchk.Length); @@ -4259,10 +4372,11 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool sResourceDirName = path.Substring(strPathchk.Length, path.IndexOf(WSManStringLiterals.DefaultPathSeparator, strPathchk.Length) - (strPathchk.Length)); } - if (null == arrResources) + if (arrResources == null) { return; } + if (path.Contains(strPathchk + sResourceDirName)) { if (path.EndsWith(strPathchk + sResourceDirName, StringComparison.OrdinalIgnoreCase)) @@ -4274,7 +4388,7 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool p.Properties.Remove("ResourceDir"); switch (methodname) { - //Get the items at Initparams level + // Get the items at Initparams level case ProviderMethods.GetChildItems: WritePSObjectPropertiesAsWSManElementObjects(p, path, null, null, WsManElementObjectTypes.WSManConfigLeafElement, recurse); break; @@ -4284,12 +4398,14 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool } } } + return; } + strPathchk = strPathchk + sResourceDirName + WSManStringLiterals.DefaultPathSeparator; if (path.EndsWith(strPathchk + WSManStringLiterals.containerSecurity, StringComparison.OrdinalIgnoreCase) || path.Contains(WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerSecurity + "_")) { - if (null != arrSecurity) + if (arrSecurity != null) { foreach (PSObject objsecurity in arrSecurity) { @@ -4300,13 +4416,13 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool objsecurity.Properties.Remove("ResourceDir"); switch (methodname) { - //Get the items at Security level + // Get the items at Security level case ProviderMethods.GetChildItems: string key = "Uri" + WSManStringLiterals.Equalto + objsecurity.Properties["Uri"].Value.ToString(); PSObject obj = new PSObject(); obj.Properties.Add(new PSNoteProperty(objsecurity.Properties["SecurityDIR"].Value.ToString(), WSManStringLiterals.ContainerChildValue)); WritePSObjectPropertiesAsWSManElementObjects(obj, path, new string[] { key }, null, WsManElementObjectTypes.WSManConfigContainerElement, recurse); - //WriteItemObject(new WSManConfigContainerElement(objsecurity.Properties["SecurityDIR"].Value.ToString(), WSManStringLiterals.ContainerChildValue, new string[] { key }), path + WSManStringLiterals.DefaultPathSeparator + objsecurity.Properties["SecurityDIR"].Value.ToString(), true); + // WriteItemObject(new WSManConfigContainerElement(objsecurity.Properties["SecurityDIR"].Value.ToString(), WSManStringLiterals.ContainerChildValue, new string[] { key }), path + WSManStringLiterals.DefaultPathSeparator + objsecurity.Properties["SecurityDIR"].Value.ToString(), true); break; case ProviderMethods.GetChildNames: @@ -4323,7 +4439,7 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool objsecurity.Properties.Remove("SecurityDIR"); switch (methodname) { - //Get the items at Security level + // Get the items at Security level case ProviderMethods.GetChildItems: WritePSObjectPropertiesAsWSManElementObjects(objsecurity, path, null, null, WsManElementObjectTypes.WSManConfigLeafElement, recurse); break; @@ -4341,15 +4457,16 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool } else if (path.EndsWith(host + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerPlugin + WSManStringLiterals.DefaultPathSeparator + currentpluginname + WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerInitParameters, StringComparison.OrdinalIgnoreCase)) { - if (null == arrInitParams) + if (arrInitParams == null) { return; } + foreach (PSObject p in arrInitParams) { switch (methodname) { - //Get the items at Initparams level + // Get the items at Initparams level case ProviderMethods.GetChildItems: WritePSObjectPropertiesAsWSManElementObjects(p, path, null, "InitParams", WsManElementObjectTypes.WSManConfigLeafElement, recurse); break; @@ -4359,7 +4476,6 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool } } } - } } else @@ -4394,10 +4510,10 @@ private void GetChildItemsOrNames(string path, ProviderMethods methodname, bool /// /// /// - private int GetPluginNames(XmlDocument xmlPlugins, out PSObject PluginNames, out string CurrentPluginName, string path) + private static int GetPluginNames(XmlDocument xmlPlugins, out PSObject PluginNames, out string CurrentPluginName, string path) { PluginNames = new PSObject(); - CurrentPluginName = String.Empty; + CurrentPluginName = string.Empty; // If the execution is reached this point ... that means the path should for plugins directory (..\Plugins...). if (!path.Contains(WSManStringLiterals.DefaultPathSeparator + WSManStringLiterals.containerPlugin)) @@ -4463,20 +4579,16 @@ private void AssertError(string ErrorMessage, bool IsWSManError) /// private bool IsWSManServiceRunning() { -#if CORECLR - // TODO once s78 comes in undo this - return true; -#else - ServiceController svc = new ServiceController("WinRM"); - if (svc != null) + if (winrmServiceController == null) { - if (svc.Status.Equals(ServiceControllerStatus.Running)) - { - return true; - } + winrmServiceController = new ServiceController("WinRM"); } - return false; -#endif + else + { + winrmServiceController.Refresh(); + } + + return (winrmServiceController.Status.Equals(ServiceControllerStatus.Running)); } /// @@ -4497,7 +4609,7 @@ private void StartWSManService(bool force) } catch (CmdletInvocationException) { - //Eating cmdlet invocation exception. The exception is thrown when No is given. + // Eating cmdlet invocation exception. The exception is thrown when No is given. } } @@ -4506,18 +4618,16 @@ private void StartWSManService(bool force) /// /// /// - private bool IsPathLocalMachine(string host) + private static bool IsPathLocalMachine(string host) { bool hostfound = false; - //Check is Localhost + // Check is Localhost if (host.Equals("localhost", StringComparison.OrdinalIgnoreCase)) { hostfound = true; } - // Domain look up not available on CoreCLR? -#if !CORECLR - //Check is TestMac + // Check is TestMac if (!hostfound) { if (host.Equals(System.Net.Dns.GetHostName(), StringComparison.OrdinalIgnoreCase)) @@ -4526,7 +4636,7 @@ private bool IsPathLocalMachine(string host) } } - //Check is TestMac.redmond.microsoft.corp.com + // Check is TestMac.redmond.microsoft.corp.com if (!hostfound) { System.Net.IPHostEntry hostentry = System.Net.Dns.GetHostEntry("localhost"); @@ -4535,7 +4645,7 @@ private bool IsPathLocalMachine(string host) hostfound = true; } - //Check is 127.0.0.1 or ::1 + // Check is 127.0.0.1 or ::1 if (!hostfound) { foreach (System.Net.IPAddress ipaddress in hostentry.AddressList) @@ -4548,7 +4658,7 @@ private bool IsPathLocalMachine(string host) } } - //check if any IPAddress. + // check if any IPAddress. if (!hostfound) { foreach (System.Net.IPAddress ipaddress in System.Net.Dns.GetHostAddresses(System.Net.Dns.GetHostName())) @@ -4559,16 +4669,16 @@ private bool IsPathLocalMachine(string host) } } } -#endif + return hostfound; } #region Plugin private functions - private void GenerateObjectNameAndKeys(Hashtable InputAttributes, string ResourceURI, string ContainerItem, out string ItemName, out string[] keys) + private static void GenerateObjectNameAndKeys(Hashtable InputAttributes, string ResourceURI, string ContainerItem, out string ItemName, out string[] keys) { StringBuilder sbHashKey = new StringBuilder(); - string keysColumns = String.Empty; + string keysColumns = string.Empty; foreach (DictionaryEntry attribute in InputAttributes) { if (IsPKey(attribute.Key.ToString(), ResourceURI)) @@ -4598,96 +4708,101 @@ private void GenerateObjectNameAndKeys(Hashtable InputAttributes, string Resourc keys = keysColumns.Split('|'); } - - private void ProcessCertMappingObjects(XmlDocument xmlCerts, out Hashtable Certcache, out Hashtable Keyscache) + private static void ProcessCertMappingObjects(XmlDocument xmlCerts, out Hashtable Certcache, out Hashtable Keyscache) { Hashtable lCache = new Hashtable(); Hashtable kCache = new Hashtable(); XmlNodeList xmlnodesCerts = xmlCerts.GetElementsByTagName("cfg:" + "CertMapping"); - if (null == xmlnodesCerts) + if (xmlnodesCerts == null) { Certcache = null; Keyscache = null; return; } + foreach (XmlNode node in xmlnodesCerts) { Hashtable InputAttributes = new Hashtable(); PSObject objCerts = new PSObject(); string[] keys = null; - string ItemName = String.Empty; + string ItemName = string.Empty; foreach (XmlNode childnode in node.ChildNodes) { // if (childnode.LocalName.Equals("URI")) // { - // //sbCerts.Append(childnode.LocalName); - // //sbCerts.Append(WSManStringLiterals.Equalto); - // //sbCerts.Append(childnode.InnerText); - // //keys[0] = childnode.LocalName + WSManStringLiterals.Equalto + childnode.InnerText; + // // sbCerts.Append(childnode.LocalName); + // // sbCerts.Append(WSManStringLiterals.Equalto); + // // sbCerts.Append(childnode.InnerText); + // // keys[0] = childnode.LocalName + WSManStringLiterals.Equalto + childnode.InnerText; // } // else if (childnode.LocalName.Equals("Subject")) // { - // //sbCerts.Append(childnode.LocalName); - // //sbCerts.Append(WSManStringLiterals.Equalto); - // //sbCerts.Append(childnode.InnerText); - // //keys[1] = childnode.LocalName + WSManStringLiterals.Equalto + childnode.InnerText; + // // sbCerts.Append(childnode.LocalName); + // // sbCerts.Append(WSManStringLiterals.Equalto); + // // sbCerts.Append(childnode.InnerText); + // // keys[1] = childnode.LocalName + WSManStringLiterals.Equalto + childnode.InnerText; // } // else if (childnode.LocalName.Equals("Issuer")) // { - // //sbCerts.Append(childnode.LocalName); - // //sbCerts.Append(WSManStringLiterals.Equalto); - // //sbCerts.Append(childnode.InnerText); - // //keys[2] = childnode.LocalName + WSManStringLiterals.Equalto + childnode.InnerText; + // // sbCerts.Append(childnode.LocalName); + // // sbCerts.Append(WSManStringLiterals.Equalto); + // // sbCerts.Append(childnode.InnerText); + // // keys[2] = childnode.LocalName + WSManStringLiterals.Equalto + childnode.InnerText; // } + InputAttributes.Add(childnode.LocalName, childnode.InnerText); objCerts.Properties.Add(new PSNoteProperty(childnode.LocalName, childnode.InnerText)); } + GenerateObjectNameAndKeys(InputAttributes, WSManStringLiterals.containerCertMapping, WSManStringLiterals.containerClientCertificate, out ItemName, out keys); - //lCache.Add(WSManStringLiterals.containerClientCertificate + "_" + Math.Abs(sbCerts.ToString().GetHashCode()), objCerts); + // lCache.Add(WSManStringLiterals.containerClientCertificate + "_" + Math.Abs(sbCerts.ToString().GetHashCode()), objCerts); lCache.Add(ItemName, objCerts); kCache.Add(ItemName, keys); - //kCache.Add(WSManStringLiterals.containerClientCertificate + "_" + Math.Abs(sbCerts.ToString().GetHashCode()), keys); + // kCache.Add(WSManStringLiterals.containerClientCertificate + "_" + Math.Abs(sbCerts.ToString().GetHashCode()), keys); } + Certcache = lCache; Keyscache = kCache; } - private void ProcessListenerObjects(XmlDocument xmlListeners, out Hashtable listenercache, out Hashtable Keyscache) + private static void ProcessListenerObjects(XmlDocument xmlListeners, out Hashtable listenercache, out Hashtable Keyscache) { Hashtable lCache = new Hashtable(); Hashtable kCache = new Hashtable(); XmlNodeList xmlnodesListeners = xmlListeners.GetElementsByTagName("cfg:" + WSManStringLiterals.containerListener); - if (null == xmlnodesListeners) + if (xmlnodesListeners == null) { listenercache = null; Keyscache = null; return; } + foreach (XmlNode node in xmlnodesListeners) { Hashtable InputAttributes = new Hashtable(); PSObject objListener = new PSObject(); string[] Keys = null; - string ItemName = String.Empty; + string ItemName = string.Empty; foreach (XmlNode childnode in node.ChildNodes) { - //if (childnode.LocalName.Equals("Address")) - //{ + // if (childnode.LocalName.Equals("Address")) + // { // sbListener.Append(childnode.LocalName); // sbListener.Append(WSManStringLiterals.Equalto); // sbListener.Append(childnode.InnerText); // Keys[0] = childnode.LocalName + WSManStringLiterals.Equalto + childnode.InnerText; // objListener.Properties.Add(new PSNoteProperty(childnode.LocalName, childnode.InnerText)); - //} - //else if (childnode.LocalName.Equals("Transport")) - //{ + // } + // else if (childnode.LocalName.Equals("Transport")) + // { // sbListener.Append(childnode.LocalName); // sbListener.Append(WSManStringLiterals.Equalto); // sbListener.Append(childnode.InnerText); // Keys[1] = childnode.LocalName + WSManStringLiterals.Equalto + childnode.InnerText; // objListener.Properties.Add(new PSNoteProperty(childnode.LocalName, childnode.InnerText)); - //} + // } + if (childnode.LocalName.Equals("ListeningOn")) { string ListeningOnItem = childnode.LocalName + "_" + Math.Abs(childnode.InnerText.GetHashCode()); @@ -4700,21 +4815,23 @@ private void ProcessListenerObjects(XmlDocument xmlListeners, out Hashtable list objListener.Properties.Add(new PSNoteProperty(childnode.LocalName, childnode.InnerText)); } } + GenerateObjectNameAndKeys(InputAttributes, WSManStringLiterals.containerListener, WSManStringLiterals.containerListener, out ItemName, out Keys); - //lCache.Add(WSManStringLiterals.containerListener + "_" + Math.Abs(sbListener.ToString().GetHashCode()), objListener); + // lCache.Add(WSManStringLiterals.containerListener + "_" + Math.Abs(sbListener.ToString().GetHashCode()), objListener); lCache.Add(ItemName, objListener); kCache.Add(ItemName, Keys); - //kCache.Add(WSManStringLiterals.containerListener + "_" + Math.Abs(sbListener.ToString().GetHashCode()), Keys); + // kCache.Add(WSManStringLiterals.containerListener + "_" + Math.Abs(sbListener.ToString().GetHashCode()), Keys); } + listenercache = lCache; Keyscache = kCache; } - private PSObject ProcessPluginConfigurationLevel(XmlDocument xmldoc, bool setRunasPasswordAsSecureString = false) + private static PSObject ProcessPluginConfigurationLevel(XmlDocument xmldoc, bool setRunasPasswordAsSecureString = false) { PSObject objConfiglvl = null; - if (null != xmldoc) + if (xmldoc != null) { XmlNodeList nodelistPlugin = xmldoc.GetElementsByTagName("PlugInConfiguration"); if (nodelistPlugin.Count > 0) @@ -4723,11 +4840,11 @@ private PSObject ProcessPluginConfigurationLevel(XmlDocument xmldoc, bool setRun XmlAttributeCollection attributecol = nodelistPlugin.Item(0).Attributes; XmlNode runAsUserNode = attributecol.GetNamedItem(WSManStringLiterals.ConfigRunAsUserName); - bool runAsUserPresent = runAsUserNode != null && !String.IsNullOrEmpty(runAsUserNode.Value); + bool runAsUserPresent = runAsUserNode != null && !string.IsNullOrEmpty(runAsUserNode.Value); for (int i = 0; i <= attributecol.Count - 1; i++) { - if (String.Equals(attributecol[i].LocalName, WSManStringLiterals.ConfigRunAsPasswordName, StringComparison.OrdinalIgnoreCase) + if (string.Equals(attributecol[i].LocalName, WSManStringLiterals.ConfigRunAsPasswordName, StringComparison.OrdinalIgnoreCase) && runAsUserPresent && setRunasPasswordAsSecureString) { @@ -4739,7 +4856,7 @@ private PSObject ProcessPluginConfigurationLevel(XmlDocument xmldoc, bool setRun } } } - //Containers in Plugin Level Configs + // Containers in Plugin Level Configs if (objConfiglvl != null) { objConfiglvl.Properties.Add(new PSNoteProperty("InitializationParameters", WSManStringLiterals.ContainerChildValue)); @@ -4751,11 +4868,11 @@ private PSObject ProcessPluginConfigurationLevel(XmlDocument xmldoc, bool setRun return objConfiglvl; } - private ArrayList ProcessPluginResourceLevel(XmlDocument xmldoc, out ArrayList arrSecurity) + private static ArrayList ProcessPluginResourceLevel(XmlDocument xmldoc, out ArrayList arrSecurity) { ArrayList Resources = null; ArrayList nSecurity = null; - if (null != xmldoc) + if (xmldoc != null) { XmlNodeList xmlpluginResource = xmldoc.GetElementsByTagName("Resource"); if (xmlpluginResource.Count > 0) @@ -4769,7 +4886,7 @@ private ArrayList ProcessPluginResourceLevel(XmlDocument xmldoc, out ArrayList a XmlAttributeCollection attributecol = xe.Attributes; bool ExactMatchFound = false; bool SupportsOptionsFound = false; - string resourceUri = String.Empty; + string resourceUri = string.Empty; for (int i = 0; i <= attributecol.Count - 1; i++) { @@ -4778,33 +4895,36 @@ private ArrayList ProcessPluginResourceLevel(XmlDocument xmldoc, out ArrayList a resourceUri = attributecol[i].Value; strUniqueResourceId = "Resource_" + Convert.ToString(Math.Abs(attributecol[i].Value.GetHashCode()), CultureInfo.InvariantCulture); objResource.Properties.Add(new PSNoteProperty("ResourceDir", strUniqueResourceId)); - } + if (attributecol[i].LocalName.Equals("ExactMatch", StringComparison.OrdinalIgnoreCase)) { objResource.Properties.Add(new PSNoteProperty(attributecol[i].LocalName, attributecol[i].Value)); ExactMatchFound = true; continue; } + if (attributecol[i].LocalName.Equals("SupportsOptions", StringComparison.OrdinalIgnoreCase)) { objResource.Properties.Add(new PSNoteProperty(attributecol[i].LocalName, attributecol[i].Value)); SupportsOptionsFound = true; continue; } + objResource.Properties.Add(new PSNoteProperty(attributecol[i].LocalName, attributecol[i].Value)); } + if (!ExactMatchFound) { objResource.Properties.Add(new PSNoteProperty("ExactMatch", false)); } + if (!SupportsOptionsFound) { objResource.Properties.Add(new PSNoteProperty("SupportsOptions", false)); } - - //Processing capabilities + // Processing capabilities XmlDocument xmlCapabilities = new XmlDocument(); xmlCapabilities.LoadXml("" + xe.InnerXml + ""); @@ -4818,25 +4938,26 @@ private ArrayList ProcessPluginResourceLevel(XmlDocument xmldoc, out ArrayList a enumcapability.SetValue(nodeCapabilities[i].Attributes["Type"].Value, i); } } + objResource.Properties.Add(new PSNoteProperty("Capability", enumcapability)); objResource.Properties.Add(new PSNoteProperty(WSManStringLiterals.containerSecurity, WSManStringLiterals.ContainerChildValue)); - //Process Security in Resources. We add the resource Unique ID in to each security to - //identify in the Provider methods. + // Process Security in Resources. We add the resource Unique ID in to each security to + // identify in the Provider methods. nSecurity = ProcessPluginSecurityLevel(nSecurity, xmlCapabilities, strUniqueResourceId, resourceUri); Resources.Add(objResource); } - } } + arrSecurity = nSecurity; return Resources; } - private ArrayList ProcessPluginInitParamLevel(XmlDocument xmldoc) + private static ArrayList ProcessPluginInitParamLevel(XmlDocument xmldoc) { ArrayList InitParamLvl = null; - if (null != xmldoc) + if (xmldoc != null) { XmlNodeList nodelistInitParam = xmldoc.GetElementsByTagName("Param"); if (nodelistInitParam.Count > 0) @@ -4846,36 +4967,32 @@ private ArrayList ProcessPluginInitParamLevel(XmlDocument xmldoc) { PSObject objInitParam = new PSObject(); XmlAttributeCollection attributecol = xe.Attributes; - String Name = String.Empty; - String Value = String.Empty; + string Name = string.Empty; + string Value = string.Empty; for (int i = 0; i <= attributecol.Count - 1; i++) { if (attributecol[i].LocalName.Equals("Name", StringComparison.OrdinalIgnoreCase)) { Name = attributecol[i].Value; } + if (attributecol[i].LocalName.Equals("Value", StringComparison.OrdinalIgnoreCase)) { - String ValueAsXML = attributecol[i].Value; -#if CORECLR - //SecurityElement.Escape() not supported on .NET Core, use WebUtility.HtmlEncode() to replace. - //During the encoding, single quote "\'" convert to "'", then manually convert "'" to "'" since we are encode xml not html; - Value = System.Net.WebUtility.HtmlEncode(ValueAsXML); - Value = Value.Replace("'", "'"); -#else + string ValueAsXML = attributecol[i].Value; Value = SecurityElement.Escape(ValueAsXML); -#endif } } + objInitParam.Properties.Add(new PSNoteProperty(Name, Value)); InitParamLvl.Add(objInitParam); } } } + return InitParamLvl; } - private ArrayList ProcessPluginSecurityLevel(ArrayList arrSecurity, XmlDocument xmlSecurity, string UniqueResourceID, string ParentResourceUri) + private static ArrayList ProcessPluginSecurityLevel(ArrayList arrSecurity, XmlDocument xmlSecurity, string UniqueResourceID, string ParentResourceUri) { // ArrayList SecurityLvl = null; if (xmlSecurity != null) @@ -4883,7 +5000,7 @@ private ArrayList ProcessPluginSecurityLevel(ArrayList arrSecurity, XmlDocument XmlNodeList nodelistSecurity = xmlSecurity.GetElementsByTagName(WSManStringLiterals.containerSecurity); if (nodelistSecurity.Count > 0) { - //SecurityLvl = new ArrayList(); + // SecurityLvl = new ArrayList(); foreach (XmlElement xe in nodelistSecurity) { bool ExactMatchFound = false; @@ -4896,18 +5013,22 @@ private ArrayList ProcessPluginSecurityLevel(ArrayList arrSecurity, XmlDocument { objSecurity.Properties.Add(new PSNoteProperty("SecurityDIR", "Security_" + Math.Abs(UniqueResourceID.GetHashCode()))); } + if (attributecol[i].LocalName.Equals("ExactMatch", StringComparison.OrdinalIgnoreCase)) { objSecurity.Properties.Add(new PSNoteProperty(attributecol[i].LocalName, attributecol[i].Value)); ExactMatchFound = true; continue; } + objSecurity.Properties.Add(new PSNoteProperty(attributecol[i].LocalName, attributecol[i].Value)); } + if (!ExactMatchFound) { objSecurity.Properties.Add(new PSNoteProperty("ExactMatch", false)); } + objSecurity.Properties.Add(new PSNoteProperty("ResourceDir", UniqueResourceID)); objSecurity.Properties.Add(new PSNoteProperty("ParentResourceUri", ParentResourceUri)); @@ -4915,6 +5036,7 @@ private ArrayList ProcessPluginSecurityLevel(ArrayList arrSecurity, XmlDocument } } } + return arrSecurity; } @@ -4928,18 +5050,17 @@ private ArrayList ProcessPluginSecurityLevel(ArrayList arrSecurity, XmlDocument /// Resource URI for the XML. /// Name of the Host. /// Type of Operation. - ///List of Resources. - ///List of Securities - ///List of initialization parameters. + /// List of Resources. + /// List of Securities + /// List of initialization parameters. /// An Configuration XML, ready to send to server. - private string ConstructPluginXml(PSObject objinputparam, string ResourceURI, string host, string Operation, ArrayList resources, ArrayList securities, ArrayList initParams) + private static string ConstructPluginXml(PSObject objinputparam, string ResourceURI, string host, string Operation, ArrayList resources, ArrayList securities, ArrayList initParams) { - StringBuilder sbvalues = new StringBuilder(); sbvalues.Append(""); return sbvalues.ToString(); } @@ -5007,6 +5129,11 @@ private string ConstructPluginXml(PSObject objinputparam, string ResourceURI, st private object ValidateAndGetUserObject(string configurationName, object value) { PSObject basePsObject = value as PSObject; + PSCredential psCredential = null; + if (basePsObject == null) + { + psCredential = value as PSCredential; + } if (configurationName.Equals(WSManStringLiterals.ConfigRunAsPasswordName, StringComparison.OrdinalIgnoreCase)) { @@ -5016,7 +5143,7 @@ private object ValidateAndGetUserObject(string configurationName, object value) } else { - string error = String.Format( + string error = string.Format( helper.GetResourceMsgFromResourcetext("InvalidValueType"), WSManStringLiterals.ConfigRunAsPasswordName, typeof(SecureString).FullName); @@ -5031,9 +5158,13 @@ private object ValidateAndGetUserObject(string configurationName, object value) { return basePsObject.BaseObject as PSCredential; } + else if (psCredential != null) + { + return psCredential; + } else { - string error = String.Format( + string error = string.Format( helper.GetResourceMsgFromResourcetext("InvalidValueType"), WSManStringLiterals.ConfigRunAsUserName, typeof(PSCredential).FullName); @@ -5050,38 +5181,29 @@ private object ValidateAndGetUserObject(string configurationName, object value) /// Appends the plain text value of a SecureString variable to the StringBuilder. /// if the propertyValue provided is not SecureString appends empty string. /// - /// Value to append - private string GetStringFromSecureString(object propertyValue) + /// Value to append. + private static string GetStringFromSecureString(object propertyValue) { - SecureString value = propertyValue as SecureString; - string passwordValueToAdd = String.Empty; + string passwordValueToAdd = string.Empty; - if (value != null) + if (propertyValue is SecureString value) { -#if !CORECLR - //coreCLR only supports marshal for unicode IntPtr ptr = Marshal.SecureStringToBSTR(value); passwordValueToAdd = Marshal.PtrToStringAuto(ptr); Marshal.ZeroFreeBSTR(ptr); -#else - IntPtr ptr = SecureStringMarshal.SecureStringToCoTaskMemUnicode(value); - passwordValueToAdd = Marshal.PtrToStringUni(ptr); - Marshal.ZeroFreeCoTaskMemAnsi(ptr); -#endif - - } return passwordValueToAdd; } - private string ConstructResourceXml(PSObject objinputparams, ArrayList resources, ArrayList securities) + private static string ConstructResourceXml(PSObject objinputparams, ArrayList resources, ArrayList securities) { - StringBuilder sbvalues = new StringBuilder(""); + StringBuilder sbvalues = new StringBuilder(string.Empty); if (objinputparams == null && resources == null) { return sbvalues.ToString(); } + object[] capability = null; sbvalues.Append(""); if (objinputparams != null) @@ -5108,9 +5230,10 @@ private string ConstructResourceXml(PSObject objinputparams, ArrayList resources } } } + sbvalues.Append(WSManStringLiterals.GreaterThan); if (securities != null) - sbvalues.Append(ConstructSecurityXml(null, securities, String.Empty)); + sbvalues.Append(ConstructSecurityXml(null, securities, string.Empty)); sbvalues.Append(ConstructCapabilityXml(capability)); sbvalues.Append(""); } @@ -5147,6 +5270,7 @@ private string ConstructResourceXml(PSObject objinputparams, ArrayList resources } } } + sbvalues.Append(WSManStringLiterals.GreaterThan); if (securities != null) sbvalues.Append(ConstructSecurityXml(null, securities, p.Properties["ResourceDir"].Value.ToString())); @@ -5154,18 +5278,20 @@ private string ConstructResourceXml(PSObject objinputparams, ArrayList resources sbvalues.Append(""); } } + sbvalues.Append(""); return sbvalues.ToString(); } - private string ConstructSecurityXml(PSObject objinputparams, ArrayList securities, string strResourceIdentity) + private static string ConstructSecurityXml(PSObject objinputparams, ArrayList securities, string strResourceIdentity) { - // - StringBuilder sbvalues = new StringBuilder(""); + // + StringBuilder sbvalues = new StringBuilder(string.Empty); if (objinputparams == null && securities == null) { return sbvalues.ToString(); } + if (objinputparams != null) { AddSecurityProperties(objinputparams.Properties, sbvalues); @@ -5180,10 +5306,11 @@ private string ConstructSecurityXml(PSObject objinputparams, ArrayList securitie } } } + return sbvalues.ToString(); } - private void AddSecurityProperties( + private static void AddSecurityProperties( PSMemberInfoCollection properties, StringBuilder sbValues) { @@ -5203,20 +5330,22 @@ private void AddSecurityProperties( sbValues.Append(WSManStringLiterals.EnclosingDoubleQuotes + propValueStr + WSManStringLiterals.EnclosingDoubleQuotes); } } + sbValues.Append(WSManStringLiterals.GreaterThan); sbValues.Append(""); } - private string ConstructInitParamsXml(PSObject objinputparams, ArrayList initparams) + private static string ConstructInitParamsXml(PSObject objinputparams, ArrayList initparams) { // - // - // - StringBuilder sbvalues = new StringBuilder(""); + // + // + StringBuilder sbvalues = new StringBuilder(string.Empty); if (objinputparams == null && initparams == null) { return sbvalues.ToString(); } + sbvalues.Append(""); if (objinputparams != null) { @@ -5253,13 +5382,14 @@ private string ConstructInitParamsXml(PSObject objinputparams, ArrayList initpar } } } + sbvalues.Append(""); return sbvalues.ToString(); } - private string ConstructCapabilityXml(object[] capabilities) + private static string ConstructCapabilityXml(object[] capabilities) { - StringBuilder sbvalues = new StringBuilder(""); + StringBuilder sbvalues = new StringBuilder(string.Empty); foreach (object cap in capabilities) { sbvalues.Append(""); } + return sbvalues.ToString(); } - private bool IsValueOfParamList(string name, string[] paramcontainer) + private static bool IsValueOfParamList(string name, string[] paramcontainer) { bool result = false; foreach (string value in paramcontainer) @@ -5283,27 +5414,28 @@ private bool IsValueOfParamList(string name, string[] paramcontainer) break; } } + return result; } -#endregion Plugin private functions + #endregion Plugin private functions - enum ProviderMethods + private enum ProviderMethods { GetChildItems, GetChildNames + } - }; - - enum WsManElementObjectTypes + private enum WsManElementObjectTypes { WSManConfigElement, WSManConfigContainerElement, WSManConfigLeafElement - }; + } -#region def + #region def private static readonly string[] WinrmRootName = new string[] { "winrm/Config" }; + private static readonly string[] WinRmRootConfigs = new string[] { "Client", "Service", @@ -5311,10 +5443,9 @@ enum WsManElementObjectTypes "Listener", "Plugin", "ClientCertificate" +}; - }; - - //Defining Primarykeys for resource uri's + // Defining Primarykeys for resource uri's private static readonly string[] PKeyListener = new string[] { "Address", "Transport" }; private static readonly string[] PKeyPlugin = new string[] { "Name" }; private static readonly string[] PKeyCertMapping = new string[] { "Issuer", "Subject", "Uri" }; @@ -5349,18 +5480,17 @@ enum WsManElementObjectTypes /// private static readonly List globalWarningUris = new List { - WinrmRootName[0].ToString() + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerWinrs, - WinrmRootName[0].ToString() + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerService}; + WinrmRootName[0] + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerWinrs, + WinrmRootName[0] + WSManStringLiterals.WinrmPathSeparator + WSManStringLiterals.containerService}; -#endregion def - -#endregion private + #endregion def + #endregion private } -#region "Dynamic Parameter Classes" + #region "Dynamic Parameter Classes" -#region "New-Item Dynamic Parameters" + #region "New-Item Dynamic Parameters" /// /// Computer dynamic parameters. This is similar to connect-wsman parameters. @@ -5368,7 +5498,6 @@ enum WsManElementObjectTypes /// public class WSManProviderNewItemComputerParameters { - /// /// The following is the definition of the input parameter "OptionSet". /// OptionSet is a hash table and is used to pass a set of switches to the @@ -5381,16 +5510,17 @@ public class WSManProviderNewItemComputerParameters public Hashtable OptionSet { get { return optionset; } + set { optionset = value; } } - private Hashtable optionset; + private Hashtable optionset; /// /// 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. @@ -5406,8 +5536,10 @@ public Hashtable OptionSet public AuthenticationMechanism Authentication { get { return authentication; } + set { authentication = value; } } + private AuthenticationMechanism authentication = AuthenticationMechanism.Default; /// @@ -5419,14 +5551,16 @@ public AuthenticationMechanism Authentication public string CertificateThumbprint { get { return thumbPrint; } + set { thumbPrint = value; } } + private string thumbPrint = null; /// /// 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] @@ -5435,8 +5569,10 @@ public string CertificateThumbprint public SessionOption SessionOption { get { return sessionoption; } + set { sessionoption = value; } } + private SessionOption sessionoption; /// @@ -5445,12 +5581,14 @@ public SessionOption SessionOption /// [Parameter(ParameterSetName = "nameSet")] [ValidateNotNullOrEmpty] - public String ApplicationName + public string ApplicationName { get { return applicationname; } + set { applicationname = value; } } - private String applicationname = "wsman"; + + private string applicationname = "wsman"; /// /// The following is the definition of the input parameter "Port". @@ -5459,13 +5597,15 @@ public String ApplicationName [Parameter] [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = "nameSet")] - [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". @@ -5478,8 +5618,10 @@ public Int32 Port public SwitchParameter UseSSL { get { return usessl; } + set { usessl = value; } } + private SwitchParameter usessl; /// @@ -5494,10 +5636,11 @@ public SwitchParameter UseSSL public Uri ConnectionURI { get { return connectionuri; } + set { connectionuri = value; } } - private Uri connectionuri; + private Uri connectionuri; } /// @@ -5516,8 +5659,10 @@ public class WSManProviderNewItemPluginParameters public string Plugin { get { return _plugin; } + set { _plugin = value; } } + private string _plugin; /// @@ -5528,8 +5673,10 @@ public string Plugin public string FileName { get { return _filename; } + set { _filename = value; } } + private string _filename; /// @@ -5541,8 +5688,10 @@ public string FileName public string SDKVersion { get { return _sdkversion; } + set { _sdkversion = value; } } + private string _sdkversion; /// @@ -5553,8 +5702,10 @@ public string SDKVersion public System.Uri Resource { get { return _resourceuri; } + set { _resourceuri = value; } } + private System.Uri _resourceuri; /// @@ -5566,8 +5717,10 @@ public System.Uri Resource public object[] Capability { get { return _capability; } + set { _capability = value; } } + private object[] _capability; /// @@ -5579,8 +5732,10 @@ public object[] Capability public string XMLRenderingType { get { return _xmlRenderingtype; } + set { _xmlRenderingtype = value; } } + private string _xmlRenderingtype; /// @@ -5591,59 +5746,69 @@ public string XMLRenderingType public string File { get { return _file; } + set { _file = value; } } + private string _file; /// /// Parameter for RunAs credentials for a Plugin. /// [ValidateNotNull] - [Parameter()] + [Parameter] public PSCredential RunAsCredential { - get { return this.runAsCredentials; } + get { return this.runAsCredentials; } + set { this.runAsCredentials = value; } } + private PSCredential runAsCredentials; /// /// Parameter for Plugin Host Process configuration (Shared or Separate). /// - [Parameter()] + [Parameter] public SwitchParameter UseSharedProcess { get { return this.sharedHost; } + set { this.sharedHost = value; } } + private bool sharedHost; /// /// Parameter for Auto Restart configuration for Plugin. /// - [Parameter()] + [Parameter] public SwitchParameter AutoRestart { get { return this.autoRestart; } + set { this.autoRestart = value; } } + private bool autoRestart; /// /// Parameter for Idle timeout for HostProcess. /// - [Parameter()] + [Parameter] public uint? ProcessIdleTimeoutSec { get { return this.processIdleTimeoutSeconds; } + set { this.processIdleTimeoutSeconds = value; } } + private uint? processIdleTimeoutSeconds; } @@ -5653,7 +5818,6 @@ public uint? ProcessIdleTimeoutSec /// public class WSManProviderInitializeParameters { - /// /// Parameter ParamName. /// @@ -5663,8 +5827,10 @@ public class WSManProviderInitializeParameters public string ParamName { get { return _paramname; } + set { _paramname = value; } } + private string _paramname; /// @@ -5676,8 +5842,10 @@ public string ParamName public string ParamValue { get { return _paramvalue; } + set { _paramvalue = value; } } + private string _paramvalue; } @@ -5695,8 +5863,10 @@ public class WSManProviderNewItemResourceParameters public System.Uri ResourceUri { get { return _resourceuri; } + set { _resourceuri = value; } } + private System.Uri _resourceuri; /// @@ -5708,8 +5878,10 @@ public System.Uri ResourceUri public object[] Capability { get { return _capability; } + set { _capability = value; } } + private object[] _capability; } @@ -5727,15 +5899,17 @@ public class WSManProviderNewItemSecurityParameters public string Sddl { get { return _sddl; } + set { _sddl = value; } } + private string _sddl; } -#region "ClientCertificate Dynamic Parameters" + #region "ClientCertificate Dynamic Parameters" /// /// Client Certificate Dynamic Parameters - /// Path - WsMan:\Localhost\ClientCertificate + /// Path - WsMan:\Localhost\ClientCertificate. /// public class WSManProviderClientCertificateParameters { @@ -5750,17 +5924,19 @@ public string Issuer { return _issuer; } + set { _issuer = value; } } + private string _issuer; /// /// Parameter Subject. /// - [Parameter()] + [Parameter] [ValidateNotNullOrEmpty] public string Subject { @@ -5768,11 +5944,13 @@ public string Subject { return _subject; } + set { _subject = value; } } + private string _subject = "*"; /// @@ -5787,11 +5965,13 @@ public System.Uri URI { return _uri; } + set { _uri = value; } } + private System.Uri _uri = new Uri("*", UriKind.RelativeOrAbsolute); /// @@ -5804,19 +5984,19 @@ public bool Enabled { return _enabled; } + set { _enabled = value; } } - private bool _enabled = true; + private bool _enabled = true; } + #endregion -#endregion - -#region Listener Dynamic Parameters + #region Listener Dynamic Parameters /// /// Listener Dynamic parameters @@ -5835,11 +6015,13 @@ public string Address { return _address; } + set { _address = value; } } + private string _address; /// @@ -5853,11 +6035,13 @@ public string Transport { return _transport; } + set { _transport = value; } } + private string _transport = "http"; /// @@ -5871,12 +6055,14 @@ public int Port { return _port; } + set { _port = value; _IsPortSpecified = true; } } + private int _port = 0; /// @@ -5890,11 +6076,13 @@ public string HostName { return _hostName; } + set { _hostName = value; } } + private string _hostName; /// @@ -5907,11 +6095,13 @@ public bool Enabled { return _enabled; } + set { _enabled = value; } } + private bool _enabled = true; /// @@ -5927,11 +6117,13 @@ public string URLPrefix { return _urlprefix; } + set { _urlprefix = value; } } + private string _urlprefix = "wsman"; /// @@ -5946,11 +6138,13 @@ public string CertificateThumbPrint { return _certificatethumbprint; } + set { _certificatethumbprint = value; } } + private string _certificatethumbprint; /// @@ -5962,45 +6156,47 @@ public bool IsPortSpecified { return _IsPortSpecified; } + set { _IsPortSpecified = value; } - } + private bool _IsPortSpecified = false; } -#endregion + #endregion -#endregion + #endregion -#region SetItemDynamicParameters + #region SetItemDynamicParameters /// /// Set-Item Dynamic parameters - /// Path - WsMan:\Localhost\Client> Set-Item .\TrustedHosts + /// Path - WsMan:\Localhost\Client> Set-Item .\TrustedHosts. /// public class WSManProviderSetItemDynamicParameters { /// /// Parameter Concatenate. /// - [Parameter()] + [Parameter] public SwitchParameter Concatenate { get { return _concatenate; } + set { _concatenate = value; } } + private SwitchParameter _concatenate = false; } + #endregion SetItemDynamicParameters -#endregion SetItemDynamicParameters - -#endregion + #endregion -#region "String Literals" + #region "String Literals" internal static class WSManStringLiterals { @@ -6009,144 +6205,134 @@ internal static class WSManStringLiterals /// /// The default path separator used by the base implementation of the providers. /// - /// internal const char DefaultPathSeparator = '\\'; /// /// The alternate path separator used by the base implementation of the providers. /// - /// internal const char AlternatePathSeparator = '/'; /// - /// Double Quotes used while constructing XML + /// Double Quotes used while constructing XML. /// internal const char EnclosingDoubleQuotes = '\"'; /// - /// Equalto Used while constructing XML + /// Equalto Used while constructing XML. /// internal const char Equalto = '='; /// - /// For XML Construction + /// For XML Construction. /// internal const char GreaterThan = '>'; /// - /// XML Closing Tag + /// XML Closing Tag. /// internal const string XmlClosingTag = "/>"; /// - /// White space used while constructing XML + /// White space used while constructing XML. /// internal const char SingleWhiteSpace = ' '; /// - /// Root node of WsMan + /// Root node of WsMan. /// internal const string ProviderName = "WSMan"; /// - /// /// internal const string WsMan_Schema = "http://schemas.microsoft.com/wbem/wsman/1/config"; /// - /// /// internal const string NS_XSI = "xmlns:xsi=" + "\"http://www.w3.org/2001/XMLSchema-instance\""; /// - /// /// internal const string ATTR_NIL = "xsi:nil=" + "\"true\""; /// - /// /// internal const string ATTR_NIL_NAME = "xsi:nil"; /// - /// /// internal const char WinrmPathSeparator = '/'; /// - /// /// internal const string rootpath = "WSMan"; /// - /// /// internal const string ContainerChildValue = "Container"; -#region WsMan Containers + #region WsMan Containers /// - /// Plugin Container + /// Plugin Container. /// internal const string containerPlugin = "Plugin"; /// - /// Client Container + /// Client Container. /// internal const string containerClient = "Client"; /// - /// Shell Container + /// Shell Container. /// internal const string containerShell = "Shell"; /// - /// ClientCertificate Container + /// ClientCertificate Container. /// internal const string containerClientCertificate = "ClientCertificate"; /// - /// Listener Container + /// Listener Container. /// internal const string containerListener = "Listener"; /// - /// Service Container + /// Service Container. /// internal const string containerService = "Service"; /// - /// Auth Container - Under Client,Service + /// Auth Container - Under Client,Service. /// internal const string containerAuth = "Auth"; /// - /// DefaultPorts Container - Under Client,Service + /// DefaultPorts Container - Under Client,Service. /// internal const string containerDefaultPorts = "DefaultPorts"; /// - /// TrustedHosts Container - Under Client,Service + /// TrustedHosts Container - Under Client,Service. /// internal const string containerTrustedHosts = "TrustedHosts"; /// - /// Security Container - Under Plugin + /// Security Container - Under Plugin. /// internal const string containerSecurity = "Security"; /// - /// Resources Container - Under Plugin + /// Resources Container - Under Plugin. /// internal const string containerResources = "Resources"; /// - /// Resource in Resources Container - Under Plugin + /// Resource in Resources Container - Under Plugin. /// internal const string containerSingleResource = "Resource"; /// - /// InitParameters Container - Under Plugin + /// InitParameters Container - Under Plugin. /// internal const string containerInitParameters = "InitializationParameters"; /// - /// Quotas Container - Under Plugin + /// Quotas Container - Under Plugin. /// internal const string containerQuotasParameters = "Quotas"; /// - /// Winrs Container - Exposed as Shell + /// Winrs Container - Exposed as Shell. /// internal const string containerWinrs = "Winrs"; /// - /// certmapping Container - Exposed as ClientCertificate in the provider. + /// Certmapping Container - Exposed as ClientCertificate in the provider. /// internal const string containerCertMapping = "Service/certmapping"; - /// - /// Possible Values in Plugin Top Level XML + /// Possible Values in Plugin Top Level XML. /// internal static readonly string[] NewItemPluginConfigParams = new string[] { @@ -6172,9 +6358,9 @@ internal static class WSManStringLiterals /// Possible Values in Plugin Top Security XML internal static readonly string[] NewItemSecurityParams = new string[] { "Uri", "Sddl", "ExactMatch" }; -#endregion WsMan Containers + #endregion WsMan Containers -#region WSMAN Config Names + #region WSMAN Config Names /// /// Name of the configuration which represents RunAs Password. /// @@ -6230,7 +6416,7 @@ internal static class WSManStringLiterals /// internal const string HiddenSuffixForSourceOfValue = "___Source"; -#endregion + #endregion /// /// This is used to start the service. return a bool value. if false we throw error. @@ -6256,29 +6442,30 @@ function Start-WSManServiceD15A7957836142a18627D7E1D342DD82 {{ if ($force -or $pscmdlet.ShouldContinue($queryForStart, $captionForStart)) {{ - Restart WinRM -Force -Confirm:$false + Restart-Service WinRM -Force -Confirm:$false return $true }} + return $false }} #end of Begin block }} $_ | Start-WSManServiceD15A7957836142a18627D7E1D342DD82 -force $args[0] -captionForStart $args[1] -queryForStart $args[2] "; - } -#endregion "String Literals" + #endregion "String Literals" -#region "WsMan Output Objects" + #region "WsMan Output Objects" /// - /// Base Output object + /// Base Output object. /// public class WSManConfigElement { internal WSManConfigElement() { } + internal WSManConfigElement(string name, string typenameofelement) { _name = name; @@ -6291,9 +6478,10 @@ internal WSManConfigElement(string name, string typenameofelement) public string Name { get { return _name; } - set { _name = value; } + set { _name = value; } } + private string _name; /// @@ -6302,8 +6490,10 @@ public string Name public string TypeNameOfElement { get { return _typenameofelement; } + set { _typenameofelement = value; } } + private string _typenameofelement; /// @@ -6312,17 +6502,19 @@ public string TypeNameOfElement public string Type { get { return _typenameofelement; } + set { _typenameofelement = value; } } } /// - /// Leaf Element + /// Leaf Element. /// public class WSManConfigLeafElement : WSManConfigElement { internal WSManConfigLeafElement() { } + internal WSManConfigLeafElement(string Name, object Value, string TypeNameOfElement, object SourceOfValue = null) { _value = Value; @@ -6337,9 +6529,10 @@ internal WSManConfigLeafElement(string Name, object Value, string TypeNameOfElem public object SourceOfValue { get { return _SourceOfValue; } - set { _SourceOfValue = value; } + set { _SourceOfValue = value; } } + private object _SourceOfValue; /// @@ -6348,13 +6541,14 @@ public object SourceOfValue public object Value { get { return _value; } - set { _value = value; } + set { _value = value; } } + private object _value; } /// - /// Container Element + /// Container Element. /// public class WSManConfigContainerElement : WSManConfigElement { @@ -6372,17 +6566,12 @@ internal WSManConfigContainerElement(string Name, string TypeNameOfElement, stri public string[] Keys { get { return _keys; } - set { _keys = value; } + set { _keys = value; } } + private string[] _keys; } - - -#endregion "WsMan Output Objects" - - + #endregion "WsMan Output Objects" } - - diff --git a/src/Microsoft.WSMan.Management/CredSSP.cs b/src/Microsoft.WSMan.Management/CredSSP.cs index 44984e0f7b0..a8457ab5bc2 100644 --- a/src/Microsoft.WSMan.Management/CredSSP.cs +++ b/src/Microsoft.WSMan.Management/CredSSP.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.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.Collections.ObjectModel; -using Microsoft.Win32; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; +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.Security; using System.Threading; -#if CORECLR -using System.Xml.XPath; -#endif +using System.Xml; + +using Microsoft.Win32; using Dbg = System.Management.Automation; @@ -52,38 +50,16 @@ public class WSManCredSSPCommandBase : PSCmdlet public string Role { get { return role; } - set { role = value; } - } - private string role; - /*/// - /// Role can either "Client" or "Server". - /// - [Parameter(ParameterSetName = Client, Mandatory = true, Position = 0)] - public SwitchParameter ClientRole - { - get { return isClient; } - set { isClient = value; } - } - private bool isClient; - - /// - /// - /// - [Parameter(ParameterSetName = Server, Mandatory = true, Position = 0)] - public SwitchParameter ServerRole - { - get { return isServer; } - set { isServer = value; } + set { role = value; } } - private bool isServer;*/ + private string role; #endregion #region Utilities /// - /// /// /// /// Returns a session object upon successful creation..otherwise @@ -119,11 +95,9 @@ internal IWSManSession CreateWSManSession() /// Disables CredSSP authentication on the client. CredSSP authentication /// enables an application to delegate the user's credentials from the client to /// the server, hence allowing the user to perform management operations that - /// access a second hop + /// access a second hop. /// - - - [Cmdlet(VerbsLifecycle.Disable, "WSManCredSSP", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141438")] + [Cmdlet(VerbsLifecycle.Disable, "WSManCredSSP", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096628")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cred")] [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SSP")] public class DisableWSManCredSSPCommand : WSManCredSSPCommandBase, IDisposable @@ -138,7 +112,7 @@ private void DisableClientSideSettings() { WSManHelper helper = new WSManHelper(this); IWSManSession m_SessionObj = CreateWSManSession(); - if (null == m_SessionObj) + if (m_SessionObj == null) { return; } @@ -147,25 +121,22 @@ private void DisableClientSideSettings() { string result = m_SessionObj.Get(helper.CredSSP_RUri, 0); XmlDocument resultopxml = new XmlDocument(); - string inputXml = null; resultopxml.LoadXml(result); XmlNamespaceManager nsmgr = new XmlNamespaceManager(resultopxml.NameTable); nsmgr.AddNamespace("cfg", helper.CredSSP_XMLNmsp); XmlNode xNode = resultopxml.SelectSingleNode(helper.CredSSP_SNode, nsmgr); - if (!(xNode == null)) - { - inputXml = @"false"; - } - else + if (xNode is null) { InvalidOperationException ex = new InvalidOperationException(); ErrorRecord er = new ErrorRecord(ex, helper.GetResourceMsgFromResourcetext("WinrmNotConfigured"), ErrorCategory.InvalidOperation, null); WriteError(er); return; } + + string inputXml = @"false"; + m_SessionObj.Put(helper.CredSSP_RUri, inputXml, 0); -#if !CORECLR if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) { this.DeleteUserDelegateSettings(); @@ -178,14 +149,6 @@ private void DisableClientSideSettings() thread.Start(); thread.Join(); } -#else - { - ThreadStart start = new ThreadStart(this.DeleteUserDelegateSettings); - Thread thread = new Thread(start); - thread.Start(); - thread.Join(); - } -#endif if (!helper.ValidateCreadSSPRegistryRetry(false, null, applicationname)) { @@ -199,7 +162,7 @@ private void DisableClientSideSettings() } finally { - if (!String.IsNullOrEmpty(m_SessionObj.Error)) + if (!string.IsNullOrEmpty(m_SessionObj.Error)) { helper.AssertError(m_SessionObj.Error, true, null); } @@ -213,7 +176,7 @@ private void DisableServerSideSettings() { WSManHelper helper = new WSManHelper(this); IWSManSession m_SessionObj = CreateWSManSession(); - if (null == m_SessionObj) + if (m_SessionObj == null) { return; } @@ -222,19 +185,12 @@ private void DisableServerSideSettings() { string result = m_SessionObj.Get(helper.Service_CredSSP_Uri, 0); XmlDocument resultopxml = new XmlDocument(); - string inputXml = null; resultopxml.LoadXml(result); XmlNamespaceManager nsmgr = new XmlNamespaceManager(resultopxml.NameTable); nsmgr.AddNamespace("cfg", helper.Service_CredSSP_XMLNmsp); XmlNode xNode = resultopxml.SelectSingleNode(helper.CredSSP_SNode, nsmgr); - if (!(xNode == null)) - { - inputXml = string.Format(CultureInfo.InvariantCulture, - @"false", - helper.Service_CredSSP_XMLNmsp); - } - else + if (xNode is null) { InvalidOperationException ex = new InvalidOperationException(); ErrorRecord er = new ErrorRecord(ex, @@ -244,11 +200,16 @@ private void DisableServerSideSettings() return; } + string inputXml = string.Format( + CultureInfo.InvariantCulture, + @"false", + helper.Service_CredSSP_XMLNmsp); + m_SessionObj.Put(helper.Service_CredSSP_Uri, inputXml, 0); } finally { - if (!String.IsNullOrEmpty(m_SessionObj.Error)) + if (!string.IsNullOrEmpty(m_SessionObj.Error)) { helper.AssertError(m_SessionObj.Error, true, null); } @@ -267,7 +228,7 @@ private void DeleteUserDelegateSettings() GPO.OpenLocalMachineGPO(1); KeyHandle = GPO.GetRegistryKey(2); RegistryKey rootKey = Registry.CurrentUser; - string GPOpath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy Objects"; + const string GPOpath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy Objects"; RegistryKey GPOKey = rootKey.OpenSubKey(GPOpath, true); foreach (string keyname in GPOKey.GetSubKeyNames()) { @@ -277,6 +238,7 @@ private void DeleteUserDelegateSettings() DeleteDelegateSettings(applicationname, Registry.CurrentUser, key, GPO); } } + KeyHandle = System.IntPtr.Zero; } @@ -288,7 +250,6 @@ private void DeleteDelegateSettings(string applicationname, RegistryKey rootKey, bool otherkeys = false; try { - string Registry_Path_Credentials_Delegation = Registry_Path + @"\CredentialsDelegation"; RegistryKey Allow_Fresh_Credential_Key = rootKey.OpenSubKey(Registry_Path_Credentials_Delegation + @"\" + helper.Key_Allow_Fresh_Credentials, true); if (Allow_Fresh_Credential_Key != null) @@ -308,8 +269,10 @@ private void DeleteDelegateSettings(string applicationname, RegistryKey rootKey, otherkeys = true; } } + Allow_Fresh_Credential_Key.DeleteValue(value); } + foreach (string keyvalue in KeyCollection) { Allow_Fresh_Credential_Key.SetValue(Convert.ToString(i + 1, CultureInfo.InvariantCulture), keyvalue, RegistryValueKind.String); @@ -317,6 +280,7 @@ private void DeleteDelegateSettings(string applicationname, RegistryKey rootKey, } } } + if (!otherkeys) { rKey = rootKey.OpenSubKey(Registry_Path_Credentials_Delegation, true); @@ -327,17 +291,20 @@ private void DeleteDelegateSettings(string applicationname, RegistryKey rootKey, { rKey.DeleteValue(helper.Key_Allow_Fresh_Credentials, false); } + object regval2 = rKey.GetValue(helper.Key_Concatenate_Defaults_AllowFresh); if (regval2 != null) { rKey.DeleteValue(helper.Key_Concatenate_Defaults_AllowFresh, false); } + if (rKey.OpenSubKey(helper.Key_Allow_Fresh_Credentials) != null) { rKey.DeleteSubKeyTree(helper.Key_Allow_Fresh_Credentials); } } } + GPO.Save(true, true, new Guid("35378EAC-683F-11D2-A89A-00C04FBBCFA2"), new Guid("6AD20875-336C-4e22-968F-C709ACB15814")); } catch (InvalidOperationException ex) @@ -363,20 +330,11 @@ private void DeleteDelegateSettings(string applicationname, RegistryKey rootKey, } #endregion private /// - /// begin processing method. + /// Begin processing method. /// protected override void BeginProcessing() { -#if !CORECLR - if (Environment.OSVersion.Version.Major < 6) - { - //OS is XP/Win2k3. Throw error. - WSManHelper helper = new WSManHelper(this); - string message = helper.FormatResourceMsgFromResourcetext("CmdletNotAvailable"); - throw new InvalidOperationException(message); - } -#endif - //If not running elevated, then throw an "elevation required" error message. + // If not running elevated, then throw an "elevation required" error message. WSManHelper.ThrowIfNotAdministrator(); if (Role.Equals(Client, StringComparison.OrdinalIgnoreCase)) @@ -388,22 +346,22 @@ protected override void BeginProcessing() { DisableServerSideSettings(); } - }//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 @@ -414,7 +372,7 @@ protected override void BeginProcessing() } #endregion IDisposable Members - }//End Class + } #endregion DisableWsManCredSSP #region EnableCredSSP @@ -429,46 +387,43 @@ protected override void BeginProcessing() /// 1. Enables WSMan local configuration on client to enable CredSSP /// 2. Sets CredSSP policy AllowFreshCredentials to wsman/Delegate. This policy /// allows delegating explicit credentials to a server when server - /// authentication is achieved via a trusted X509 certificate or Kerberos + /// authentication is achieved via a trusted X509 certificate or Kerberos. /// - [Cmdlet(VerbsLifecycle.Enable, "WSManCredSSP", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141442")] + [Cmdlet(VerbsLifecycle.Enable, "WSManCredSSP", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096719")] + [OutputType(typeof(XmlElement))] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cred")] [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SSP")] public class EnableWSManCredSSPCommand : WSManCredSSPCommandBase, IDisposable/*, IDynamicParameters*/ { - #region Private Data - - //private const string DelegateComputerParam = "DelegateComputer"; - //private String[] delegatecomputer; - //private RuntimeDefinedParameterDictionary dynamicParameters = new RuntimeDefinedParameterDictionary(); - - #endregion - /// - /// delegate parameter + /// Delegate parameter. /// [Parameter(Position = 1)] [ValidateNotNullOrEmpty] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] DelegateComputer + public string[] DelegateComputer { get { return delegatecomputer; } + set { delegatecomputer = value; } } - private String[] delegatecomputer; + + private string[] delegatecomputer; /// /// Property that sets force parameter. /// - [Parameter()] + [Parameter] public SwitchParameter Force { get { return force; } + set { force = value; } } + private bool force = false; - //helper variable + // helper variable private WSManHelper helper; // The application name MUST be "wsman" as wsman got approval from security @@ -482,19 +437,9 @@ public SwitchParameter Force /// 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); -#if !CORECLR - if (Environment.OSVersion.Version.Major < 6) - { - //OS is XP/Win2k3. Throw error. - string message = helper.FormatResourceMsgFromResourcetext("CmdletNotAvailable"); - throw new InvalidOperationException(message); - } -#endif - //If not running elevated, then throw an "elevation required" error message. - WSManHelper.ThrowIfNotAdministrator(); // DelegateComputer cannot be specified when Role is other than client if ((delegatecomputer != null) && !Role.Equals(Client, StringComparison.OrdinalIgnoreCase)) @@ -528,70 +473,32 @@ protected override void BeginProcessing() { EnableServerSideSettings(); } - }//End BeginProcessing() - - /* - /// - /// This method returns DynamicParameters used for Enable-WSManCredSSP cmdlet. Enable-WSManCredSSP - /// supports -DelegateComputer parameter when -Role is client. - /// - /// - /// An object representing the dynamic parameters for the cmdlet or null if there - /// are none. - /// - object IDynamicParameters.GetDynamicParameters() - { - // return null if the role is not client. - if (!Role.Equals(ClientRole, StringComparison.OrdinalIgnoreCase)) - { - return dynamicParameters; - } - - // Construct attributes for the DelegateComputer parameter - Collection delegateComputerAttributeCollection = new Collection(); - ParameterAttribute paramAttribute = new ParameterAttribute(); - paramAttribute.Mandatory = true; - paramAttribute.Position = 1; - ValidateNotNullOrEmptyAttribute notNullAttribute = new ValidateNotNullOrEmptyAttribute(); - delegateComputerAttributeCollection.Add(paramAttribute); - delegateComputerAttributeCollection.Add(notNullAttribute); - - // Construct the parameter and return. - RuntimeDefinedParameter delegateComputer = new RuntimeDefinedParameter( - DelegateComputerParam, - typeof(string[]), - delegateComputerAttributeCollection); - dynamicParameters.Add(DelegateComputerParam, delegateComputer); - - return dynamicParameters; - - } // GetDynamicParameters*/ + } #endregion /// - /// /// /// /// private void EnableClientSideSettings() { - String query = helper.GetResourceMsgFromResourcetext("CredSSPContinueQuery"); - String caption = helper.GetResourceMsgFromResourcetext("CredSSPContinueCaption"); + string query = helper.GetResourceMsgFromResourcetext("CredSSPContinueQuery"); + string caption = helper.GetResourceMsgFromResourcetext("CredSSPContinueCaption"); if (!force && !ShouldContinue(query, caption)) { return; } IWSManSession m_SessionObj = CreateWSManSession(); - if (null == m_SessionObj) + if (m_SessionObj == null) { return; } try { - //get the credssp node to check if wsman is configured on this machine + // get the credssp node to check if wsman is configured on this machine string result = m_SessionObj.Get(helper.CredSSP_RUri, 0); XmlNode node = helper.GetXmlNode(result, helper.CredSSP_SNode, helper.CredSSP_XMLNmsp); @@ -602,18 +509,15 @@ private void EnableClientSideSettings() WriteError(er); return; } - // Extract delegateComputer information from dynamic parameters collection - //RuntimeDefinedParameter delegateComputerParameter = dynamicParameters[DelegateComputerParam]; - //delegatecomputer = (string[])delegateComputerParameter.Value; - string newxmlcontent = @"true"; + const string newxmlcontent = @"true"; try { XmlDocument xmldoc = new XmlDocument(); - //push the xml string with credssp enabled + + // push the xml string with credssp enabled xmldoc.LoadXml(m_SessionObj.Put(helper.CredSSP_RUri, newxmlcontent, 0)); -#if !CORECLR // No ApartmentState In CoreCLR // set the Registry using GroupPolicyObject if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) { @@ -627,14 +531,6 @@ private void EnableClientSideSettings() thread.Start(); thread.Join(); } -#else - { - ThreadStart start = new ThreadStart(this.UpdateCurrentUserRegistrySettings); - Thread thread = new Thread(start); - thread.Start(); - thread.Join(); - } -#endif if (helper.ValidateCreadSSPRegistryRetry(true, delegatecomputer, applicationname)) { @@ -652,7 +548,7 @@ private void EnableClientSideSettings() } finally { - if (!String.IsNullOrEmpty(m_SessionObj.Error)) + if (!string.IsNullOrEmpty(m_SessionObj.Error)) { helper.AssertError(m_SessionObj.Error, true, delegatecomputer); } @@ -666,22 +562,22 @@ private void EnableClientSideSettings() private void EnableServerSideSettings() { - String query = helper.GetResourceMsgFromResourcetext("CredSSPServerContinueQuery"); - String caption = helper.GetResourceMsgFromResourcetext("CredSSPContinueCaption"); + string query = helper.GetResourceMsgFromResourcetext("CredSSPServerContinueQuery"); + string caption = helper.GetResourceMsgFromResourcetext("CredSSPContinueCaption"); if (!force && !ShouldContinue(query, caption)) { return; } IWSManSession m_SessionObj = CreateWSManSession(); - if (null == m_SessionObj) + if (m_SessionObj == null) { return; } try { - //get the credssp node to check if wsman is configured on this machine + // get the credssp node to check if wsman is configured on this machine string result = m_SessionObj.Get(helper.Service_CredSSP_Uri, 0); XmlNode node = helper.GetXmlNode(result, helper.CredSSP_SNode, @@ -698,10 +594,12 @@ private void EnableServerSideSettings() try { XmlDocument xmldoc = new XmlDocument(); - string newxmlcontent = string.Format(CultureInfo.InvariantCulture, + string newxmlcontent = string.Format( + CultureInfo.InvariantCulture, @"true", helper.Service_CredSSP_XMLNmsp); - //push the xml string with credssp enabled + + // push the xml string with credssp enabled xmldoc.LoadXml(m_SessionObj.Put(helper.Service_CredSSP_Uri, newxmlcontent, 0)); WriteObject(xmldoc.FirstChild); } @@ -712,7 +610,7 @@ private void EnableServerSideSettings() } finally { - if (!String.IsNullOrEmpty(m_SessionObj.Error)) + if (!string.IsNullOrEmpty(m_SessionObj.Error)) { helper.AssertError(m_SessionObj.Error, true, delegatecomputer); } @@ -725,7 +623,6 @@ private void EnableServerSideSettings() } /// - /// /// private void UpdateCurrentUserRegistrySettings() { @@ -734,7 +631,7 @@ private void UpdateCurrentUserRegistrySettings() GPO.OpenLocalMachineGPO(1); KeyHandle = GPO.GetRegistryKey(2); RegistryKey rootKey = Registry.CurrentUser; - string GPOpath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy Objects"; + const string GPOpath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy Objects"; RegistryKey GPOKey = rootKey.OpenSubKey(GPOpath, true); foreach (string keyname in GPOKey.GetSubKeyNames()) { @@ -742,15 +639,14 @@ private void UpdateCurrentUserRegistrySettings() { string key = GPOpath + "\\" + keyname + "\\" + @"Software\Policies\Microsoft\Windows"; UpdateGPORegistrySettings(applicationname, this.delegatecomputer, Registry.CurrentUser, key); - } } - //saving gpo settings + // saving gpo settings GPO.Save(true, true, new Guid("35378EAC-683F-11D2-A89A-00C04FBBCFA2"), new Guid("7A9206BD-33AF-47af-B832-D4128730E990")); } /// - /// Updates the grouppolicy registry settings + /// Updates the grouppolicy registry settings. /// /// /// @@ -758,33 +654,21 @@ private void UpdateCurrentUserRegistrySettings() /// private void UpdateGPORegistrySettings(string applicationname, string[] delegatestring, RegistryKey rootKey, string Registry_Path) { - //RegistryKey rootKey = Registry.LocalMachine; + // RegistryKey rootKey = Registry.LocalMachine; RegistryKey Credential_Delegation_Key; RegistryKey Allow_Fresh_Credential_Key; int i = 0; try { string Registry_Path_Credentials_Delegation = Registry_Path + @"\CredentialsDelegation"; - //open the registry key.If key is not present,create a new one - Credential_Delegation_Key = rootKey.OpenSubKey(Registry_Path_Credentials_Delegation, true); - if (Credential_Delegation_Key == null) - Credential_Delegation_Key = rootKey.CreateSubKey(Registry_Path_Credentials_Delegation -#if !CORECLR - , RegistryKeyPermissionCheck.ReadWriteSubTree -#endif - ); + // open the registry key.If key is not present,create a new one + Credential_Delegation_Key = rootKey.OpenSubKey(Registry_Path_Credentials_Delegation, true) ?? rootKey.CreateSubKey(Registry_Path_Credentials_Delegation, RegistryKeyPermissionCheck.ReadWriteSubTree); Credential_Delegation_Key.SetValue(helper.Key_Allow_Fresh_Credentials, 1, RegistryValueKind.DWord); Credential_Delegation_Key.SetValue(helper.Key_Concatenate_Defaults_AllowFresh, 1, RegistryValueKind.DWord); // add the delegate value - Allow_Fresh_Credential_Key = rootKey.OpenSubKey(Registry_Path_Credentials_Delegation + @"\" + helper.Key_Allow_Fresh_Credentials, true); - if (Allow_Fresh_Credential_Key == null) - Allow_Fresh_Credential_Key = rootKey.CreateSubKey(Registry_Path_Credentials_Delegation + @"\" + helper.Key_Allow_Fresh_Credentials -#if !CORECLR - , RegistryKeyPermissionCheck.ReadWriteSubTree -#endif - ); + Allow_Fresh_Credential_Key = rootKey.OpenSubKey(Registry_Path_Credentials_Delegation + @"\" + helper.Key_Allow_Fresh_Credentials, true) ?? rootKey.CreateSubKey(Registry_Path_Credentials_Delegation + @"\" + helper.Key_Allow_Fresh_Credentials, RegistryKeyPermissionCheck.ReadWriteSubTree); if (Allow_Fresh_Credential_Key != null) { @@ -811,23 +695,21 @@ private void UpdateGPORegistrySettings(string applicationname, string[] delegate ErrorRecord er = new ErrorRecord(ex, "ArgumentException", ErrorCategory.InvalidOperation, null); WriteError(er); } - } #region IDisposable Members /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - //CleanUp(); GC.SuppressFinalize(this); } /// - /// public dispose method + /// Public dispose method. /// public void @@ -838,7 +720,7 @@ private void UpdateGPORegistrySettings(string applicationname, string[] delegate } #endregion IDisposable Members - }//End Class + } #endregion EnableCredSSP #region Get-CredSSP @@ -854,17 +736,18 @@ private void UpdateGPORegistrySettings(string applicationname, string[] delegate /// 2. Gets the configuration information for the CredSSP policy /// AllowFreshCredentials . This policy allows delegating explicit credentials /// to a server when server authentication is achieved via a trusted X509 - /// certificate or Kerberos + /// certificate or Kerberos. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cred")] [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SSP")] - [Cmdlet(VerbsCommon.Get, "WSManCredSSP", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141443")] + [Cmdlet(VerbsCommon.Get, "WSManCredSSP", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096838")] + [OutputType(typeof(string))] public class GetWSManCredSSPCommand : PSCmdlet, IDisposable { - # region private - WSManHelper helper = null; + #region private + private WSManHelper helper = null; /// - /// method to get the values. + /// Method to get the values. /// private string GetDelegateSettings(string applicationname) { @@ -896,6 +779,7 @@ private string GetDelegateSettings(string applicationname) } } } + if (result.EndsWith(listvalue, StringComparison.OrdinalIgnoreCase)) { result = result.Remove(result.Length - 1); @@ -919,31 +803,20 @@ private string GetDelegateSettings(string applicationname) ErrorRecord er = new ErrorRecord(ex, "ObjectDisposedException", ErrorCategory.PermissionDenied, null); WriteError(er); } - return result; + + return result; } - # endregion private + #endregion private - # region overrides + #region overrides /// /// Method to begin processing. /// 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); -#if !CORECLR - if (Environment.OSVersion.Version.Major < 6) - { - //OS is XP/Win2k3. Throw error. - string message = helper.FormatResourceMsgFromResourcetext("CmdletNotAvailable"); - throw new InvalidOperationException(message); - } -#endif - //If not running elevated, then throw an "elevation required" error message. - WSManHelper.ThrowIfNotAdministrator(); - IWSManSession m_SessionObj = null; try { @@ -960,7 +833,7 @@ protected override void BeginProcessing() } // The application name MUST be "wsman" as wsman got approval from security // folks who suggested to register the SPN with name "wsman". - string applicationname = "wsman"; + const string applicationname = "wsman"; string credsspResult = GetDelegateSettings(applicationname); if (string.IsNullOrEmpty(credsspResult)) { @@ -1013,7 +886,7 @@ protected override void BeginProcessing() } finally { - if (!String.IsNullOrEmpty(m_SessionObj.Error)) + if (!string.IsNullOrEmpty(m_SessionObj.Error)) { helper.AssertError(m_SessionObj.Error, true, null); } @@ -1028,17 +901,16 @@ protected override void BeginProcessing() #region IDisposable Members /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - //CleanUp(); GC.SuppressFinalize(this); } /// - /// public dispose method + /// Public dispose method. /// public void @@ -1049,15 +921,7 @@ protected override void BeginProcessing() } #endregion IDisposable Members - - - - - } - - - #endregion } diff --git a/src/Microsoft.WSMan.Management/CurrentConfigurations.cs b/src/Microsoft.WSMan.Management/CurrentConfigurations.cs index 190221b46da..3182c04c535 100644 --- a/src/Microsoft.WSMan.Management/CurrentConfigurations.cs +++ b/src/Microsoft.WSMan.Management/CurrentConfigurations.cs @@ -1,34 +1,17 @@ -//------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// -// -// Pankaj Sarda -// -// -// -// Class that queries the server and gets current configurations. -// Also provides a generic way to update the configurations. -// -// -// -//------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Globalization; +using System.Xml; namespace Microsoft.WSMan.Management { - using System; - using System.Globalization; - using System.Xml; - #if CORECLR - using System.Xml.XPath; - #endif - /// /// Class that queries the server and gets current configurations. /// Also provides a generic way to update the configurations. /// - internal class CurrentConfigurations + internal sealed class CurrentConfigurations { /// /// Prefix used to add NameSpace of root element to namespace manager. @@ -38,7 +21,7 @@ internal class CurrentConfigurations /// /// This holds the current configurations XML. /// - private XmlDocument rootDocument; + private readonly XmlDocument rootDocument; /// /// Holds the reference to the current document element. @@ -53,7 +36,7 @@ internal class CurrentConfigurations /// /// Session of the WsMan sserver. /// - private IWSManSession serverSession; + private readonly IWSManSession serverSession; /// /// Gets the server session associated with the configuration. @@ -78,10 +61,7 @@ public XmlDocument RootDocument /// Current server session. public CurrentConfigurations(IWSManSession serverSession) { - if (serverSession == null) - { - throw new ArgumentNullException("serverSession"); - } + ArgumentNullException.ThrowIfNull(serverSession); this.rootDocument = new XmlDocument(); this.serverSession = serverSession; @@ -96,10 +76,7 @@ public CurrentConfigurations(IWSManSession serverSession) /// False, if operation failed. public bool RefreshCurrentConfiguration(string responseOfGet) { - if (String.IsNullOrEmpty(responseOfGet)) - { - throw new ArgumentNullException("responseOfGet"); - } + ArgumentException.ThrowIfNullOrEmpty(responseOfGet); this.rootDocument.LoadXml(responseOfGet); this.documentElement = this.rootDocument.DocumentElement; @@ -107,7 +84,7 @@ public bool RefreshCurrentConfiguration(string responseOfGet) this.nameSpaceManger = new XmlNamespaceManager(this.rootDocument.NameTable); this.nameSpaceManger.AddNamespace(CurrentConfigurations.DefaultNameSpacePrefix, this.documentElement.NamespaceURI); - return String.IsNullOrEmpty(this.serverSession.Error); + return string.IsNullOrEmpty(this.serverSession.Error); } /// @@ -115,13 +92,10 @@ public bool RefreshCurrentConfiguration(string responseOfGet) /// Issues a PUT request with the ResourceUri provided. /// /// Resource URI to use. - /// False, if operation is not succesful. + /// False, if operation is not successful. public void PutConfigurationOnServer(string resourceUri) { - if (String.IsNullOrEmpty(resourceUri)) - { - throw new ArgumentNullException("resourceUri"); - } + ArgumentException.ThrowIfNullOrEmpty(resourceUri); this.serverSession.Put(resourceUri, this.rootDocument.InnerXml, 0); } @@ -134,10 +108,7 @@ public void PutConfigurationOnServer(string resourceUri) /// Path with namespace to the node from Root element. Must not end with '/'. public void RemoveOneConfiguration(string pathToNodeFromRoot) { - if (pathToNodeFromRoot == null) - { - throw new ArgumentNullException("pathToNodeFromRoot"); - } + ArgumentNullException.ThrowIfNull(pathToNodeFromRoot); XmlNode nodeToRemove = this.documentElement.SelectSingleNode( @@ -148,12 +119,12 @@ public void RemoveOneConfiguration(string pathToNodeFromRoot) { if (nodeToRemove is XmlAttribute) { - this.RemoveAttribute(nodeToRemove as XmlAttribute); + RemoveAttribute(nodeToRemove as XmlAttribute); } } else { - throw new ArgumentException("Node is not present in the XML, Please give valid XPath", "pathToNodeFromRoot"); + throw new ArgumentException("Node is not present in the XML, Please give valid XPath", nameof(pathToNodeFromRoot)); } } @@ -167,20 +138,9 @@ public void RemoveOneConfiguration(string pathToNodeFromRoot) /// Value of the configurations. public void UpdateOneConfiguration(string pathToNodeFromRoot, string configurationName, string configurationValue) { - if (pathToNodeFromRoot == null) - { - throw new ArgumentNullException("pathToNodeFromRoot"); - } - - if (String.IsNullOrEmpty(configurationName)) - { - throw new ArgumentNullException("configurationName"); - } - - if (configurationValue == null) - { - throw new ArgumentNullException("configurationValue"); - } + ArgumentNullException.ThrowIfNull(pathToNodeFromRoot); + ArgumentException.ThrowIfNullOrEmpty(configurationName); + ArgumentNullException.ThrowIfNull(configurationValue); XmlNode nodeToUpdate = this.documentElement.SelectSingleNode( @@ -198,7 +158,7 @@ public void UpdateOneConfiguration(string pathToNodeFromRoot, string configurati } } - XmlNode attr = this.rootDocument.CreateNode(XmlNodeType.Attribute, configurationName, String.Empty); + XmlNode attr = this.rootDocument.CreateNode(XmlNodeType.Attribute, configurationName, string.Empty); attr.Value = configurationValue; nodeToUpdate.Attributes.SetNamedItem(attr); @@ -212,10 +172,7 @@ public void UpdateOneConfiguration(string pathToNodeFromRoot, string configurati /// Value of the Node, or Null if no node present. public string GetOneConfiguration(string pathFromRoot) { - if (pathFromRoot == null) - { - throw new ArgumentNullException("pathFromRoot"); - } + ArgumentNullException.ThrowIfNull(pathFromRoot); XmlNode requiredNode = this.documentElement.SelectSingleNode( @@ -234,7 +191,7 @@ public string GetOneConfiguration(string pathFromRoot) /// Removes the attribute from OwnerNode. /// /// Attribute to Remove. - private void RemoveAttribute(XmlAttribute attributeToRemove) + private static void RemoveAttribute(XmlAttribute attributeToRemove) { XmlElement ownerElement = attributeToRemove.OwnerElement; ownerElement.RemoveAttribute(attributeToRemove.Name); diff --git a/src/Microsoft.WSMan.Management/Interop.cs b/src/Microsoft.WSMan.Management/Interop.cs index 17ee74fcd87..3abb938a572 100644 --- a/src/Microsoft.WSMan.Management/Interop.cs +++ b/src/Microsoft.WSMan.Management/Interop.cs @@ -1,32 +1,28 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. 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.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; +using System.Xml; #pragma warning disable 1591 namespace Microsoft.WSMan.Management { - #region "public Api" - - #region WsManEnumFlags /// _WSManEnumFlags enumeration. - [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] [TypeLibType((short)0)] public enum WSManEnumFlags @@ -60,12 +56,8 @@ public enum WSManEnumFlags #endregion WsManEnumFlags - - - #region WsManSessionFlags /// WSManSessionFlags enumeration. - /// [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] [TypeLibType((short)0)] public enum WSManSessionFlags @@ -132,20 +124,19 @@ public enum WSManSessionFlags #region AuthenticationMechanism /// WSManEnumFlags enumeration - /// [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] public enum AuthenticationMechanism { /// - /// Use no authentication + /// Use no authentication. /// None = 0x0, /// - /// Use Default authentication + /// Use Default authentication. /// Default = 0x1, /// - /// Use digest authentication for a remote operation + /// Use digest authentication for a remote operation. /// Digest = 0x2, /// @@ -153,23 +144,23 @@ public enum AuthenticationMechanism /// Negotiate = 0x4, /// - /// Use basic authentication for a remote operation + /// Use basic authentication for a remote operation. /// Basic = 0x8, /// - /// Use kerberos authentication for a remote operation + /// Use kerberos authentication for a remote operation. /// Kerberos = 0x10, /// - /// Use client certificate authentication for a remote operation + /// Use client certificate authentication for a remote operation. /// ClientCertificate = 0x20, /// - /// Use CredSSP authentication for a remote operation + /// Use CredSSP authentication for a remote operation. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Credssp")] Credssp = 0x80, - } + } #endregion AuthenticationMechanism @@ -182,7 +173,7 @@ public enum AuthenticationMechanism [ComImport] [TypeLibType((short)4304)] #if CORECLR - [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] #else [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] #endif @@ -204,8 +195,8 @@ public interface IWSMan #endif /// CreateSession method of IWSMan interface. - /// An original IDL definition of CreateSession method was the following: HRESULT CreateSession ([optional, defaultvalue("")] BSTR connection, [optional, defaultvalue(0)] long flags, [optional] IDispatch* connectionOptions, [out, retval] IDispatch** ReturnValue); - // IDL: HRESULT CreateSession ([optional, defaultvalue("")] BSTR connection, [optional, defaultvalue(0)] long flags, [optional] IDispatch* connectionOptions, [out, retval] IDispatch** ReturnValue); + /// An original IDL definition of CreateSession method was the following: HRESULT CreateSession ([optional, defaultvalue(string.Empty)] BSTR connection, [optional, defaultvalue(0)] long flags, [optional] IDispatch* connectionOptions, [out, retval] IDispatch** ReturnValue); + // IDL: HRESULT CreateSession ([optional, defaultvalue(string.Empty)] BSTR connection, [optional, defaultvalue(0)] long flags, [optional] IDispatch* connectionOptions, [out, retval] IDispatch** ReturnValue); [DispId(1)] #if CORECLR @@ -264,7 +255,7 @@ string Error [ComImport] [TypeLibType((short)4288)] #if CORECLR - [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] #else [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] #endif @@ -321,7 +312,7 @@ string Password [ComImport] [TypeLibType((short)4288)] #if CORECLR - [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] #else [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] #endif @@ -338,7 +329,6 @@ string CertificateThumbprint [DispId(1)] set; } - } /// IWSManConnectionOptions interface. @@ -346,7 +336,7 @@ string CertificateThumbprint [ComImport] [TypeLibType((short)4288)] #if CORECLR - [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] #else [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] #endif @@ -386,8 +376,7 @@ void SetProxy(int accessType, /// ProxyAuthenticationUseDigest method of IWSManConnectionOptionsEx2 interface. [DispId(11)] int ProxyAuthenticationUseDigest(); - }; - + } #endregion IWSManConnectionOptions @@ -397,7 +386,7 @@ void SetProxy(int accessType, [ComImport] [TypeLibType((short)4288)] #if CORECLR - [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] #else [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] #endif @@ -461,7 +450,7 @@ string Error [TypeLibType((short)4304)] [SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] #if CORECLR - [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] #else [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] #endif @@ -486,8 +475,8 @@ public interface IWSManEx #endif /// CreateSession method of IWSManEx interface. - /// An original IDL definition of CreateSession method was the following: HRESULT CreateSession ([optional, defaultvalue("")] BSTR connection, [optional, defaultvalue(0)] long flags, [optional] IDispatch* connectionOptions, [out, retval] IDispatch** ReturnValue); - // IDL: HRESULT CreateSession ([optional, defaultvalue("")] BSTR connection, [optional, defaultvalue(0)] long flags, [optional] IDispatch* connectionOptions, [out, retval] IDispatch** ReturnValue); + /// An original IDL definition of CreateSession method was the following: HRESULT CreateSession ([optional, defaultvalue(string.Empty)] BSTR connection, [optional, defaultvalue(0)] long flags, [optional] IDispatch* connectionOptions, [out, retval] IDispatch** ReturnValue); + // IDL: HRESULT CreateSession ([optional, defaultvalue(string.Empty)] BSTR connection, [optional, defaultvalue(0)] long flags, [optional] IDispatch* connectionOptions, [out, retval] IDispatch** ReturnValue); [DispId(1)] #if CORECLR @@ -498,7 +487,6 @@ public interface IWSManEx object CreateSession([MarshalAs(UnmanagedType.BStr)] string connection, int flags, [MarshalAs(UnmanagedType.IDispatch)] object connectionOptions); #endif - /// CreateConnectionOptions method of IWSManEx interface. /// An original IDL definition of CreateConnectionOptions method was the following: HRESULT CreateConnectionOptions ([out, retval] IDispatch** ReturnValue); // IDL: HRESULT CreateConnectionOptions ([out, retval] IDispatch** ReturnValue); @@ -511,9 +499,7 @@ public interface IWSManEx #endif object CreateConnectionOptions(); - /// - /// /// /// string CommandLine @@ -540,11 +526,9 @@ string Error get; } - /// CreateResourceLocator method of IWSManEx interface. - /// An original IDL definition of CreateResourceLocator method was the following: HRESULT CreateResourceLocator ([optional, defaultvalue("")] BSTR strResourceLocator, [out, retval] IDispatch** ReturnValue); - // IDL: HRESULT CreateResourceLocator ([optional, defaultvalue("")] BSTR strResourceLocator, [out, retval] IDispatch** ReturnValue); - + /// An original IDL definition of CreateResourceLocator method was the following: HRESULT CreateResourceLocator ([optional, defaultvalue(string.Empty)] BSTR strResourceLocator, [out, retval] IDispatch** ReturnValue); + // IDL: HRESULT CreateResourceLocator ([optional, defaultvalue(string.Empty)] BSTR strResourceLocator, [out, retval] IDispatch** ReturnValue); [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "str")] [DispId(5)] @@ -567,7 +551,6 @@ string Error /// An original IDL definition of SessionFlagCredUsernamePassword method was the following: HRESULT SessionFlagCredUsernamePassword ([out, retval] long* ReturnValue); // IDL: HRESULT SessionFlagCredUsernamePassword ([out, retval] long* ReturnValue); - [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Username")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cred")] [DispId(7)] @@ -708,28 +691,26 @@ string Error [DispId(29)] int EnumerationFlagAssociatedInstance(); } -#endregion IWsManEx + #endregion IWsManEx -#region IWsManResourceLocator + #region IWsManResourceLocator /// IWSManResourceLocator interface. [SuppressMessage("Microsoft.Design", "CA1040:AvoidEmptyInterfaces")] [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")] - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sel")] [Guid("A7A1BA28-DE41-466A-AD0A-C4059EAD7428")] [ComImport] [TypeLibType((short)4288)] #if CORECLR - [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] #else [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] #endif - public interface IWSManResourceLocator { #if CORECLR @@ -753,21 +734,19 @@ public interface IWSManResourceLocator [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")] string ResourceUri { - // IDL: HRESULT resourceUri (BSTR value); + [SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:PropertyAccessorsMustFollowOrder", Justification = "COM interface defines put_ before get_.")] [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "resource")] [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")] [DispId(1)] set; // IDL: HRESULT resourceUri ([out, retval] BSTR* ReturnValue); - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "resource")] [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")] [DispId(1)] [return: MarshalAs(UnmanagedType.BStr)] get; - } /// AddSelector method of IWSManResourceLocator interface. Add selector to resource locator @@ -775,7 +754,6 @@ string ResourceUri // Add selector to resource locator // IDL: HRESULT AddSelector (BSTR resourceSelName, VARIANT selValue); - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "resource")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "sel")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sel")] @@ -787,8 +765,6 @@ string ResourceUri // Clear all selectors // IDL: HRESULT ClearSelectors (void); - - [DispId(3)] void ClearSelectors(); @@ -810,7 +786,6 @@ string FragmentPath set; } - /// FragmentDialect property of IWSManResourceLocator interface. Gets the Fragment dialect /// An original IDL definition of FragmentDialect property was the following: BSTR FragmentDialect; // Gets the Fragment dialect @@ -837,24 +812,24 @@ string FragmentDialect [DispId(6)] void AddOption([MarshalAs(UnmanagedType.BStr)] string OptionName, object OptionValue, int mustComply); - - /// MustUnderstandOptions property of IWSManResourceLocator interface. Sets the MustUnderstandOptions value + /// MustUnderstandOptions property of IWSManResourceLocator interface. Sets the MustUnderstandOptions value /// An original IDL definition of MustUnderstandOptions property was the following: long MustUnderstandOptions; - // Sets the MustUnderstandOptions value + // Sets the MustUnderstandOptions value // IDL: long MustUnderstandOptions; int MustUnderstandOptions { - // IDL: HRESULT MustUnderstandOptions ([out, retval] long* ReturnValue); - - [DispId(7)] - get; // IDL: HRESULT MustUnderstandOptions (long value); + [SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:PropertyAccessorsMustFollowOrder", Justification = "COM interface defines put_ before get_.")] [DispId(7)] set; - } + // IDL: HRESULT MustUnderstandOptions ([out, retval] long* ReturnValue); + + [DispId(7)] + get; + } /// ClearOptions method of IWSManResourceLocator interface. Clear all options /// An original IDL definition of ClearOptions method was the following: HRESULT ClearOptions (void); @@ -864,8 +839,6 @@ int MustUnderstandOptions [DispId(8)] void ClearOptions(); - - /// Error property of IWSManResourceLocator interface. /// An original IDL definition of Error property was the following: BSTR Error; // IDL: BSTR Error; @@ -881,19 +854,16 @@ string Error [return: MarshalAs(UnmanagedType.BStr)] get; } - - - } -#endregion IWsManResourceLocator + #endregion IWsManResourceLocator -#region IWSManSession + #region IWSManSession /// IWSManSession interface. [Guid("FC84FC58-1286-40C4-9DA0-C8EF6EC241E0")] [ComImport] [TypeLibType((short)4288)] #if CORECLR - [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] #else [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] #endif @@ -922,7 +892,6 @@ public interface IWSManSession /// An original IDL definition of Get method was the following: HRESULT Get (VARIANT resourceUri, [optional, defaultvalue(0)] long flags, [out, retval] BSTR* ReturnValue); // IDL: HRESULT Get (VARIANT resourceUri, [optional, defaultvalue(0)] long flags, [out, retval] BSTR* ReturnValue); - [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] [DispId(1)] [return: MarshalAs(UnmanagedType.BStr)] @@ -952,23 +921,20 @@ public interface IWSManSession void Delete(object resourceUri, int flags); /// - /// /// /// /// /// /// /// - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "URI")] [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#")] [DispId(5)] - String Invoke([MarshalAs(UnmanagedType.BStr)] string actionURI, [In] object resourceUri, [MarshalAs(UnmanagedType.BStr)] string parameters, [In] int flags); - + string Invoke([MarshalAs(UnmanagedType.BStr)] string actionURI, [In] object resourceUri, [MarshalAs(UnmanagedType.BStr)] string parameters, [In] int flags); /// Enumerate method of IWSManSession interface. - /// An original IDL definition of Enumerate method was the following: HRESULT Enumerate (VARIANT resourceUri, [optional, defaultvalue("")] BSTR filter, [optional, defaultvalue("")] BSTR dialect, [optional, defaultvalue(0)] long flags, [out, retval] IDispatch** ReturnValue); - // IDL: HRESULT Enumerate (VARIANT resourceUri, [optional, defaultvalue("")] BSTR filter, [optional, defaultvalue("")] BSTR dialect, [optional, defaultvalue(0)] long flags, [out, retval] IDispatch** ReturnValue); + /// An original IDL definition of Enumerate method was the following: HRESULT Enumerate (VARIANT resourceUri, [optional, defaultvalue(string.Empty)] BSTR filter, [optional, defaultvalue(string.Empty)] BSTR dialect, [optional, defaultvalue(0)] long flags, [out, retval] IDispatch** ReturnValue); + // IDL: HRESULT Enumerate (VARIANT resourceUri, [optional, defaultvalue(string.Empty)] BSTR filter, [optional, defaultvalue(string.Empty)] BSTR dialect, [optional, defaultvalue(0)] long flags, [out, retval] IDispatch** ReturnValue); [DispId(6)] #if CORECLR @@ -1001,7 +967,6 @@ string Error get; } - /// BatchItems property of IWSManSession interface. /// An original IDL definition of BatchItems property was the following: long BatchItems; // IDL: long BatchItems; @@ -1018,7 +983,6 @@ int BatchItems set; } - /// Timeout property of IWSManSession interface. /// An original IDL definition of Timeout property was the following: long Timeout; // IDL: long Timeout; @@ -1036,15 +1000,15 @@ int Timeout } } -#endregion IWSManSession + #endregion IWSManSession -#region IWSManResourceLocatorInternal + #region IWSManResourceLocatorInternal /// IWSManResourceLocatorInternal interface. [Guid("EFFAEAD7-7EC8-4716-B9BE-F2E7E9FB4ADB")] [ComImport] [TypeLibType((short)400)] #if CORECLR - [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] #else [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] #endif @@ -1068,7 +1032,6 @@ public interface IWSManResourceLocatorInternal #endregion IWSManResourceLocatorInternal - /// WSMan interface. [Guid("BCED617B-EC03-420b-8508-977DC7A686BD")] [ComImport] @@ -1090,9 +1053,10 @@ public class WSManClass public class GPClass { } + [ComImport, Guid("EA502723-A23D-11d1-A7D3-0000F87571E3"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IGroupPolicyObject + internal interface IGroupPolicyObject { void New( [MarshalAs(UnmanagedType.LPWStr)] string pszDomainName, @@ -1155,7 +1119,7 @@ void GetMachineName( uint GetPropertySheetPages(out IntPtr hPages); } -#endregion IGroupPolicyObject + #endregion IGroupPolicyObject /// GpoNativeApi public sealed class GpoNativeApi @@ -1167,11 +1131,11 @@ internal static extern System.IntPtr EnterCriticalPolicySection( [In, MarshalAs(UnmanagedType.Bool)] bool bMachine); [DllImport("Userenv.dll", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool LeaveCriticalPolicySection( [In] System.IntPtr hSection); } -#endregion - + #endregion } #pragma warning restore 1591 diff --git a/src/Microsoft.WSMan.Management/InvokeWSManAction.cs b/src/Microsoft.WSMan.Management/InvokeWSManAction.cs index c7d1f41d80d..da92a680ab3 100644 --- a/src/Microsoft.WSMan.Management/InvokeWSManAction.cs +++ b/src/Microsoft.WSMan.Management/InvokeWSManAction.cs @@ -1,20 +1,18 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. 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 { @@ -25,24 +23,26 @@ namespace Microsoft.WSMan.Management /// Invoke-WSManAction -Action StartService -ResourceURI wmicimv2/Win32_Service /// -SelectorSet {Name=Spooler} /// - - [Cmdlet(VerbsLifecycle.Invoke, "WSManAction", DefaultParameterSetName = "URI", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141446")] + [Cmdlet(VerbsLifecycle.Invoke, "WSManAction", DefaultParameterSetName = "URI", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096843")] + [OutputType(typeof(XmlElement))] public class InvokeWSManActionCommand : AuthenticatingWSManCommand, IDisposable { /// /// The following is the definition of the input parameter "Action". /// Indicates the method which needs to be executed on the management object - /// specified by the ResourceURI and selectors + /// specified by the ResourceURI and selectors. /// [Parameter(Mandatory = true, Position = 1)] [ValidateNotNullOrEmpty] - public String Action + public string Action { get { return action; } + set { action = value; } } - private String action; + + private string action; /// /// The following is the definition of the input parameter "ApplicationName". @@ -50,12 +50,14 @@ public String Action /// [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". @@ -65,20 +67,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". @@ -93,27 +99,32 @@ 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] + [Alias("Path")] [ValidateNotNullOrEmpty] - public String FilePath + 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". - /// 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, @@ -124,8 +135,10 @@ public String FilePath public Hashtable OptionSet { get { return optionset; } + set { optionset = value; } } + private Hashtable optionset; /// @@ -134,19 +147,21 @@ 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 "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 = 2, ValueFromPipeline = true, @@ -156,14 +171,16 @@ public Int32 Port 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 hashtable can - /// be created using New-WSManSessionOption + /// be created using New-WSManSessionOption. /// [Parameter] [ValidateNotNullOrEmpty] @@ -172,8 +189,10 @@ public Hashtable SelectorSet public SessionOption SessionOption { get { return sessionoption; } + set { sessionoption = value; } } + private SessionOption sessionoption; /// @@ -187,8 +206,10 @@ public SessionOption SessionOption public SwitchParameter UseSSL { get { return usessl; } + set { usessl = value; } } + private SwitchParameter usessl; /// @@ -202,14 +223,15 @@ public SwitchParameter UseSSL public Hashtable ValueSet { get { return valueset; } + set { valueset = value; } } - private Hashtable valueset; + private Hashtable valueset; /// /// 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, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -219,16 +241,16 @@ public Hashtable ValueSet public Uri ResourceURI { get { return resourceuri; } + set { resourceuri = value; } } - private Uri resourceuri; + private Uri resourceuri; 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. @@ -239,10 +261,8 @@ protected override void BeginProcessing() helper.WSManOp = "invoke"; - //create the connection string + // create the connection string connectionStr = helper.CreateConnectionString(connectionuri, port, computername, applicationname); - - } /// @@ -250,14 +270,12 @@ protected override void BeginProcessing() /// protected override void ProcessRecord() { - - try { - //create the resourcelocator object + // create the resourcelocator object 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, action); @@ -267,39 +285,38 @@ protected override void ProcessRecord() XmlDocument xmldoc = new XmlDocument(); xmldoc.LoadXml(resultXml); 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 @@ -319,11 +336,5 @@ protected override void EndProcessing() // WSManHelper helper = new WSManHelper(); helper.CleanUp(); } - - - - - - - }//End Class + } } diff --git a/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj b/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj index 0f9f93aca3a..da5ea7ddd04 100644 --- a/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj +++ b/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj @@ -1,20 +1,16 @@ - - + + - 6.0.0 - netcoreapp2.0 - true - true + PowerShell's Microsoft.WSMan.Management project + $(NoWarn);CA1416 Microsoft.WSMan.Management - ../signing/visualstudiopublic.snk - true - false - false + + @@ -25,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 7fdd4f10d7a..9ad9e39c332 100644 --- a/src/Microsoft.WSMan.Management/Set-QuickConfig.cs +++ b/src/Microsoft.WSMan.Management/Set-QuickConfig.cs @@ -1,22 +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; -#if CORECLR -using System.Xml.XPath; -#endif - namespace Microsoft.WSMan.Management { @@ -31,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; /// @@ -81,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; @@ -115,7 +116,6 @@ private void QuickConfigRemoting(bool serviceonly) string xpathStatus = string.Empty; string xpathResult = string.Empty; - if (!usessl) { transport = "http"; @@ -132,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); @@ -155,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; @@ -166,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"); @@ -184,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")); @@ -193,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)); @@ -203,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); @@ -219,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); @@ -233,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) { @@ -243,6 +244,7 @@ private void QuickConfigRemoting(bool serviceonly) { WriteObject(WSManResourceLoader.GetResourceString("L_QuickConfigUpdated_Message")); } + WriteObject(finalxml.SelectSingleNode(xpathResult, nsmgr).InnerText); } else @@ -252,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 @@ -266,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 @@ -287,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 555f031bb41..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,40 +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 ResourceManager _resourceMgr = null; - private PSCmdlet cmdletname; - private NavigationCmdletProvider _provider; + private readonly PSCmdlet cmdletname; + private readonly NavigationCmdletProvider _provider; private FileStream _fs; private StreamReader _sr; - private static ResourceManager g_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) @@ -125,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(); } } @@ -153,61 +151,54 @@ internal static void ThrowIfNotAdministrator() System.Security.Principal.WindowsPrincipal principal = new System.Security.Principal.WindowsPrincipal(currentIdentity); if (!principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator)) { - string message = g_resourceMgr.GetString("ErrorElevationNeeded"); + string message = _resourceMgr.GetString("ErrorElevationNeeded"); throw new InvalidOperationException(message); } } internal string GetResourceMsgFromResourcetext(string rscname) { - return g_resourceMgr.GetString(rscname); + return _resourceMgr.GetString(rscname); } - static internal string FormatResourceMsgFromResourcetextS(string rscname, + internal static string FormatResourceMsgFromResourcetextS(string rscname, params object[] args) { - return FormatResourceMsgFromResourcetextS(g_resourceMgr, rscname, args); + return FormatResourceMsgFromResourcetextS(_resourceMgr, rscname, args); } internal string FormatResourceMsgFromResourcetext(string resourceName, params object[] args) { - return FormatResourceMsgFromResourcetextS(this._resourceMgr, resourceName, args); + 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) @@ -226,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) @@ -250,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; } @@ -284,6 +277,7 @@ internal static Dictionary GetSessionObjCache() catch (COMException) { } + return Sessions.SessionObjCache; } @@ -302,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 { @@ -311,9 +305,10 @@ internal string GetRootNodeName(string operation, string resourceUri, string act } else { - //error + // error } } + return resultStr; } @@ -332,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) { @@ -370,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; } @@ -408,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) @@ -419,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; @@ -439,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 + @"""]"; } @@ -463,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 { @@ -485,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) @@ -522,6 +499,7 @@ internal XmlNode GetXmlNode(string xmlString, string xpathpattern, string xmlnam { nsmgr.AddNamespace("cfg", xmlnamespace); } + node = xDoc.SelectSingleNode(xpathpattern, nsmgr); return node; } @@ -548,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) { @@ -577,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) @@ -612,6 +591,7 @@ internal IWSManResourceLocator InitializeResourceLocator(Hashtable optionset, Ha { AssertError(ex.Message, false, null); } + return m_resource; } @@ -626,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); @@ -641,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); @@ -660,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; } @@ -713,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; } } } @@ -724,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; @@ -763,6 +748,7 @@ internal IWSManSession CreateSessionObject(IWSManEx wsmanObject, AuthenticationM { ProxyAuthenticationFlags = connObject.ProxyAuthenticationUseDigest(); } + if (sessionoption.ProxyCredential != null) { try @@ -778,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; @@ -838,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) @@ -866,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(); } @@ -911,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); } @@ -926,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. /// /// /// @@ -946,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) @@ -970,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); } - } } @@ -1010,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) { @@ -1028,7 +1012,7 @@ internal bool ValidateCredSSPRegistry(bool AllowFreshCredentialsValueShouldBePre } string[] valuenames = rGPOLocalMachineKey.GetValueNames(); - if (valuenames.Length <= 0) + if (valuenames.Length == 0) { return !AllowFreshCredentialsValueShouldBePresent; } @@ -1078,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); @@ -1092,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/map.json b/src/Microsoft.WSMan.Management/map.json deleted file mode 100644 index abdf1ee1783..00000000000 --- a/src/Microsoft.WSMan.Management/map.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "wmi/wmx/cmdlets/Management/ConfigProvider.cs" : "ConfigProvider.cs", - "wmi/wmx/cmdlets/Management/CredSSP.cs" : "CredSSP.cs", - "wmi/wmx/cmdlets/Management/CurrentConfigurations.cs" : "CurrentConfigurations.cs", - "wmi/wmx/cmdlets/Management/Interop.cs" : "Interop.cs", - "wmi/wmx/cmdlets/Management/InvokeWSManAction.cs" : "InvokeWSManAction.cs", - "wmi/wmx/cmdlets/Management/NewWSManSession.cs" : "NewWSManSession.cs", - "wmi/wmx/cmdlets/Management/PingWSMan.cs" : "PingWSMan.cs", - "wmi/wmx/cmdlets/Management/Set-QuickConfig.cs" : "Set-QuickConfig.cs", - "wmi/wmx/cmdlets/Management/WSManConnections.cs" : "WSManConnections.cs", - "wmi/wmx/cmdlets/Management/WsManHelper.cs" : "WsManHelper.cs", - "wmi/wmx/cmdlets/Management/WSManInstance.cs" : "WSManInstance.cs", - "wmi/wmx/cmdlets/Snapin/WsManSnapin.cs" : "WsManSnapin.cs", - "wmi/wmx/cmdlets/Management/resources/WsManResources.txt" : "resources/WsManResources.txt" -} 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/AssemblyInfo.cs b/src/Microsoft.WSMan.Runtime/AssemblyInfo.cs deleted file mode 100644 index e55ec824d47..00000000000 --- a/src/Microsoft.WSMan.Runtime/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Reflection; - -[assembly:AssemblyFileVersionAttribute("3.0.0.0")] -[assembly:AssemblyVersion("3.0.0.0")] diff --git a/src/Microsoft.WSMan.Runtime/Microsoft.WSMan.Runtime.csproj b/src/Microsoft.WSMan.Runtime/Microsoft.WSMan.Runtime.csproj index 7e9cc3ea3a7..2a9439c0274 100644 --- a/src/Microsoft.WSMan.Runtime/Microsoft.WSMan.Runtime.csproj +++ b/src/Microsoft.WSMan.Runtime/Microsoft.WSMan.Runtime.csproj @@ -1,31 +1,8 @@ - - + + - 6.0.0 - netcoreapp2.0 - true - true + PowerShell's Microsoft.WSMan.Runtime project Microsoft.WSMan.Runtime - ../signing/visualstudiopublic.snk - true - false - false - - - - $(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/Microsoft.WSMan.Runtime/map.json b/src/Microsoft.WSMan.Runtime/map.json deleted file mode 100644 index 6c9d318c87f..00000000000 --- a/src/Microsoft.WSMan.Runtime/map.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "wmi/wmx/cmdlets/Runtime/WSManSessionOption.cs" : "WSManSessionOption.cs" -} \ No newline at end of file diff --git a/src/Modules/PSGalleryModules.csproj b/src/Modules/PSGalleryModules.csproj new file mode 100644 index 00000000000..cbc47ef200c --- /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 51f1597eae5..00000000000 --- a/src/Modules/Shared/Pester +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 51f1597eae5e517ce65fed293c9d6e245b1a9d60 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.LocalAccounts/LocalAccounts.format.ps1xml b/src/Modules/Windows-Core+Full/Microsoft.PowerShell.LocalAccounts/LocalAccounts.format.ps1xml deleted file mode 100644 index 91b3b358a23..00000000000 --- a/src/Modules/Windows-Core+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-Core+Full/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.psd1 b/src/Modules/Windows-Core+Full/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.psd1 deleted file mode 100644 index b2b562f0002..00000000000 --- a/src/Modules/Windows-Core+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-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 1c67c2b7510..00000000000 --- a/src/Modules/Windows-Core+Full/PSDiagnostics/PSDiagnostics.psm1 +++ /dev/null @@ -1,444 +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)] - [string] - $OutputFilePath, - [Parameter(Position=2)] - [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 ($OutputFilePath -ne $null) - { - $executestring += " -o $OutputFilePath" - } - - if ($ProviderFilePath -ne $null) - { - $executestring += " -pf $ProviderFilePath" - } - - if ($Format -ne $null) - { - $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 a394a1931cb..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", "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.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 367a4acea48..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($callerPSCmdlet -eq $null) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "ParseMetadata") } - - if($metadataXml -eq $null) - { - $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(($metadataXML -ne $null) -and ($metadataXML.Edmx -ne $null)) - { - 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 (($schema -ne $null) -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 ($schema -eq $null) - { - Write-Error $LocalizedData.EmptySchema - continue - } - - if ($metadata.Namespace -eq $null) - { - $metaData.Namespace = $schema.Namespace - } - - foreach ($entityType in $schema.EntityType) - { - $baseType = $null - - if ($entityType.BaseType -ne $null) - { - # add it to the processing queue - $baseType = GetBaseType $entityType $metaData - if ($baseType -eq $null) - { - $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 ($complexType.BaseType -ne $null) - { - # add it to the processing queue - $baseType = GetBaseType $complexType $metaData - if ($baseType -eq $null) - { - $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 ($action.HttpMethod -eq $null) - { - if ($action.IsSideEffecting -ne $null) - { - $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 ($parameter.Nullable -ne $null) - { - $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($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "VerifyMetaData") } - if($callerPSCmdlet -eq $null) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "VerifyMetaData") } - if($progressBarStatus -eq $null) { 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 ($entitySet.Type -eq $null) - { - $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(($currentCommand.Noun -ne $null -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($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateClientSideProxyModule") } - if($callerPSCmdlet -eq $null) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "GenerateClientSideProxyModule") } - if($progressBarStatus -eq $null) { 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($callerPSCmdlet -ne $null) - { - $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($entitySet -eq $null) { throw ($LocalizedData.ArguementNullError -f "EntitySet", "GenerateClientSideProxyModule") } - if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateClientSideProxyModule") } - if($callerPSCmdlet -eq $null) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "GenerateClientSideProxyModule") } - if($progressBarStatus -eq $null) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "GenerateClientSideProxyModule") } - - $entitySetName = $entitySet.Name - if(($resourceNameMapping -ne $null) -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 ($xmlWriter -eq $null) - { - 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) | ? { -not $_.isKey } - $nullableProperties = $nonKeyProperties | ? { $_.isNullable } - $nonNullableProperties = $nonKeyProperties | ? { -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($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateGetProxyCmdlet") } - if($metaData -eq $null) { 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 (($keys -ne $null -and $keys.Length -gt 0) -or (($navigationProperties -ne $null -and $navigationProperties.Length -gt 0) -and $cmdletAdapter -ne "NetworkControllerAdapter")) - { - $xmlWriter.WriteStartElement('QueryableProperties') - $position = 0 - - $keys | ? { $_ -ne $null } | % { - $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 | ? { $_ -ne $null } | % { - $associatedType = GetAssociatedType $metaData $_ - $associatedEntitySet = GetEntitySetForEntityType $metaData $associatedType - $nvgProperty = $_ - - (GetAllProperties $associatedType) | ? { $_.IsKey } | % { - $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($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateNewProxyCmdlet") } - if($metaData -eq $null) { 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 | ? { $_ -ne $null } | % { - $associatedType = GetAssociatedType $metaData $_ - $associatedEntitySet = GetEntitySetForEntityType $metaData $associatedType - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', "Association:Create:$($associatedEntitySet.Name)") - $xmlWriter.WriteAttributeString('CmdletParameterSet', $_.Name) - - $associatedKeys = ((GetAllProperties $associatedType) | ? { $_.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($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateRemoveProxyCmdlet") } - if($metaData -eq $null) { 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 | ? { $_ -ne $null } | % { - - $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) | ? { $_.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($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateActionProxyCmdlet") } - if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateActionProxyCmdlet") } - if($action -eq $null) { throw ($LocalizedData.ArguementNullError -f "Action", "GenerateActionProxyCmdlet") } - if($noun -eq $null) { 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 | ? { $_ -ne $null } | % { - $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($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateServiceActionProxyCmdlet") } - - $xmlWriter = New-Object System.XMl.XmlTextWriter($path,$Null) - - if ($xmlWriter -eq $null) - { - throw $LocalizedData.XmlWriterInitializationError -f "ServiceActions" - } - - $xmlWriter = SaveCDXMLHeader $xmlWriter $uri 'ServiceActions' 'ServiceActions' - - $actions = $metaData.Actions | Where-Object { $_.EntitySet -eq $null } - - 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($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateModuleManifest") } - if($modulePath -eq $null) { throw ($LocalizedData.ArguementNullError -f "ModulePath", "GenerateModuleManifest") } - if($progressBarStatus -eq $null) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "GenerateModuleManifest") } - - $NestedModules = @() - foreach ($entitySet in $metaData.EntitySets) - { - $entitySetName = $entitySet.Name - if(($resourceNameMapping -ne $null) -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 ($metadataEntityDefinition -ne $null -and - $metaData -ne $null -and - $metadataEntityDefinition.BaseType -ne $null) - { - $baseType = $metaData.EntityTypes | Where {$_.Namespace+"."+$_.Name -eq $metadataEntityDefinition.BaseType} - if ($baseType -eq $null) - { - $baseType = $metaData.ComplexTypes | Where {$_.Namespace+"."+$_.Name -eq $metadataEntityDefinition.BaseType} - } - } - - if ($baseType -ne $null) - { - $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($baseType -eq $null) { throw ($LocalizedData.ArguementNullError -f "BaseType", "AddDerivedTypes") } - if($entityAndComplexTypesQueue -eq $null) { throw ($LocalizedData.ArguementNullError -f "EntityAndComplexTypesQueue", "AddDerivedTypes") } - if($namespace -eq $null) { 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($metadataEntityDefinition -eq $null) { throw ($LocalizedData.ArguementNullError -f "MetadataEntityDefinition", "ParseMetadataTypeDefinition") } - if($namespace -eq $null) { 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 | % { - if ($_ -ne $null) - { - if ($_.Nullable -ne $null) - { - $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 | % { - if ($_ -ne $null) - { - ($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($entityType -eq $null) { 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($currentEntityType -ne $null) - { - 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($fullyQualifiedName -eq $null) { 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($entityType -eq $null) { throw ($LocalizedData.ArguementNullError -f "EntityType", "GetEntitySetForEntityType") } - - $result = $metaData.EntitySets | ? { ($_.Type.Namespace -eq $entityType.Namespace) -and ($_.Type.Name -eq $entityType.Name) } - - if (($result.Count -eq 0) -and ($entityType.BaseType -ne $null)) - { - 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($callerPSCmdlet -ne $null) - { - $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($navProperty -eq $null) { throw ($LocalizedData.ArguementNullError -f "NavigationProperty", "GetAssociatedType") } - - $associationName = $navProperty.AssociationName - $association = $Metadata.Associations | ? { $_.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($xmlWriter -eq $null) { 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 ($keyProperties -ne $null) - { - $pos = AddParametersCDXML $xmlWriter $keyProperties $pos $true $prefixForKeys ":Key" $complexTypeMapping - } - - if ($mandatoryProperties -ne $null) - { - $pos = AddParametersCDXML $xmlWriter $mandatoryProperties $pos $true $null $null $complexTypeMapping - } - - if ($otherProperties -ne $null) - { - $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 | ? { $_ -ne $null } | % { - $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($typeDefinationFileName -eq $null) { throw ($LocalizedData.ArguementNullError -f "TypeDefinationFileName", "GenerateComplexTypeDefination") } - if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateComplexTypeDefination") } - if($callerPSCmdlet -eq $null) { 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($typesToBeGenerated -ne $null -and $typesToBeGenerated.Count -gt 0) - { - $complexTypeMapping = @{} - $entityTypeNameSpaceMapping = @{} - - foreach ($entityType in $typesToBeGenerated) - { - if ($entityType -ne $null) - { - $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($entityType.BaseType -ne $null) - { - $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($callerPSCmdlet -eq $null) { 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 "/" | % { - 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 ($keyParams -eq $null -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 | ? { $_ -ne $null } | %{ - $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 ($additionalProperties -ne $null) - { - 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 | ? { $_.isNullable } - } - else { - $additionalProperties = $additionalProperties | ? { -not $_.isNullable } - } - - if ($nullableProperties -eq $null) - { - $nullableProperties = $additionalProperties - } - else { - if ($nullableProperties.Count -eq 1) { - $nullableProperties = @($nullableProperties) - } - if ($additionalProperties -ne $null) { - $nullableProperties += $additionalProperties - } - } - - if ($nullableProperties -ne $null -And $keyProperties -ne $null) - { - 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 6023cc67273..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($callerPSCmdlet -eq $null) { 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 ($credential -ne $null) - { - $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 ($credential -ne $null) - { - $cmdParams.Add('Credential', $credential) - } - - if ($headers -ne $null) - { - $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($webResponse -ne $null) - { - if ($webResponse.StatusCode -eq 200) - { - $metaData = $webResponse.Content - - if ($metadata -eq $null) - { - $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($metaData -ne $null) - { - 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($localizedDataErrorString -eq $null) { throw ($LocalizedData.ArguementNullError -f "localizedDataErrorString", "VerifyMetadataHelper") } - if($localizedDataWarningString -eq $null) { 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($exception -eq $null) - { - $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($cmdletName -eq $null) { throw ($LocalizedData.ArguementNullError -f "CmdletName", "ProgressBarHelper") } - if($status -eq $null) { 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($typeName -eq $null) { 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($complexTypeMapping -ne $null -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($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLHeader") } - if($defaultNoun -eq $null) { 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($xmlWriter -eq $null) { 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($xmlWriter -eq $null) { 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 ($keyProperties -ne $null) - { - $pos = AddParametersCDXML $xmlWriter $keyProperties $pos $true $prefixForKeys ":Key" $complexTypeMapping - } - - if ($mandatoryProperties -ne $null) - { - $pos = AddParametersCDXML $xmlWriter $mandatoryProperties $pos $true $null $null $complexTypeMapping - } - - if ($otherProperties -ne $null) - { - $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 | ? { $_ -ne $null } | % { - $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($xmlWriter -eq $null) { 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 b7aa441111e..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($callerPSCmdlet -eq $null) { 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($NewMetadata -eq $null) { 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($SchemaXML -eq $null) { throw ($LocalizedData.ArguementNullError -f "SchemaXML", "ParseEntityTypes") } - - foreach ($entityType in $SchemaXML.EntityType) - { - $baseType = $null - - if ($entityType.BaseType -ne $null) - { - # add it to the processing queue - $baseType = GetBaseType $entityType $Metadata $SchemaXML.Namespace $GlobalMetadata - if ($baseType -eq $null) - { - $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($SchemaXML -eq $null) { throw ($LocalizedData.ArguementNullError -f "SchemaXML", "ParseComplexTypes") } - - foreach ($complexType in $SchemaXML.ComplexType) - { - $baseType = $null - - if ($complexType.BaseType -ne $null) - { - # add it to the processing queue - $baseType = GetBaseType $complexType $metadata $SchemaXML.Namespace $GlobalMetadata - if ($baseType -eq $null -and $entityAndComplexTypesQueue -ne $null -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($SchemaXML -eq $null) { 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($SchemaXML -eq $null) { 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 ($newEnumType.IsFlags -eq $null) - { - # 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 (($element.Value -eq $null) -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($SchemaEntityContainerXML -eq $null) { 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($SchemaEntityContainerXML -eq $null) { 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($SchemaActionsXML -eq $null) { throw ($LocalizedData.ArguementNullError -f "SchemaActionsXML", "ParseActions") } - - foreach ($action in $SchemaActionsXML) - { - # HttpMethod is only used for legacy Service Operations - if ($action.HttpMethod -eq $null) - { - $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 ($parameter.Nullable -ne $null) - { - $parameterIsNullable = [System.Convert]::ToBoolean($parameter.Nullable); - } - else - { - $parameterIsNullable = $true - } - - $newParameter = [ODataUtils.TypeProperty] @{ - "Name" = $parameter.Name; - "TypeName" = $parameter.Type; - "IsNullable" = $parameterIsNullable; - } - - $newAction.Parameters += $newParameter - } - - if ($action.EntitySet -ne $null) - { - $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($SchemaFunctionsXML -eq $null) { throw ($LocalizedData.ArguementNullError -f "SchemaFunctionsXML", "ParseFunctions") } - - foreach ($function in $SchemaFunctionsXML) - { - # HttpMethod is only used for legacy Service Operations - if ($function.HttpMethod -eq $null) - { - $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 ($parameter.Nullable -ne $null) - { - $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($MetadataXML -eq $null) { 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 ($schema -eq $null) - { - 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($callerPSCmdlet -eq $null) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "VerifyMetaData") } - if($progressBarStatus -eq $null) { 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 ($entitySet.Type -eq $null) - { - $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 ($metadataEntityDefinition -ne $null -and - $metaData -ne $null -and - $MetadataEntityDefinition.BaseType -ne $null) - { - $baseType = $Metadata.EntityTypes | Where { $_.Namespace + "." + $_.Name -eq $MetadataEntityDefinition.BaseType -or $_.Alias + "." + $_.Name -eq $MetadataEntityDefinition.BaseType } - if ($baseType -eq $null) - { - $baseType = $Metadata.ComplexTypes | Where { $_.Namespace + "." + $_.Name -eq $MetadataEntityDefinition.BaseType -or $_.Alias + "." + $_.Name -eq $MetadataEntityDefinition.BaseType } - } - - if ($baseType -eq $null) - { - # Look in other metadatas, since the class can be defined in referenced metadata - foreach ($referencedMetadata in $GlobalMetadata) - { - if (($baseType = $referencedMetadata.EntityTypes | Where { $_.Namespace + "." + $_.Name -eq $MetadataEntityDefinition.BaseType -or $_.Alias + "." + $_.Name -eq $MetadataEntityDefinition.BaseType }) -ne $null -or - ($baseType = $referencedMetadata.ComplexTypes | Where { $_.Namespace + "." + $_.Name -eq $MetadataEntityDefinition.BaseType -or $_.Alias + "." + $_.Name -eq $MetadataEntityDefinition.BaseType }) -ne $null) - { - # Found base class - break - } - } - } - } - - if ($baseType -ne $null) - { - $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 ($BaseTypeStr -ne $null) - { - - # Look for base class definition in all referenced metadatas (including entry point) - foreach ($referencedMetadata in $GlobalMetadata) - { - if (($baseType = $referencedMetadata.EntityTypes | Where { $_.Namespace + "." + $_.Name -eq $BaseTypeStr -or $_.Alias + "." + $_.Name -eq $BaseTypeStr }) -ne $null -or - ($baseType = $referencedMetadata.ComplexTypes | Where { $_.Namespace + "." + $_.Name -eq $BaseTypeStr -or $_.Alias + "." + $_.Name -eq $BaseTypeStr }) -ne $null) - { - # Found base class - break - } - } - } - - if ($baseType -ne $null) - { - $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($baseType -eq $null) { throw ($LocalizedData.ArguementNullError -f "BaseType", "AddDerivedTypes") } - if($entityAndComplexTypesQueue -eq $null) { throw ($LocalizedData.ArguementNullError -f "EntityAndComplexTypesQueue", "AddDerivedTypes") } - if($namespace -eq $null) { 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($metadataEntityDefinition -eq $null) { throw ($LocalizedData.ArguementNullError -f "MetadataEntityDefinition", "ParseMetadataTypeDefinition") } - if($namespace -eq $null) { throw ($LocalizedData.ArguementNullError -f "Namespace", "ParseMetadataTypeDefinition") } - - [ODataUtils.EntityTypeV4] $newEntityType = CreateNewEntityType -metadataEntityDefinition $metadataEntityDefinition -baseType $baseType -baseTypeStr $baseTypeStr -namespace $namespace -alias $alias -isEntity $isEntity - - if ($baseType -ne $null) - { - # Add properties inherited from BaseType - ParseMetadataBaseTypeDefinitionHelper $newEntityType $baseType - } - - # properties defined on EntityType - $newEntityType.EntityProperties += $metadataEntityDefinition.Property | % { - if ($_ -ne $null) - { - if ($_.Nullable -ne $null) - { - $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 ($baseType -eq $null) - { - # @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 ($metadataEntityDefinition -ne $null -and $metadataEntityDefinition.Key -ne $null) - { - 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 ($EntityType -ne $null -and $BaseType -ne $null) - { - # 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($metadataEntityDefinition -eq $null) { throw ($LocalizedData.ArguementNullError -f "MetadataEntityDefinition", "ParseMetadataTypeDefinition") } - if($namespace -eq $null) { 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($progressBarStatus -eq $null) { 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 $_.EntitySet -eq $null } - $functions += $Metadata.Functions | Where-Object { $_.EntitySet -eq '' -or $_.EntitySet -eq $null } - } - - 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($EntitySet -eq $null) { throw ($LocalizedData.ArguementNullError -f "EntitySet", "GenerateClientSideProxyModule") } - if($Metadata -eq $null) { 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 ($xmlWriter -eq $null) - { - 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 | ? { -not $_.isKey } - $nullableProperties = $nonKeyProperties | ? { $_.isNullable } - $nonNullableProperties = $nonKeyProperties | ? { -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 ($UriResourcePathKeyFormat -ne $null -and $UriResourcePathKeyFormat -ne '') - { - $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($Singleton -eq $null) { throw ($LocalizedData.ArguementNullError -f "Singleton", "SaveCDXMLSingletonCmdlets") } - if($Metadata -eq $null) { 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 ($xmlWriter -eq $null) - { - throw ($LocalizedData.XmlWriterInitializationError -f $singletonName) - } - - # Get associated EntityType - $associatedEntityType = $Metadata.EntityTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $singletonType -or $_.Alias + "." + $_.Name -eq $singletonType} - - if ($associatedEntityType -eq $null) - { - # Look in other metadatas, since the class can be defined in referenced metadata - foreach ($referencedMetadata in $GlobalMetadata) - { - if (($associatedEntityType = $referencedMetadata.EntityTypes | Where { $_.Namespace + "." + $_.Name -eq $singletonType -or $_.Alias + "." + $_.Name -eq $singletonType }) -ne $null) - { - # Found associated class - break - } - } - } - - if ($associatedEntityType -ne $null) - { - $xmlWriter = SaveCDXMLHeader $xmlWriter $Uri $singletonName $singletonName $CmdletAdapter - - if ($associatedEntityType.BaseType -eq $null -and $associatedEntityType.BaseTypeStr -ne $null -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 | ? { -not $_.isKey } - $nullableProperties = $nonKeyProperties | ? { $_.isNullable } - $nonNullableProperties = $nonKeyProperties | ? { -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($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLInstanceCmdlets") } - if($Metadata -eq $null) { 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 ($navProperty -ne $null) - { - $associatedType = GetAssociatedType $Metadata $GlobalMetadata $navProperty - $associatedTypeKeyProperties = $associatedType.EntityProperties | ? { $_.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 | ? { $_ -ne $null } | % { - $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 | ? { - $_.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($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLNewCmdlet") } - if($Metadata -eq $null) { 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 | ? { $_ -ne $null } | % { - $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 | ? { $_.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($entityType -eq $null) { throw ($LocalizedData.ArguementNullError -f "EntityType", "GetEntitySetForEntityType") } - - $result = $Metadata.EntitySets | ? { ($_.Type.Namespace -eq $entityType.Namespace) -and ($_.Type.Name -eq $entityType.Name) } - - if (($result.Count -eq 0) -and ($entityType.BaseType -ne $null)) - { - 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($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLRemoveCmdlet") } - if($Metadata -eq $null) { 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 | ? { $_ -ne $null } | % { - - $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 | ? { $_.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 | ? { $_.Name -eq $associationType } - - if (!$associatedType -and $GlobalMetadata -ne $null) - { - $associationFullTypeName = $navProperty.Type.Replace("Collection(", "").Replace(")", "") - - foreach ($referencedMetadata in $GlobalMetadata) - { - if (($associatedType = $referencedMetadata.EntityTypes | Where { $_.Namespace + "." + $_.Name -eq $associationFullTypeName -or $_.Alias + "." + $_.Name -eq $associationFullTypeName }) -ne $null -or - ($associatedType = $referencedMetadata.ComplexTypes | Where { $_.Namespace + "." + $_.Name -eq $associationFullTypeName -or $_.Alias + "." + $_.Name -eq $associationFullTypeName }) -ne $null -or - ($associatedType = $referencedMetadata.EnumTypes | Where { $_.Namespace + "." + $_.Name -eq $associationFullTypeName -or $_.Alias + "." + $_.Name -eq $associationFullTypeName }) -ne $null) - { - # 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($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLAction") } - if($action -eq $null) { 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 | ? { $_ -ne $null } | % { - $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($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLFunction") } - if($function -eq $null) { 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 | ? { $_ -ne $null } | % { - $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 ($xmlWriter -eq $null) - { - 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 '' -or $_.EntitySet -eq $null } - $functions += $Metadata.Functions | Where-Object { $_.EntitySet -eq '' -or $_.EntitySet -eq $null } - } - - 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 ($action -ne $null) - { - $xmlWriter = SaveCDXMLAction $xmlWriter $action '' $false $null $complexTypeMapping - } - } - } - - # Save functions - if ($functions.Length -gt 0) - { - foreach ($function in $functions) - { - if ($function -ne $null) - { - $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($progressBarStatus -eq $null) { 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 ($entityType -ne $null) - { - $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 ($entityType.Alias -ne $null -and $entityType.Alias -ne "") - { - $entityTypeShortName = $entityType.Alias + '.' + $entityType.Name - if(!$complexTypeMapping.ContainsKey($entityTypeShortName)) - { - $complexTypeMapping.Add($entityTypeShortName, $entityType.Name) - } - } - } - } - - foreach ($enumType in $enumTypesToBeGenerated) - { - if ($enumType -ne $null) - { - $enumTypeFullName = $enumType.Namespace + '.' + $enumType.Name - if(!$complexTypeMapping.ContainsKey($enumTypeFullName)) - { - $complexTypeMapping.Add($enumTypeFullName, $enumType.Name) - } - - if (($enumType.Alias -ne $null -and $enumType.Alias -ne "") -or ($metadata.Alias -ne $null -and $metadata.Alias -ne "")) - { - if ($enumType.Alias -ne $null -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 ($typeDefinition -ne $null) - { - $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 ($metadata.Alias -ne $null -and $metadata.Alias -ne "") - { - # 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 ($typeDefinition -ne $null) - { - 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 ($enumType -ne $null) - { - $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 ($entityType -ne $null) - { - $entityTypeFullName = $entityType.Namespace + '.' + $entityType.Name - - Write-Verbose ($LocalizedData.VerboseAddingTypeDefinationToGeneratedModule -f $entityTypeFullName, "$OutputModule\$typeDefinationFileName") - - if ($entityType.BaseTypeStr -ne $null -and $entityType.BaseTypeStr -ne '' -and $entityType.BaseType -eq $null) - { - # 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 (($baseType = $referencedMetadata.EntityTypes | Where { $_.Namespace + "." + $_.Name -eq $entityType.BaseTypeStr -or $_.Alias + "." + $_.Name -eq $entityType.BaseTypeStr }) -ne $null -or - ($baseType = $referencedMetadata.ComplexTypes | Where { $_.Namespace + "." + $_.Name -eq $entityType.BaseTypeStr -or $_.Alias + "." + $_.Name -eq $entityType.BaseTypeStr }) -ne $null) - { - # 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-Full/PSWorkflow/PSWorkflow.psd1 b/src/Modules/Windows-Full/PSWorkflow/PSWorkflow.psd1 deleted file mode 100644 index 491a6b33d54..00000000000 --- a/src/Modules/Windows-Full/PSWorkflow/PSWorkflow.psd1 +++ /dev/null @@ -1,15 +0,0 @@ -@{ -GUID="{3b6cc51d-c096-4b38-b78d-0fed6277096a}" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright="© Microsoft Corporation. All rights reserved." -ModuleVersion="2.0.0.0" -PowerShellVersion="3.0" -CLRVersion="4.0" -CmdletsToExport='New-PSWorkflowExecutionOption' -AliasesToExport = 'nwsn' -FunctionsToExport = 'New-PSWorkflowSession' -NestedModules="Microsoft.PowerShell.Activities","Microsoft.PowerShell.Workflow.ServiceCore","PSWorkflow.psm1" -TypesToProcess="PSWorkflow.types.ps1xml" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390817' -} diff --git a/src/Modules/Windows-Full/PSWorkflow/PSWorkflow.psm1 b/src/Modules/Windows-Full/PSWorkflow/PSWorkflow.psm1 deleted file mode 100644 index eeb09e8b03e..00000000000 --- a/src/Modules/Windows-Full/PSWorkflow/PSWorkflow.psm1 +++ /dev/null @@ -1,64 +0,0 @@ -function New-PSWorkflowSession -{ - <# - .EXTERNALHELP Microsoft.PowerShell.Workflow.ServiceCore.dll-help.xml - #> - [CmdletBinding(DefaultParameterSetName='ComputerName', HelpUri='https://go.microsoft.com/fwlink/?LinkID=238268', RemotingCapability='OwnedByCommand')] - [OutputType([System.Management.Automation.Runspaces.PSSession])] - param( - [Parameter(ParameterSetName='ComputerName', Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] - [Alias('Cn')] - [ValidateNotNullOrEmpty()] - [string[]] - $ComputerName, - - [Parameter(ParameterSetName='ComputerName', ValueFromPipelineByPropertyName=$true)] - [Object] - $Credential, - - [string[]] - $Name, - - [Parameter(ParameterSetName='ComputerName')] - [ValidateRange(1, 65535)] - [int] - $Port, - - [Parameter(ParameterSetName='ComputerName')] - [switch] - $UseSSL, - - [Parameter(ParameterSetName='ComputerName', ValueFromPipelineByPropertyName=$true)] - [string] - $ApplicationName, - - [Parameter(ParameterSetName='ComputerName')] - [int] - $ThrottleLimit, - - [Parameter(ParameterSetName='ComputerName')] - [ValidateNotNull()] - [System.Management.Automation.Remoting.PSSessionOption] - $SessionOption, - - [Parameter(ParameterSetName='ComputerName')] - [System.Management.Automation.Runspaces.AuthenticationMechanism] - $Authentication, - - [Parameter(ParameterSetName='ComputerName')] - [string] - $CertificateThumbprint, - - [Parameter(ParameterSetName='ComputerName')] - [switch] - $EnableNetworkAccess - ) - Process - { - New-PSSession -ConfigurationName Microsoft.PowerShell.Workflow @PSBoundParameters - } -} - -Set-Alias -Name nwsn -Value New-PSWorkflowSession - -Export-ModuleMember -Function New-PSWorkflowSession -Alias nwsn diff --git a/src/Modules/Windows-Full/PSWorkflow/PSWorkflow.types.ps1xml b/src/Modules/Windows-Full/PSWorkflow/PSWorkflow.types.ps1xml deleted file mode 100644 index 7459661a550..00000000000 --- a/src/Modules/Windows-Full/PSWorkflow/PSWorkflow.types.ps1xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - Microsoft.PowerShell.Activities.InlineScriptContext - - - PSStandardMembers - - - SerializationDepth - 4 - - - - - - - Microsoft.PowerShell.Activities.InlineScript - - - PSStandardMembers - - - SerializationDepth - 1 - - - SerializationMethod - SpecificProperties - - - PropertySerializationSet - - ErrorAction - PSActionRetryCount - PSActionRetryIntervalSec - PSAllowRedirection - PSApplicationName - PSAuthentication - PSCertificateThumbprint - PSComputerName - PSConfigurationName - PSConnectionRetryCount - PSConnectionRetryIntervalSec - PSConnectionUri - PSCredential - PSActionRunningTimeoutSec - PSPort - PSSessionOption - PSUseSsl - Verbose - WarningAction - WhatIf - Command - PSCommandName - DisplayName - Id - - - - - - - - Microsoft.PowerShell.Workflow.PSWorkflowJob - - - PSStandardMembers - - - SerializationMethod - SpecificProperties - - - PropertySerializationSet - - HasMoreData - StatusMessage - Location - Command - JobStateInfo - InstanceId - Id - Name - State - StartParameters - - - - - - State - $this.JobStateInfo.State.ToString() - - - - diff --git a/src/Modules/Windows-Full/PSWorkflowUtility/PSWorkflowUtility.psd1 b/src/Modules/Windows-Full/PSWorkflowUtility/PSWorkflowUtility.psd1 deleted file mode 100644 index 06615e8c0e3..00000000000 --- a/src/Modules/Windows-Full/PSWorkflowUtility/PSWorkflowUtility.psd1 +++ /dev/null @@ -1,14 +0,0 @@ -@{ -GUID="{e40bae2f-9558-479f-939b-e52407a19c86}" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright="© Microsoft Corporation. All rights reserved." -ModuleVersion="1.0.0.0" -PowerShellVersion="3.0" -CLRVersion="4.0" -AliasesToExport = @() -CmdletsToExport = @() -FunctionsToExport = 'Invoke-AsWorkflow' -NestedModules="PSWorkflowUtility.psm1" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390818' -} diff --git a/src/Modules/Windows-Full/PSWorkflowUtility/PSWorkflowUtility.psm1 b/src/Modules/Windows-Full/PSWorkflowUtility/PSWorkflowUtility.psm1 deleted file mode 100644 index 1b5075d91b2..00000000000 --- a/src/Modules/Windows-Full/PSWorkflowUtility/PSWorkflowUtility.psm1 +++ /dev/null @@ -1,34 +0,0 @@ -workflow Invoke-AsWorkflow -{ - <# - .EXTERNALHELP Microsoft.PowerShell.Workflow.ServiceCore.dll-help.xml - #> - [System.Security.SecurityCritical()] - [CmdletBinding(DefaultParameterSetName='Command', HelpUri='https://go.microsoft.com/fwlink/?LinkId=238267')] - param( - [Parameter(Mandatory=$true,ParameterSetName="Command")] - [ValidateNotNullOrEmpty()] - [String]$CommandName, - [Parameter(ParameterSetName="Command")] - [HashTable]$Parameter, - [Parameter(Mandatory=$true,ParameterSetName="Expression")] - [String]$Expression - ) - if($CommandName) - { - if($Parameter) - { - InlineScript {& $using:CommandName @using:Parameter} - } - else - { - InlineScript {& $using:CommandName} - } - } - else - { - Invoke-Expression -Command $Expression - } -} - -Export-ModuleMember -Function Invoke-AsWorkflow 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/map.json b/src/Modules/map.json deleted file mode 100644 index 2e546e2df97..00000000000 --- a/src/Modules/map.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "monad/miscfiles/modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1": "Shared/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1", - "monad/miscfiles/modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1": "Shared/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1", - "monad/miscfiles/modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1": "Shared/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1", - "monad/miscfiles/modules/Microsoft.PowerShell.Archive/ArchiveResources.psd1": "Shared/Microsoft.PowerShell.Archive/ArchiveResources.psd1", - "monad/miscfiles/modules/PSGet/PSGet.psd1": "Shared/PowerShellGet/PowerShellGet.psd1", - "monad/miscfiles/modules/PSGet/PSModule.psm1": "Shared/PowerShellGet/PSModule.psm1", - "monad/miscfiles/modules/PSGet/PSGet.Format.ps1xml": "Shared/PowerShellGet/PSGet.Format.ps1xml", - "monad/miscfiles/modules/PSGet/PSGet.Resource.psd1": "Shared/PowerShellGet/PSGet.Resource.psd1", - "monad/miscfiles/modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1": "Shared/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1", - "monad/src/oneget/PowerShell.Module/PackageManagement.psd1": "Shared/PackageManagement/PackageManagement.psd1", - "monad/src/oneget/PowerShell.Module/PackageManagement.format.ps1xml": "Shared/PackageManagement/PackageManagement.format.ps1xml", - "monad/src/oneget/providers/inbox/powershell.metaprovider/PackageProviderFunctions.psm1": "Shared/PackageManagement/PackageProviderFunctions.psm1", - "wmi/psws/PSODataUtils/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataAdapter.ps1": "Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataAdapter.ps1", - "wmi/psws/PSODataUtils/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtils.psd1": "Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtils.psd1", - "wmi/psws/PSODataUtils/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtils.psm1": "Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtils.psm1", - "wmi/psws/PSODataUtils/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtilsHelper.ps1": "Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtilsHelper.ps1", - "wmi/psws/PSODataUtils/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataV4Adapter.ps1": "Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataV4Adapter.ps1", - "wmi/psws/PSODataUtils/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtilsStrings.psd1": "Windows-Full/Microsoft.PowerShell.ODataUtils/en-US/Microsoft.PowerShell.ODataUtilsStrings.psd1", - - "wmi/WMIv2/Client/CIMCmdlets/CimCmdlets.psd1": "Windows-Core+Full/CimCmdlets/CimCmdlets.psd1", - "monad/miscfiles/modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1": "Windows-Core+Full/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1", - "monad/miscfiles/display/WSMan.format.ps1xml": "Windows-Core+Full/Microsoft.WSMan.Management/WSMan.format.ps1xml", - "monad/src/LocalAccounts/Microsoft.PowerShell.LocalAccounts.psd1": "Windows-Core+Full/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.psd1", - "monad/src/LocalAccounts/LocalAccounts.format.ps1xml": "Windows-Core+Full/Microsoft.PowerShell.LocalAccounts/LocalAccounts.format.ps1xml", - "monad/miscfiles/modules/PSDiagnostics/PSDiagnostics.psd1": "Windows-Core+Full/PSDiagnostics/PSDiagnostics.psd1", - "monad/miscfiles/modules/PSDiagnostics/PSDiagnostics.psm1": "Windows-Core+Full/PSDiagnostics/PSDiagnostics.psm1", - - "monad/miscfiles/modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1": "Windows-Full/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1", - "monad/miscfiles/modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1": "Windows-Full/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1", - "monad/miscfiles/modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1": "Windows-Full/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", - "monad/miscfiles/types/getevent.types.ps1xml": "Windows-Full/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml", - "monad/miscfiles/display/Event.format.ps1xml": "Windows-Full/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml", - "monad/miscfiles/display/Diagnostics.format.ps1xml": "Windows-Full/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml", - "monad/miscfiles/modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1": "Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", - "monad/src/ScheduledJob/PSScheduledJob.Format.ps1xml": "Windows-Full/PSScheduledJob/PSScheduledJob.Format.ps1xml", - "monad/src/ScheduledJob/PSScheduledJob.psd1": "Windows-Full/PSScheduledJob/PSScheduledJob.psd1", - "monad/src/ScheduledJob/PSScheduledJob.types.ps1xml": "Windows-Full/PSScheduledJob/PSScheduledJob.types.ps1xml", - "monad/src/m3p/product/PSWorkflow.psd1": "Windows-Full/PSWorkflow/PSWorkflow.psd1", - "monad/src/m3p/product/PSWorkflow.psm1": "Windows-Full/PSWorkflow/PSWorkflow.psm1", - "monad/src/m3p/product/PSWorkflow.types.ps1xml": "Windows-Full/PSWorkflow/PSWorkflow.types.ps1xml", - "monad/src/m3p/product/PSWorkflowUtility.psd1": "Windows-Full/PSWorkflowUtility/PSWorkflowUtility.psd1", - "monad/src/m3p/product/PSWorkflowUtility.psm1": "Windows-Full/PSWorkflowUtility/PSWorkflowUtility.psm1", - - "monad/miscfiles/modules/Microsoft.PowerShell.Diagnostics/CoreClr/Microsoft.PowerShell.Diagnostics.psd1": "Windows-Core/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", - - "monad/miscfiles/modules/Microsoft.PowerShell.Security/CoreClr/Microsoft.PowerShell.Security.psd1": "Windows-Core/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1", - "monad/miscfiles/modules/Microsoft.PowerShell.Utility/CoreClr/Microsoft.PowerShell.Utility.psd1": "Windows+Unix-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1" -} 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +