diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bba47fe..1ba00af 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,9 +6,9 @@ Below is our guidance for how to report issues, propose new features, and submit The Windows Advanced Settings team is VERY active in this GitHub Repo. In fact, we live in it all day long and carry out all our development in the open! -When the team finds issues, we file them in the repo. When we propose new ideas or think-up new features, we file new feature requests. When we work on fixes or features, we create branches and work on those improvements. And when PRs are reviewed, we review in public - including all the good, the bad, and the ugly parts. +When the team identifies an issue, we log it in the repository. When we propose new ideas or think up new features, we file new feature requests. When we work on fixes or features, we create branches and work on those improvements. And when PRs are reviewed, we review in public - including all the good, the bad, and the ugly parts. -The point of doing all this work in public is to ensure that we are holding ourselves to a high degree of transparency, and so that the community sees that we apply the same processes and hold ourselves to the same quality-bar as we do to community-submitted issues and PRs. We also want to make sure that we expose our team culture and "tribal knowledge" that is inherent in any closely-knit team, which often contains considerable value to those new to the project who are trying to figure out "why the heck does this thing look/work like this???" +The point of doing all this work in public is to ensure that we are holding ourselves to a high degree of transparency, and so that the community sees that we apply the same processes and hold ourselves to the same quality bar as we do to community-submitted issues and PRs. We also want to make sure that we expose our team culture and "tribal knowledge" that is inherent in any closely-knit team, which often contains considerable value to those new to the project who are trying to figure out "why the heck does this thing look/work like this???" ### Repo bot @@ -16,9 +16,9 @@ The team triages new issues several times a week. During triage, the team uses l We employ a bot engine to help us automate common processes within our workflow. -We drive the bot by tagging issues with specific labels which cause the bot engine to close issues, merge branches, etc. This bot engine helps us keep the repo clean by automating the process of notifying appropriate parties if/when information/follow-up is needed, and closing stale issues/PRs after reminders have remained unanswered for several days. +We drive the bot by tagging issues with specific labels, which cause the bot engine to close issues, merge branches, etc. This bot engine helps us keep the repo clean by automating the process of notifying appropriate parties if/when information/follow-up is needed, and closing stale issues/PRs after reminders have remained unanswered for several days. -Therefore, if you do file issues, or create PRs, please keep an eye on your GitHub notifications. If you do not respond to requests for information, your issues/PRs may be closed automatically. +Therefore, if you do file issues or create PRs, please keep an eye on your GitHub notifications. If you do not respond to requests for information, your issues/PRs may be closed automatically. --- ## Reporting security issues @@ -53,7 +53,7 @@ When you hit "New Issue", select the type of issue closest to what you want to r **Complete the information requested in the issue template, providing as much information as possible**. The more information you provide, the more likely your issue/ask will be understood and implemented. Helpful information includes: * What device you're running (inc. CPU type, memory, disk, etc.) -* What build of Windows your device is running +* What build of Windows is your device running 👉 Tip: Run the following in PowerShell Core @@ -80,52 +80,52 @@ When you hit "New Issue", select the type of issue closest to what you want to r Microsoft Windows [Version 10.0.18900.1001] ``` -* What tools and apps you're using (e.g. VS 2022, VSCode, etc.) +* What tools and apps you're using (e.g., VS 2022, VSCode, etc.) * Don't assume we're experts in setting up YOUR environment. Teach us to help you! * **We LOVE detailed repro steps!** What steps do we need to take to reproduce the issue? Assume we love to read repro steps. As much detail as you can stand is probably _barely_ enough detail for us! * Prefer error message text where possible or screenshots of errors if text cannot be captured. -* **If you intend to implement the fix/feature yourself then say so!** If you do not indicate otherwise we will assume that the issue is our to solve, or may label the issue as `Help-Wanted`. +* **If you intend to implement the fix/feature yourself, then say so!** If you do not indicate otherwise, we will assume that the issue is ours to solve, or we may label the issue as `Help-Wanted`. ### DO NOT post "+1" comments > ⚠ DO NOT post "+1", "me too", or similar comments - they just add noise to an issue. -If you don't have any additional info/context to add but would like to indicate that you're affected by the issue, upvote the original issue by clicking its [+😊] button and hitting 👍 (+1) icon. This way we can actually measure how impactful an issue is. +If you don't have any additional info/context to add but would like to indicate that you're affected by the issue, upvote the original issue by clicking its [+😊] button and hitting 👍 (+1) icon. This way, we can actually measure how impactful an issue is. --- -## Contributing fixes / features +## Contributing fixes/features If you're able & willing to help fix issues and/or implement features, we'd love your contribution! The best place to start is the list of ["good first issue"](https://github.com/microsoft/WindowsAdvancedSettings/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22++label%3A%22good+first+issue%22+)s. These are bugs or tasks that we on the team believe would be easier to implement for someone without any prior experience in the codebase. Once you're feeling more comfortable in the codebase, feel free to just use the ["Help Wanted"](https://github.com/microsoft/WindowsAdvancedSettings/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22+) label, or just find an issue you're interested in and hop in! Generally, we categorize issues in the following way, which is largely derived from our old internal work tracking system: -* ["Bugs"](https://github.com/microsoft/WindowsAdvancedSettings/issues?q=is%3Aopen+is%3Aissue+label%3A%22Issue-Bug%22+) are parts of the Windows Advanced Settings that are not quite working the right way. There's code to already support some scenario, but it's not quite working right. Fixing these is generally a matter of debugging the broken functionality and fixing the wrong code. +* ["Bugs"](https://github.com/microsoft/WindowsAdvancedSettings/issues?q=is%3Aopen+is%3Aissue+label%3A%22Issue-Bug%22+) are parts of the Windows Advanced Settings that are not working the right way. There's code to already support some scenarios, but it's not quite working right. Fixing these is generally a matter of debugging the broken functionality and fixing the wrong code. * ["Tasks"](https://github.com/microsoft/WindowsAdvancedSettings/issues?q=is%3Aopen+is%3Aissue+label%3A%22Issue-Task%22+) are usually new pieces of functionality that aren't yet implemented for the Windows Advanced Settings. These are usually smaller features, which we believe - could be a single, atomic PR - don't require much design consideration, or we've already written the spec for the larger feature they belong to. -* ["Features"](https://github.com/microsoft/WindowsAdvancedSettings/issues?q=is%3Aopen+is%3Aissue+label%3A%22Issue-Feature%22+) are larger pieces of new functionality. These are usually things we believe would require larger discussion of how they should be implemented, or they'll require some complicated new settings. They might just be features that are composed of many individual tasks. Often times, with features, we like to have a spec written before development work is started, to make sure we're all on the same page (see below). +* ["Features"](https://github.com/microsoft/WindowsAdvancedSettings/issues?q=is%3Aopen+is%3Aissue+label%3A%22Issue-Feature%22+) are larger pieces of new functionality. These are usually things we believe would require a larger discussion of how they should be implemented, or they'll require some complicated new settings. They might just be features that are composed of many individual tasks. Oftentimes, with features, we like to have a spec written before development work is started, to make sure we're all on the same page (see below). Bugs and tasks are obviously the easiest to get started with, but don't feel afraid of features either! -Often, we like to assign issues that generally belong to somebody's area of expertise to the team member that owns that area. This doesn't mean the community can't jump in -- they should reach out and have a chat with the assignee to see if it'd okay to take. If an issue's been assigned more than a month ago, there's a good chance it's fair game to try yourself. +Often, we like to assign issues that generally belong to somebody's area of expertise to the team member who owns that area. This doesn't mean the community can't jump in -- they should reach out and have a chat with the assignee to see if it'd be okay to take. If an issue's been assigned more than a month ago, there's a good chance it's fair game to try yourself. ### To spec or not to spec -Some issues/features may be quick and simple to describe and understand. For such scenarios, once a team member has agreed with your approach, skip ahead to the section headed "Fork, Branch, and Create your PR", below. +Some issues/features may be quick and simple to describe and understand. For such scenarios, once a team member has agreed with your approach, skip ahead to the section headed "Fork, Branch, and Create your PR" below. Small issues that do not require a spec will be labelled `Issue-Bug` or `Issue-Task`. -However, some issues/features will require careful thought & formal design before implementation. For these scenarios, we'll request that a spec is written and the associated issue will be labeled `Issue-Feature`. +However, some issues/features will require careful thought & formal design before implementation. For these scenarios, we'll request that a spec be written, and the associated issue will be labeled `Issue-Feature`. -Specs help collaborators discuss different approaches to solve a problem, describe how the feature will behave, how the feature will impact the user, what happens if something goes wrong, etc. Driving towards agreement in a spec, before any code is written, often results in simpler code, and less wasted effort in the long run. +Specs help collaborators discuss different approaches to solve a problem, describe how the feature will behave, how the feature will impact the user, what happens if something goes wrong, etc. Driving towards agreement in a spec, before any code is written, often results in simpler code and less wasted effort in the long run. -Specs will be managed in a very similar manner as code contributions so please follow the "[Fork, branch and create your PR](CONTRIBUTING.md#fork-clone-branch-and-create-your-pr)" section below. +Specs will be managed in a very similar manner as code contributions, so please follow the "[Fork, branch and create your PR](CONTRIBUTING.md#fork-clone-branch-and-create-your-pr)" section below. ### Writing / Contributing to a spec -To write/contribute to a spec: fork, branch and commit via PRs, as you would with any code changes. +To write/contribute to a spec: fork, branch, and commit via PRs, as you would with any code changes. Specs are written in markdown, stored under the [`\docs\specs`](./docs/specs) folder and named `[issue id] - [spec description].md`. @@ -135,15 +135,15 @@ Team members will be happy to help review specs and guide them to completion. ### Help wanted -Once the team has approved an issue/spec, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/WindowsAdvancedSettings/labels/Help%20Wanted). +Once the team has approved an issue/spec, development can proceed. If no developers are immediately available, the spec can be parked, ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/WindowsAdvancedSettings/labels/Help%20Wanted). --- ## Development -### Fork, clone, branch and create your PR +### Fork, clone, branch, and create your PR -Once you've discussed your proposed feature/fix/etc. with a team member, and you've agreed an approach or a spec has been written and approved, it's time to start development: +Once you've discussed your proposed feature/fix/etc. With a team member, and you've agreed on an approach or a spec has been written and approved, it's time to start development: 1. Fork the repo if you haven't already 2. Clone your fork locally @@ -154,7 +154,7 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and you ### Code review -When you'd like the team to take a look, (even if the work is not yet fully-complete), mark the PR as 'Ready For Review' so that the team can review your work and provide comments, suggestions, and request changes. It may take several cycles, but the end result will be solid, testable, conformant code that is safe for us to merge. +When you'd like the team to take a look (even if the work is not yet fully complete), mark the PR as 'Ready For Review' so that the team can review your work and provide comments, suggestions, and request changes. It may take several cycles, but the result will be solid, testable, conformant code that is safe for us to merge. ### Merge @@ -164,4 +164,4 @@ Once your code has been reviewed and approved by the requisite number of team me ## Thank you -Thank you in advance for your contribution! Now, [what's next on the list](https://github.com/microsoft/WindowsAdvancedSettings/labels/Help%20Wanted)? 😜 \ No newline at end of file +Thank you in advance for your contribution! Now, [what's next on the list](https://github.com/microsoft/WindowsAdvancedSettings/labels/Help%20Wanted)? 😜 diff --git a/README.md b/README.md index aae66f5..b84b949 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ We are excited to work alongside you, our amazing community, to build and enhanc The easiest way to communicate with the team is via GitHub issues. -Please file new issues, feature requests and suggestions, but **DO search for similar open/closed preexisting issues before creating a new issue.** +Please file new issues, feature requests and suggestions, but **search for similar open/closed preexisting issues before creating a new issue.** If you would like to ask a question that you feel doesn't warrant an issue (yet), please reach out to us via Twitter: @@ -66,8 +66,8 @@ We welcome contributions and suggestions. Most contributions require you to agre When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. ## Trademarks -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies. +This project may contain trademarks or logos for projects, products, or services. Authorised use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not confuse or imply Microsoft sponsorship. Any use of third-party trademarks or logos is subject to those third parties ' policies. diff --git a/SUPPORT.md b/SUPPORT.md index f7e9245..1ab15d4 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -2,7 +2,7 @@ ## How to file issues and get help -For help and questions about using this project, please look at readme. +For help and questions about using this project, please refer to the README. ## Microsoft Support Policy diff --git a/build/store/media/de-DE/AdvancedSettings.png b/build/store/media/de-DE/AdvancedSettings.png index be0fba7..6c6ef7e 100644 Binary files a/build/store/media/de-DE/AdvancedSettings.png and b/build/store/media/de-DE/AdvancedSettings.png differ diff --git a/build/store/media/de-DE/FileExplorerSourceIntegration.png b/build/store/media/de-DE/FileExplorerSourceIntegration.png index 184fb0b..0eb3a66 100644 Binary files a/build/store/media/de-DE/FileExplorerSourceIntegration.png and b/build/store/media/de-DE/FileExplorerSourceIntegration.png differ diff --git a/build/store/media/en-us/AdvancedSettings.png b/build/store/media/en-us/AdvancedSettings.png index be0fba7..6c6ef7e 100644 Binary files a/build/store/media/en-us/AdvancedSettings.png and b/build/store/media/en-us/AdvancedSettings.png differ diff --git a/build/store/media/en-us/FileExplorerSourceIntegration.png b/build/store/media/en-us/FileExplorerSourceIntegration.png index 184fb0b..0eb3a66 100644 Binary files a/build/store/media/en-us/FileExplorerSourceIntegration.png and b/build/store/media/en-us/FileExplorerSourceIntegration.png differ diff --git a/build/store/media/es-ES/AdvancedSettings.png b/build/store/media/es-ES/AdvancedSettings.png index be0fba7..6c6ef7e 100644 Binary files a/build/store/media/es-ES/AdvancedSettings.png and b/build/store/media/es-ES/AdvancedSettings.png differ diff --git a/build/store/media/es-ES/FileExplorerSourceIntegration.png b/build/store/media/es-ES/FileExplorerSourceIntegration.png index 184fb0b..0eb3a66 100644 Binary files a/build/store/media/es-ES/FileExplorerSourceIntegration.png and b/build/store/media/es-ES/FileExplorerSourceIntegration.png differ diff --git a/build/store/media/fr-FR/AdvancedSettings.png b/build/store/media/fr-FR/AdvancedSettings.png index be0fba7..6c6ef7e 100644 Binary files a/build/store/media/fr-FR/AdvancedSettings.png and b/build/store/media/fr-FR/AdvancedSettings.png differ diff --git a/build/store/media/fr-FR/FileExplorerSourceIntegration.png b/build/store/media/fr-FR/FileExplorerSourceIntegration.png index 184fb0b..0eb3a66 100644 Binary files a/build/store/media/fr-FR/FileExplorerSourceIntegration.png and b/build/store/media/fr-FR/FileExplorerSourceIntegration.png differ diff --git a/build/store/media/it-IT/AdvancedSettings.png b/build/store/media/it-IT/AdvancedSettings.png index be0fba7..6c6ef7e 100644 Binary files a/build/store/media/it-IT/AdvancedSettings.png and b/build/store/media/it-IT/AdvancedSettings.png differ diff --git a/build/store/media/it-IT/FileExplorerSourceIntegration.png b/build/store/media/it-IT/FileExplorerSourceIntegration.png index 184fb0b..0eb3a66 100644 Binary files a/build/store/media/it-IT/FileExplorerSourceIntegration.png and b/build/store/media/it-IT/FileExplorerSourceIntegration.png differ diff --git a/build/store/media/ja-JP/AdvancedSettings.png b/build/store/media/ja-JP/AdvancedSettings.png index be0fba7..6c6ef7e 100644 Binary files a/build/store/media/ja-JP/AdvancedSettings.png and b/build/store/media/ja-JP/AdvancedSettings.png differ diff --git a/build/store/media/ja-JP/FileExplorerSourceIntegration.png b/build/store/media/ja-JP/FileExplorerSourceIntegration.png index 184fb0b..0eb3a66 100644 Binary files a/build/store/media/ja-JP/FileExplorerSourceIntegration.png and b/build/store/media/ja-JP/FileExplorerSourceIntegration.png differ diff --git a/build/store/media/ko-KR/AdvancedSettings.png b/build/store/media/ko-KR/AdvancedSettings.png index be0fba7..6c6ef7e 100644 Binary files a/build/store/media/ko-KR/AdvancedSettings.png and b/build/store/media/ko-KR/AdvancedSettings.png differ diff --git a/build/store/media/ko-KR/FileExplorerSourceIntegration.png b/build/store/media/ko-KR/FileExplorerSourceIntegration.png index 184fb0b..0eb3a66 100644 Binary files a/build/store/media/ko-KR/FileExplorerSourceIntegration.png and b/build/store/media/ko-KR/FileExplorerSourceIntegration.png differ diff --git a/build/store/media/pt-BR/AdvancedSettings.png b/build/store/media/pt-BR/AdvancedSettings.png index be0fba7..6c6ef7e 100644 Binary files a/build/store/media/pt-BR/AdvancedSettings.png and b/build/store/media/pt-BR/AdvancedSettings.png differ diff --git a/build/store/media/pt-BR/FileExplorerSourceIntegration.png b/build/store/media/pt-BR/FileExplorerSourceIntegration.png index 184fb0b..0eb3a66 100644 Binary files a/build/store/media/pt-BR/FileExplorerSourceIntegration.png and b/build/store/media/pt-BR/FileExplorerSourceIntegration.png differ diff --git a/build/store/media/ru-RU/AdvancedSettings.png b/build/store/media/ru-RU/AdvancedSettings.png index be0fba7..6c6ef7e 100644 Binary files a/build/store/media/ru-RU/AdvancedSettings.png and b/build/store/media/ru-RU/AdvancedSettings.png differ diff --git a/build/store/media/ru-RU/FileExplorerSourceIntegration.png b/build/store/media/ru-RU/FileExplorerSourceIntegration.png index 184fb0b..0eb3a66 100644 Binary files a/build/store/media/ru-RU/FileExplorerSourceIntegration.png and b/build/store/media/ru-RU/FileExplorerSourceIntegration.png differ diff --git a/build/store/media/zh-CN/AdvancedSettings.png b/build/store/media/zh-CN/AdvancedSettings.png index be0fba7..6c6ef7e 100644 Binary files a/build/store/media/zh-CN/AdvancedSettings.png and b/build/store/media/zh-CN/AdvancedSettings.png differ diff --git a/build/store/media/zh-CN/FileExplorerSourceIntegration.png b/build/store/media/zh-CN/FileExplorerSourceIntegration.png index 184fb0b..0eb3a66 100644 Binary files a/build/store/media/zh-CN/FileExplorerSourceIntegration.png and b/build/store/media/zh-CN/FileExplorerSourceIntegration.png differ diff --git a/build/store/media/zh-TW/AdvancedSettings.png b/build/store/media/zh-TW/AdvancedSettings.png index be0fba7..6c6ef7e 100644 Binary files a/build/store/media/zh-TW/AdvancedSettings.png and b/build/store/media/zh-TW/AdvancedSettings.png differ diff --git a/build/store/media/zh-TW/FileExplorerSourceIntegration.png b/build/store/media/zh-TW/FileExplorerSourceIntegration.png index 184fb0b..0eb3a66 100644 Binary files a/build/store/media/zh-TW/FileExplorerSourceIntegration.png and b/build/store/media/zh-TW/FileExplorerSourceIntegration.png differ diff --git a/src/AdvancedSettings/Assets/Dev/StoreLogo.png b/src/AdvancedSettings/Assets/Dev/StoreLogo.png index 3c5ca57..41adc15 100644 Binary files a/src/AdvancedSettings/Assets/Dev/StoreLogo.png and b/src/AdvancedSettings/Assets/Dev/StoreLogo.png differ diff --git a/src/AdvancedSettings/Assets/InitializationPage/AppList.scale-400.png b/src/AdvancedSettings/Assets/InitializationPage/AppList.scale-400.png index fcdf841..573ce5b 100644 Binary files a/src/AdvancedSettings/Assets/InitializationPage/AppList.scale-400.png and b/src/AdvancedSettings/Assets/InitializationPage/AppList.scale-400.png differ diff --git a/src/AdvancedSettings/Assets/Production/StoreLogo.png b/src/AdvancedSettings/Assets/Production/StoreLogo.png index 3c5ca57..c4f84f7 100644 Binary files a/src/AdvancedSettings/Assets/Production/StoreLogo.png and b/src/AdvancedSettings/Assets/Production/StoreLogo.png differ diff --git a/src/AdvancedSettings/WindowsAdvancedSettings.csproj b/src/AdvancedSettings/WindowsAdvancedSettings.csproj index 9975fff..2d97b30 100644 --- a/src/AdvancedSettings/WindowsAdvancedSettings.csproj +++ b/src/AdvancedSettings/WindowsAdvancedSettings.csproj @@ -1,34 +1,107 @@ - + + + - - - - Exe - - - WinExe - - + + WindowsAdvancedSettings - Dev - Assets\AdvancedSettings.ico - Assets\AdvancedSettings.ico - app.manifest + + x86;x64;arm64 win-x86;win-x64;win-arm64 - $(SolutionDir)\src\Common\PublishProfiles\win-$(Platform).pubxml + + + net8.0-windows10.0.22621.0 + + enable enable - true + + true + + + app.manifest + + + true true - true + + + true + + + true + + + + + Dev + + + Exe + WinExe + + $(DefineConstants);DISABLE_XAML_GENERATED_MAIN + $(DefineConstants);STABLE_BUILD + + + Assets\Dev + Assets\Production + + + Assets\AdvancedSettings.ico + + + + + Package-Dev.appxmanifest + Package.appxmanifest + + + + + true + true + false + + + $(SolutionDir)\src\Common\PublishProfiles\win-$(Platform).pubxml + + + + + true + true + true + true + + + + + + + + + + + + true + true + + + + + + + + @@ -37,86 +110,64 @@ Spectre - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - Designer - - + + - - - - - Designer - - - - + - + Always - - - - - - true - - - - + PreserveNewest Assets\NOTICE.txt - - + + - - + + - - $(DefineConstants);STABLE_BUILD - net8.0-windows10.0.22621.0 - - \ No newline at end of file + diff --git a/src/Common/Helpers/GPOHelper.cs b/src/Common/Helpers/GPOHelper.cs index fec0fc6..ad764f2 100644 --- a/src/Common/Helpers/GPOHelper.cs +++ b/src/Common/Helpers/GPOHelper.cs @@ -1,8 +1,9 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.Win32; using Serilog; +using System; namespace WindowsAdvancedSettings.Common.Helpers; @@ -12,73 +13,116 @@ public class GPOHelper private enum GpoRuleConfigured { - WrongValue = -3, // The policy is set to an unrecognized value - Unavailable = -2, // Couldn't access registry - NotConfigured = -1, // Policy is not configured - Disabled = 0, // Policy is disabled - Enabled = 1, // Policy is enabled + WrongValue = -3, // The policy is set to an unrecognized value + Unavailable = -2, // Couldn't access registry + NotConfigured = -1, // Policy is not configured + Disabled = 0, // Policy is disabled + Enabled = 1, // Policy is enabled } - // Registry path where gpo policy values are stored private const string PoliciesScopeMachine = "HKEY_LOCAL_MACHINE"; - private const string PoliciesPath = @"\SOFTWARE\Policies\Microsoft\Windows\WindowsAdvancedSettings"; - - // Registry value names + private const string PoliciesRegistryPath = @"HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsAdvancedSettings"; private const string PolicyConfigureEnabledWindowsAdvancedSettings = "ConfigureEnabledWindowsAdvancedSettings"; private static GpoRuleConfigured GetConfiguredValue(string registryValueName) { + ArgumentNullException.ThrowIfNull(registryValueName, nameof(registryValueName)); + try { var rawValue = Registry.GetValue( - keyName: PoliciesScopeMachine + PoliciesPath, + keyName: PoliciesScopeMachine + @"\SOFTWARE\Policies\Microsoft\Windows\WindowsAdvancedSettings", valueName: registryValueName, - defaultValue: GpoRuleConfigured.NotConfigured); - - _log.Debug($"Registry value {registryValueName} set to {rawValue}"); - - // Value will be null if the subkey specified by keyName does not exist. - if (rawValue == null) - { - return GpoRuleConfigured.NotConfigured; - } - else if (rawValue is not int && rawValue is not GpoRuleConfigured) - { - return GpoRuleConfigured.WrongValue; - } - else - { - return (GpoRuleConfigured)rawValue; - } + defaultValue: null); + + _log.Debug("Retrieved registry value {RegistryValueName} with result {@RawValue}", + registryValueName, rawValue); + + // Try to convert the registry value to our enum + var convertedValue = ConvertToGpoRuleConfigured(rawValue); + return convertedValue; } - catch (System.Security.SecurityException) + catch (System.Security.SecurityException ex) { - // The user does not have the permissions required to read from the registry key. + _log.Warning(ex, "Access denied when reading registry value {RegistryValueName}", registryValueName); return GpoRuleConfigured.Unavailable; } - catch (System.IO.IOException) + catch (System.IO.IOException ex) { - // The RegistryKey that contains the specified value has been marked for deletion. + _log.Warning(ex, "IO error when accessing registry value {RegistryValueName}", registryValueName); return GpoRuleConfigured.Unavailable; } - catch (System.ArgumentException) + catch (System.ArgumentException ex) { - // keyName does not begin with a valid registry root. + _log.Warning(ex, "Invalid registry path for value {RegistryValueName}", registryValueName); return GpoRuleConfigured.NotConfigured; } + catch (Exception ex) + { + _log.Error(ex, "Unexpected error reading registry value {RegistryValueName}", registryValueName); + return GpoRuleConfigured.Unavailable; + } + } + + private static GpoRuleConfigured ConvertToGpoRuleConfigured(object? rawValue) + { + // Use pattern matching for cleaner type checking + return rawValue switch + { + null => GpoRuleConfigured.NotConfigured, + int intValue => Enum.IsDefined(typeof(GpoRuleConfigured), intValue) + ? (GpoRuleConfigured)intValue + : GpoRuleConfigured.WrongValue, + GpoRuleConfigured enumValue => enumValue, + long longValue => TryConvertLongToGpo(longValue), + string stringValue => TryConvertStringToGpo(stringValue), + _ => GpoRuleConfigured.WrongValue + }; + } + + private static GpoRuleConfigured TryConvertLongToGpo(long value) + { + try + { + var intValue = Convert.ToInt32(value); + return Enum.IsDefined(typeof(GpoRuleConfigured), intValue) + ? (GpoRuleConfigured)intValue + : GpoRuleConfigured.WrongValue; + } + catch (OverflowException) + { + return GpoRuleConfigured.WrongValue; + } + } + + private static GpoRuleConfigured TryConvertStringToGpo(string value) + { + if (int.TryParse(value, out int intValue)) + { + return Enum.IsDefined(typeof(GpoRuleConfigured), intValue) + ? (GpoRuleConfigured)intValue + : GpoRuleConfigured.WrongValue; + } + + return GpoRuleConfigured.WrongValue; } private static bool EvaluateConfiguredValue(string registryValueName, GpoRuleConfigured defaultValue) { + ArgumentNullException.ThrowIfNull(registryValueName, nameof(registryValueName)); + var configuredValue = GetConfiguredValue(registryValueName); - if (configuredValue < 0) - { - if (configuredValue != GpoRuleConfigured.NotConfigured) - { - // Only log an error if a configuration was attempted but was incorrect. NotConfigured is expected state. - _log.Error($"Registry value {registryValueName} set to {configuredValue}, using default {defaultValue} instead."); - } + // If we got an error state (negative value) and it's not NotConfigured, log it + if (configuredValue < 0 && configuredValue != GpoRuleConfigured.NotConfigured) + { + _log.Error("Registry value {RegistryValueName} is in invalid state {State}, using default {DefaultValue}", + registryValueName, configuredValue, defaultValue); + configuredValue = defaultValue; + } + else if (configuredValue < 0) + { + // NotConfigured is normal - use default without error logging configuredValue = defaultValue; } @@ -87,7 +131,8 @@ private static bool EvaluateConfiguredValue(string registryValueName, GpoRuleCon public static bool GetConfiguredEnabledWindowsAdvancedSettingsValue() { - var defaultValue = GpoRuleConfigured.Enabled; - return EvaluateConfiguredValue(PolicyConfigureEnabledWindowsAdvancedSettings, defaultValue); + return EvaluateConfiguredValue( + PolicyConfigureEnabledWindowsAdvancedSettings, + GpoRuleConfigured.Enabled); } -} +} \ No newline at end of file diff --git a/src/FileExplorerGitIntegration/Program.cs b/src/FileExplorerGitIntegration/Program.cs index 6a03df2..81ec323 100644 --- a/src/FileExplorerGitIntegration/Program.cs +++ b/src/FileExplorerGitIntegration/Program.cs @@ -1,94 +1,190 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using FileExplorerGitIntegration.Models; using Microsoft.Extensions.Configuration; using Microsoft.Windows.AppLifecycle; using Serilog; +using Serilog.Core; using Windows.ApplicationModel.Activation; using WindowsAdvancedSettings.Common.Helpers; namespace FileExplorerGitIntegration; +/// +/// Entry point for the File Explorer Git Integration application. +/// Manages single-instance enforcement and COM server activation. +/// public sealed class Program { + private const string MainInstanceKey = "FileExplorerGitIntegration_MainInstance"; + private const string ComServerArgument = "-RegisterProcessAsComServer"; + private const string LogSubDirectory = "FileExplorerGitIntegration"; + + private static Logger? _logger; + [MTAThread] - public static async Task Main([System.Runtime.InteropServices.WindowsRuntime.ReadOnlyArray] string[] args) + public static async Task Main(string[] args) { - // Set up Logging - Environment.SetEnvironmentVariable("WINDOWSADVANCEDSETTINGS_LOG_ROOT", Path.Join(Logging.LogFolderRoot, "FileExplorerGitIntegration")); - var configuration = new ConfigurationBuilder() - .AddJsonFile("appsettings_FileExplorerGitIntegration.json") - .Build(); - Log.Logger = new LoggerConfiguration() - .ReadFrom.Configuration(configuration) - .CreateLogger(); - - Log.Information($"Launched with args: {string.Join(' ', args.ToArray())}"); - - // Force the app to be single instanced - // Get or register the main instance - var mainInstance = AppInstance.FindOrRegisterForKey("mainInstance"); - var activationArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); - - // If the main instance isn't this current instance - if (!mainInstance.IsCurrent) + try + { + InitializeLogging(); + _logger?.Information("Application started with arguments: {Args}", string.Join(" ", args)); + + // Enforce single-instance behavior + if (!await EnsureSingleInstanceAsync()) + { + return 0; + } + + // Handle COM server activation or exit + return HandleActivation(args); + } + catch (Exception ex) + { + _logger?.Fatal(ex, "Fatal error in application entry point"); + return 1; + } + finally { - Log.Information($"Not main instance, redirecting."); - await mainInstance.RedirectActivationToAsync(activationArgs); Log.CloseAndFlush(); - return; } + } - // Otherwise, we're in the main instance - // Register for activation redirection - AppInstance.GetCurrent().Activated += AppActivationRedirected; - - if (args.Length > 0 && args[0] == "-RegisterProcessAsComServer") + /// + /// Initializes Serilog configuration and sets up the logger instance. + /// + private static void InitializeLogging() + { + try { - HandleCOMServerActivation(); + Environment.SetEnvironmentVariable( + "WINDOWSADVANCEDSETTINGS_LOG_ROOT", + Path.Join(Logging.LogFolderRoot, LogSubDirectory)); + + var configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings_FileExplorerGitIntegration.json") + .Build(); + + _logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration) + .Enrich.FromLogContext() + .CreateLogger(); + + Log.Logger = _logger; } - else + catch (Exception ex) { - Log.Warning("Not being launched as a ComServer... exiting."); + // Fallback logging if configuration fails + Console.Error.WriteLine($"Failed to initialize logging: {ex.Message}"); + throw; } + } - Log.CloseAndFlush(); + /// + /// Ensures only one instance of the application is running. + /// If another instance exists, redirects the activation to it. + /// + /// True if this is the main instance, false if activation was redirected. + private static async Task EnsureSingleInstanceAsync() + { + try + { + var mainInstance = AppInstance.FindOrRegisterForKey(MainInstanceKey); + var activationArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); + + if (mainInstance.IsCurrent) + { + _logger?.Information("Main instance registered successfully"); + AppInstance.GetCurrent().Activated += OnAppActivationRedirected; + return true; + } + + _logger?.Information("Not main instance; redirecting activation"); + await mainInstance.RedirectActivationToAsync(activationArgs); + return false; + } + catch (Exception ex) + { + _logger?.Error(ex, "Error ensuring single instance"); + throw; + } } - private static void AppActivationRedirected(object? sender, Microsoft.Windows.AppLifecycle.AppActivationArguments activationArgs) + /// + /// Handles application activation and determines the appropriate action. + /// + private static int HandleActivation(string[] args) { - Log.Information($"Redirected with kind: {activationArgs.Kind}"); + if (args.Length > 0 && args[0].Equals(ComServerArgument, StringComparison.OrdinalIgnoreCase)) + { + return ActivateComServer(); + } + + _logger?.Warning("Application not launched as COM server; exiting"); + return 1; + } - // Handle COM server - if (activationArgs.Kind == ExtendedActivationKind.Launch) + /// + /// Handles redirected activation events from secondary instances. + /// + private static void OnAppActivationRedirected(object? sender, AppActivationArguments activationArgs) + { + try { - var d = activationArgs.Data as ILaunchActivatedEventArgs; - var args = d?.Arguments.Split(); + _logger?.Information("Activation redirected with kind: {ActivationKind}", activationArgs.Kind); - if (args?.Length > 1 && args[1] == "-RegisterProcessAsComServer") + if (activationArgs.Kind == ExtendedActivationKind.Launch) { - Log.Information($"Activation COM Registration Redirect: {string.Join(' ', args.ToList())}"); - HandleCOMServerActivation(); + var launchArgs = activationArgs.Data as ILaunchActivatedEventArgs; + var args = launchArgs?.Arguments.Split(); + + if (args?.Length > 1 && args[1].Equals(ComServerArgument, StringComparison.OrdinalIgnoreCase)) + { + _logger?.Information("Received COM registration request via activation"); + ActivateComServer(); + } } } + catch (Exception ex) + { + _logger?.Error(ex, "Error handling activation redirect"); + } } - private static void HandleCOMServerActivation() + /// + /// Activates the COM server if it's enabled by policy. + /// + /// Exit code indicating success or failure. + private static int ActivateComServer() { - var gpoPolicyEnabled = GPOHelper.GetConfiguredEnabledWindowsAdvancedSettingsValue(); - if (!gpoPolicyEnabled) + try { - Log.Information($"Windows Advanced Settings is disabled by policy, exiting."); - return; - } + var gpoPolicyEnabled = GPOHelper.GetConfiguredEnabledWindowsAdvancedSettingsValue(); + if (!gpoPolicyEnabled) + { + _logger?.Information("Windows Advanced Settings disabled by policy; exiting"); + return 1; + } - Log.Information($"Activating COM Server"); + _logger?.Information("Activating COM server"); - RepositoryCache cache = new RepositoryCache(); - using var gitLocalRepositoryProviderServer = new GitLocalRepositoryProviderServer(); - var gitLocalRepositoryProviderInstance = new GitLocalRepositoryProviderFactory(cache); - gitLocalRepositoryProviderServer.RegisterGitRepositoryProviderServer(() => gitLocalRepositoryProviderInstance); - gitLocalRepositoryProviderServer.Run(); + var cache = new RepositoryCache(); + using var gitLocalRepositoryProviderServer = new GitLocalRepositoryProviderServer(); + var gitLocalRepositoryProviderInstance = new GitLocalRepositoryProviderFactory(cache); + + gitLocalRepositoryProviderServer.RegisterGitRepositoryProviderServer( + () => gitLocalRepositoryProviderInstance); + + gitLocalRepositoryProviderServer.Run(); + + _logger?.Information("COM server completed execution"); + return 0; + } + catch (Exception ex) + { + _logger?.Error(ex, "Fatal error in COM server activation"); + return 1; + } } } diff --git a/test/AdvancedSettings.Tester/Assets/LockScreenLogo.scale-200.png b/test/AdvancedSettings.Tester/Assets/LockScreenLogo.scale-200.png index 735f57a..25ef6bf 100644 Binary files a/test/AdvancedSettings.Tester/Assets/LockScreenLogo.scale-200.png and b/test/AdvancedSettings.Tester/Assets/LockScreenLogo.scale-200.png differ diff --git a/test/AdvancedSettings.Tester/Assets/SplashScreen.scale-200.png b/test/AdvancedSettings.Tester/Assets/SplashScreen.scale-200.png index 023e7f1..2a1d28f 100644 Binary files a/test/AdvancedSettings.Tester/Assets/SplashScreen.scale-200.png and b/test/AdvancedSettings.Tester/Assets/SplashScreen.scale-200.png differ diff --git a/test/AdvancedSettings.Tester/Assets/Square150x150Logo.scale-200.png b/test/AdvancedSettings.Tester/Assets/Square150x150Logo.scale-200.png index af49fec..a0cc566 100644 Binary files a/test/AdvancedSettings.Tester/Assets/Square150x150Logo.scale-200.png and b/test/AdvancedSettings.Tester/Assets/Square150x150Logo.scale-200.png differ diff --git a/test/AdvancedSettings.Tester/Assets/Square44x44Logo.scale-200.png b/test/AdvancedSettings.Tester/Assets/Square44x44Logo.scale-200.png index ce342a2..90a1fc6 100644 Binary files a/test/AdvancedSettings.Tester/Assets/Square44x44Logo.scale-200.png and b/test/AdvancedSettings.Tester/Assets/Square44x44Logo.scale-200.png differ diff --git a/test/AdvancedSettings.Tester/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/test/AdvancedSettings.Tester/Assets/Square44x44Logo.targetsize-24_altform-unplated.png index f6c02ce..8758f61 100644 Binary files a/test/AdvancedSettings.Tester/Assets/Square44x44Logo.targetsize-24_altform-unplated.png and b/test/AdvancedSettings.Tester/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/test/AdvancedSettings.Tester/Assets/StoreLogo.png b/test/AdvancedSettings.Tester/Assets/StoreLogo.png index 7385b56..d7d18da 100644 Binary files a/test/AdvancedSettings.Tester/Assets/StoreLogo.png and b/test/AdvancedSettings.Tester/Assets/StoreLogo.png differ diff --git a/test/AdvancedSettings.Tester/Assets/Wide310x150Logo.scale-200.png b/test/AdvancedSettings.Tester/Assets/Wide310x150Logo.scale-200.png index 288995b..6326324 100644 Binary files a/test/AdvancedSettings.Tester/Assets/Wide310x150Logo.scale-200.png and b/test/AdvancedSettings.Tester/Assets/Wide310x150Logo.scale-200.png differ