├── .gitattributes ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ ├── config.yml │ └── feature_request.yaml ├── PULL_REQUEST_TEMPLATE.md ├── SECURITY.md ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── close-discussion-on-pr.yaml │ ├── close-old-issues.yaml │ ├── compile-check.yaml │ ├── issue-slash-commands.yaml │ ├── pre-release.yaml │ ├── remove-winutil.yaml │ ├── sponsors.yaml │ └── unittests.yaml ├── .gitignore ├── Compile.ps1 ├── LICENSE ├── README.md ├── Test-WingetInstall.ps1 ├── config ├── applications.json ├── appnavigation.json ├── autounattend.xml ├── dns.json ├── feature.json ├── preset.json ├── themes.json └── tweaks.json ├── functions ├── microwin │ ├── Invoke-Microwin.ps1 │ ├── Invoke-MicrowinBusyInfo.ps1 │ ├── Invoke-MicrowinGetIso.ps1 │ ├── Microwin-Classes.ps1 │ ├── Microwin-CopyToUSB.ps1 │ ├── Microwin-CopyVirtIO.ps1 │ ├── Microwin-GetLangFromCulture.ps1 │ ├── Microwin-GetLocalizedUsers.ps1 │ ├── Microwin-GetOscdimg.ps1 │ ├── Microwin-NewCheckInstall.ps1 │ ├── Microwin-NewFirstRun.ps1 │ ├── Microwin-NewUnattend.ps1 │ ├── Microwin-RemoveFeatures.ps1 │ ├── Microwin-RemoveFileOrDirectory.ps1 │ ├── Microwin-RemovePackages.ps1 │ ├── Microwin-RemoveProvisionedPackages.ps1 │ ├── Microwin-TestCompatibleImage.ps1 │ └── Toggle-MicrowinPanel.ps1 ├── private │ ├── Add-SelectedAppsMenuItem.ps1 │ ├── Copy-Files.ps1 │ ├── Find-AppsByNameOrDescription.ps1 │ ├── Find-TweaksByNameOrDescription.ps1 │ ├── Get-LocalizedYesNo.ps1 │ ├── Get-WPFObjectName.ps1 │ ├── Get-WinUtilCheckBoxes.ps1 │ ├── Get-WinUtilInstallerProcess.ps1 │ ├── Get-WinUtilSelectedPackages.ps1 │ ├── Get-WinUtilToggleStatus.ps1 │ ├── Get-WinUtilVariables.ps1 │ ├── Hide-WPFInstallAppBusy.ps1 │ ├── Initalize-InstallAppEntry.ps1 │ ├── Initialize-InstallAppArea.ps1 │ ├── Initialize-InstallCategoryAppList.ps1 │ ├── Install-WinUtilChoco.ps1 │ ├── Install-WinUtilProgramChoco.ps1 │ ├── Install-WinUtilProgramWinget.ps1 │ ├── Install-WinUtilWinget.ps1 │ ├── Invoke-WinUtilAssets.ps1 │ ├── Invoke-WinUtilCurrentSystem.ps1 │ ├── Invoke-WinUtilExplorerUpdate.ps1 │ ├── Invoke-WinUtilFeatureInstall.ps1 │ ├── Invoke-WinUtilGPU.ps1 │ ├── Invoke-WinUtilInstallPSProfile.ps1 │ ├── Invoke-WinUtilSSHServer.ps1 │ ├── Invoke-WinUtilScript.ps1 │ ├── Invoke-WinUtilSponsors.ps1 │ ├── Invoke-WinUtilTweaks.ps1 │ ├── Invoke-WinUtilUninstallPSProfile.ps1 │ ├── Invoke-WinutilThemeChange.ps1 │ ├── Remove-WinUtilAPPX.ps1 │ ├── Set-PackageManagerPreference.ps1 │ ├── Set-WinUtilDNS.ps1 │ ├── Set-WinUtilProgressbar.ps1 │ ├── Set-WinUtilRegistry.ps1 │ ├── Set-WinUtilScheduledTask.ps1 │ ├── Set-WinUtilService.ps1 │ ├── Set-WinUtilTaskbarItem.ps1 │ ├── Show-CustomDialog.ps1 │ ├── Show-WPFInstallAppBusy.ps1 │ ├── Test-WinUtilPackageManager.ps1 │ └── Update-WinUtilProgramWinget.ps1 └── public │ ├── Initialize-WPFUI.ps1 │ ├── Invoke-ScratchDialog.ps1 │ ├── Invoke-WPFButton.ps1 │ ├── Invoke-WPFCloseButton.ps1 │ ├── Invoke-WPFControlPanel.ps1 │ ├── Invoke-WPFFeatureInstall.ps1 │ ├── Invoke-WPFFixesNetwork.ps1 │ ├── Invoke-WPFFixesUpdate.ps1 │ ├── Invoke-WPFFixesWinget.ps1 │ ├── Invoke-WPFGetInstalled.ps1 │ ├── Invoke-WPFImpex.ps1 │ ├── Invoke-WPFInstall.ps1 │ ├── Invoke-WPFInstallUpgrade.ps1 │ ├── Invoke-WPFOOSU.ps1 │ ├── Invoke-WPFPanelAutologin.ps1 │ ├── Invoke-WPFPopup.ps1 │ ├── Invoke-WPFPresets.ps1 │ ├── Invoke-WPFRunAdobeCCCleanerTool.ps1 │ ├── Invoke-WPFRunspace.ps1 │ ├── Invoke-WPFSSHServer.ps1 │ ├── Invoke-WPFSelectedAppsUpdate.ps1 │ ├── Invoke-WPFSystemRepair.ps1 │ ├── Invoke-WPFTab.ps1 │ ├── Invoke-WPFTweakPS7.ps1 │ ├── Invoke-WPFUIElements.ps1 │ ├── Invoke-WPFUltimatePerformance.ps1 │ ├── Invoke-WPFUnInstall.ps1 │ ├── Invoke-WPFUpdatesdefault.ps1 │ ├── Invoke-WPFUpdatesdisable.ps1 │ ├── Invoke-WPFUpdatessecurity.ps1 │ ├── Invoke-WPFtweaksbutton.ps1 │ ├── Invoke-WPFundoall.ps1 │ └── Show-CTTLogo.ps1 ├── lint └── PSScriptAnalyser.ps1 ├── overrides └── main.html ├── pester ├── configs.Tests.ps1 └── functions.Tests.ps1 ├── releases └── oscdimg.exe ├── scripts ├── main.ps1 └── start.ps1 ├── sign.bat ├── tools ├── Invoke-Preprocessing.ps1 └── devdocs-generator.ps1 ├── windev.ps1 └── xaml └── inputXML.xaml /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.ps1 text eol=crlf 4 | *.json text eol=crlf 5 | *.cfg text eol=crlf 6 | 7 | *.png binary 8 | *.jpg binary 9 | 10 | config/* diff 11 | config/applications.json diff 12 | *.json diff 13 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # These owners will be the default owners for everything in 5 | # the repo. Unless a later match takes precedence, 6 | # @global-owner1 and @global-owner2 will be requested for 7 | # review when someone opens a pull request. 8 | * @ChrisTitusTech 9 | 10 | # Order is important; the last matching pattern takes the most 11 | # precedence. When someone opens a pull request that only 12 | # modifies JS files, only @js-owner and not the global 13 | # owner(s) will be requested for a review. 14 | # *.js @js-owner #This is an inline comment. 15 | 16 | # You can also use email addresses if you prefer. They'll be 17 | # used to look up users just like we do for commit author 18 | # emails. 19 | # *.go docs@example.com 20 | 21 | # Teams can be specified as code owners as well. Teams should 22 | # be identified in the format @org/team-name. Teams must have 23 | # explicit write access to the repository. In this example, 24 | # the octocats team in the octo-org organization owns all .txt files. 25 | # *.txt @octo-org/octocats 26 | 27 | # In this example, @doctocat owns any files in the build/logs 28 | # directory at the root of the repository and any of its 29 | # subdirectories. 30 | # /build/logs/ @doctocat 31 | 32 | # The `docs/*` pattern will match files like 33 | # `docs/getting-started.md` but not further nested files like 34 | # `docs/build-app/troubleshooting.md`. 35 | # docs/* docs@example.com 36 | 37 | # In this example, @octocat owns any file in an apps directory 38 | # anywhere in your repository. 39 | # apps/ @octocat 40 | 41 | # In this example, @doctocat owns any file in the `/docs` 42 | # directory in the root of your repository and any of its 43 | # subdirectories. 44 | # /docs/ @doctocat 45 | 46 | # In this example, any change inside the `/scripts` directory 47 | # will require approval from @doctocat or @octocat. 48 | # /scripts/ @doctocat @octocat 49 | 50 | # In this example, @octocat owns any file in a `/logs` directory such as 51 | # `/build/logs`, `/scripts/logs`, and `/deeply/nested/logs`. Any changes 52 | # in a `/logs` directory will require approval from @octocat. 53 | # **/logs @octocat 54 | 55 | # In this example, @octocat owns any file in the `/apps` 56 | # directory in the root of your repository except for the `/apps/github` 57 | # subdirectory, as its owners are left empty. 58 | # /apps/ @octocat 59 | # /apps/github -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://www.cttstore.com/windows-toolbox 2 | github: christitustech 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: "Bug report" 2 | description: "Report a bug to help us identify and fix issues in the project." 3 | labels: ["bug"] 4 | 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | # 🐞 **Issue Report** 10 | Thank you for taking the time to report an issue! Please provide as much detail as possible to help us address the problem efficiently. 11 | 12 | ## ⚠️ **IMPORTANT** 13 | - 🛠️ **Supported environments only:** We only support Windows 11. Custom ISOs that are not made using Microwin are not supported. 14 | - 💡 For general questions, use the [Discussions section](https://github.com/Christitustech/winutil/discussions) or join our Community-driven [Discord Server](https://discord.gg/RUbZUZyByQ). 15 | 16 | - type: checkboxes 17 | attributes: 18 | label: ⚙️ Issue Checklist 19 | options: 20 | - label: I have read the guidelines. 21 | - label: I checked for duplicate issues. 22 | - label: I searched for existing discussions. 23 | - label: I checked for an existing pull request that addresses this issue. 24 | validations: 25 | required: true 26 | 27 | - type: input 28 | id: affected_part 29 | attributes: 30 | label: 📜 What part of Winutil are you having issues with? 31 | placeholder: "e.g., Microwin, Tweaks, etc." 32 | validations: 33 | required: true 34 | 35 | - type: textarea 36 | id: issue_description 37 | attributes: 38 | label: 📝 Provide a clear and concise description of the issue. 39 | validations: 40 | required: true 41 | 42 | - type: textarea 43 | id: steps_to_reproduce 44 | attributes: 45 | label: 🔄 Steps to reproduce the issue. 46 | placeholder: "e.g., Step 1: ..., Step 2: ..." 47 | validations: 48 | required: true 49 | 50 | - type: textarea 51 | id: error_output 52 | attributes: 53 | label: ❌ Paste the full error output (if available). 54 | placeholder: "Include any relevant logs or error messages." 55 | 56 | - type: textarea 57 | id: additional_context 58 | attributes: 59 | label: 🖼️ Additional context. 60 | placeholder: "Include screenshots, code blocks (use triple backticks ```), or any other relevant information." 61 | validations: 62 | required: false 63 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 💻 Community Discord 4 | url: https://discord.gg/RUbZUZyByQ 5 | about: Join our Community Discord server to chat with other users in the Winutil community. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: "Feature request" 2 | description: "Suggest a new feature or improvement for the project." 3 | labels: ["enhancement"] 4 | 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | # ✨ **Feature request** 10 | Thank you for taking the time to suggest a feature! Please provide as much detail as possible to help us understand and evaluate your request. 11 | 12 | ## ⚠️ **IMPORTANT** 13 | - 🛠️ **Supported environments only:** We only support Windows 11. 14 | - 💡 For general questions, use the [Discussions section](https://github.com/Christitustech/winutil/discussions) or join our Community-driven [Discord Server](https://discord.gg/RUbZUZyByQ). 15 | 16 | - type: checkboxes 17 | attributes: 18 | label: ⚙️ Issue Checklist 19 | options: 20 | - label: I have read the guidelines. 21 | - label: I checked for duplicate issues. 22 | - label: I searched for existing discussions. 23 | - label: I checked for an existing pull request that addresses this request. 24 | validations: 25 | required: true 26 | 27 | - type: textarea 28 | id: problem_statement 29 | attributes: 30 | label: ❓ Is your feature request related to a problem? 31 | placeholder: "Provide a clear and concise description of the issue you're facing. Example: 'I'm always frustrated when [...]'" 32 | validations: 33 | required: false 34 | 35 | - type: textarea 36 | id: proposed_solution 37 | attributes: 38 | label: 💡 Describe the solution you'd like 39 | placeholder: "Provide a clear and concise description of what you want to happen." 40 | validations: 41 | required: true 42 | 43 | - type: textarea 44 | id: alternatives 45 | attributes: 46 | label: 🔄 Describe alternatives you've considered 47 | placeholder: "Provide details on any alternative solutions or features you've thought about." 48 | validations: 49 | required: false 50 | 51 | - type: textarea 52 | id: additional_context 53 | attributes: 54 | label: 🖼️ Additional context 55 | placeholder: "Include screenshots, code blocks (use triple backticks ```), or any other relevant information." 56 | validations: 57 | required: false 58 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | <!--Before you make this PR have you followed the docs here? - https://christitustech.github.io/winutil/contribute/ --> 2 | 3 | ## Type of Change 4 | - [ ] New feature 5 | - [ ] Bug fix 6 | - [ ] Documentation update 7 | - [ ] Refactoring 8 | - [ ] Hotfix 9 | - [ ] Security patch 10 | - [ ] UI/UX improvement 11 | 12 | ## Description 13 | <!--[Provide a detailed explanation of the changes you have made. Include the reasons behind these changes and any relevant context. Link any related issues.]--> 14 | 15 | ## Testing 16 | <!--[Detail the testing you have performed to ensure that these changes function as intended. Include information about any added tests.]--> 17 | 18 | ## Impact 19 | <!--[Discuss the impact of your changes on the project. This might include effects on performance, new dependencies, or changes in behaviour.]--> 20 | 21 | ## Issue related to PR 22 | <!--[What issue/discussion is related to this PR (if any)]--> 23 | - Resolves # 24 | 25 | ## Additional Information 26 | <!--[Any additional information that reviewers should be aware of.]--> 27 | 28 | ## Checklist 29 | - [ ] My code adheres to the coding and style guidelines of the project. 30 | - [ ] I have performed a self-review of my own code. 31 | - [ ] I have commented my code, particularly in hard-to-understand areas. 32 | - [ ] I have made corresponding changes to the documentation. 33 | - [ ] My changes generate no errors/warnings/merge conflicts. 34 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | If you find a security issue please make post it in the issues tab. If you think it should be private you can email me at contact@christitus.com. 4 | 5 | For immediate response check out our discord server @ [](https://discord.gg/RUbZUZyByQ) 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | ignore: 8 | - dependency-name: "actions/stale" 9 | versions: '>= 9' 10 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | tag-prefix: '' 2 | categories: 3 | - title: '🚀 Features' 4 | labels: 5 | - 'feature' 6 | - 'enhancement' 7 | - title: '🐛 Bug Fixes' 8 | labels: 9 | - 'fix' 10 | - 'bugfix' 11 | - 'bug' 12 | - title: '📚 Documentation' 13 | label: 'documentation' 14 | - title: '🔒 Security' 15 | label: 'security' 16 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 17 | template: | 18 | ## Changes 19 | 20 | $CHANGES 21 | 22 | change-title-escapes: '\<*_&"''' 23 | autolabeler: 24 | - label: 'documentation' 25 | files: 26 | - '*.md' 27 | branch: 28 | - '/docs{0,1}\/.+/' 29 | - label: 'bug' 30 | branch: 31 | - '/fix\/.+/' 32 | title: 33 | - '/fix/i' 34 | - label: 'enhancement' 35 | branch: 36 | - '/feature\/.+/' 37 | body: 38 | - '/[A-Z]+-[0-9]+/' 39 | - label: 'documentation' 40 | files: 41 | - '**/*.md' 42 | - 'docs/**/*' 43 | - label: 'security' 44 | branch: 45 | - '/security\/.+/' 46 | replacers: 47 | - search: /"/g 48 | replace: '' 49 | - search: /'/g 50 | replace: '' 51 | exclude-labels: 52 | - 'skip-changelog' 53 | 54 | filter-by-commitish: true 55 | -------------------------------------------------------------------------------- /.github/workflows/close-discussion-on-pr.yaml: -------------------------------------------------------------------------------- 1 | name: Close Discussion on PR Merge 2 | 3 | on: 4 | pull_request: 5 | types: [closed] 6 | 7 | jobs: 8 | closeDiscussion: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v4 13 | 14 | - name: Check if PR was merged 15 | if: github.event.pull_request.merged == true 16 | run: echo "PR was merged" 17 | 18 | - name: Extract Discussion Number & Close If any Were Found 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | if: github.event.pull_request.merged == true 22 | id: extract-discussion 23 | run: | 24 | pr_body="${{ github.event.pull_request.body }}" 25 | discussion_ids=$(echo "$pr_body" | grep -oP '(?i)(resolve|fix|close)[s|d]? #\K[0-9]+') 26 | 27 | if [ -z "$discussion_ids" ]; then 28 | echo "No discussion IDs found." 29 | exit 0 30 | fi 31 | 32 | for discussion_id in $discussion_ids; do 33 | echo "Attempting to close discussion #$discussion_id" 34 | response=$(curl -s -X PATCH -H "Authorization: token $GITHUB_TOKEN" \ 35 | -H "Accept: application/vnd.github.v3+json" \ 36 | -d '{"state": "closed"}' \ 37 | "https://api.github.com/repos/${{ github.repository }}/discussions/$discussion_id") 38 | 39 | if echo "$response" | jq -e '.id' > /dev/null; then 40 | echo "Successfully closed discussion #$discussion_id" 41 | else 42 | error_message=$(echo "$response" | jq -r '.message // "Unknown error"') 43 | echo "Warning: Failed to close discussion #$discussion_id. Error: $error_message" 44 | echo "Full response: $response" 45 | fi 46 | done 47 | shell: bash 48 | continue-on-error: true 49 | -------------------------------------------------------------------------------- /.github/workflows/close-old-issues.yaml: -------------------------------------------------------------------------------- 1 | name: Close Inactive Issues 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' # Run daily 6 | workflow_dispatch: # This line enables manual triggering 7 | 8 | jobs: 9 | close-issues: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write # Ensure necessary permissions for issues 13 | pull-requests: none 14 | contents: none 15 | steps: 16 | - name: Close inactive issues 17 | uses: actions/stale@v8 18 | with: 19 | # A list of labels to reference when looking through issues, 20 | # and only when one (or even more) of these labels are found.. 21 | # then skip this issue, and never try to stale and/or close it. 22 | exempt-issue-labels: "Keep Issue Open" 23 | # Split it into two weeks, after one week the issue will be marked as stale, 24 | # after another week have pasted without any update.. the issue will then be closed. 25 | days-before-issue-stale: 90 26 | days-before-issue-close: 365 27 | # NEVER mark PRs as Stale or Close + this workflow should never have write permissions on PRs, EVER! 28 | days-before-pr-stale: -1 29 | days-before-pr-close: -1 30 | # Sends a message for both the Stale and Close events of an issue. 31 | stale-issue-message: "This issue was marked as stale due to inactivity." 32 | close-issue-message: "This issue was closed after remaining stale without updates." 33 | # Increase this value if the project receives a lot of 34 | # PRs (yes.. apparently they're processed no matter what) & Issues. 35 | # Default value for it (according to the docs) is 30 36 | operations-per-run: 200 37 | # Make this field equal true if you want to test your configuration if it works correctly or not 38 | debug-only: false 39 | repo-token: ${{ secrets.GITHUB_TOKEN }} 40 | -------------------------------------------------------------------------------- /.github/workflows/compile-check.yaml: -------------------------------------------------------------------------------- 1 | name: Compile & Check 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | workflow_dispatch: # Manual trigger added 9 | workflow_call: # Allow other Actions to call this workflow 10 | 11 | jobs: 12 | Compile-and-Check: 13 | runs-on: windows-latest 14 | steps: 15 | - name: Checkout Sources 16 | uses: actions/checkout@v4 17 | 18 | - name: Compile and Syntaxcheck winutil.ps1 19 | shell: pwsh 20 | run: | 21 | Set-ExecutionPolicy Bypass -Scope Process -Force; ./Compile.ps1 22 | continue-on-error: false # Directly fail the job on error, removing the need for a separate check 23 | -------------------------------------------------------------------------------- /.github/workflows/issue-slash-commands.yaml: -------------------------------------------------------------------------------- 1 | name: Issue slash commands 2 | 3 | on: 4 | issue_comment: 5 | types: [created, edited] 6 | 7 | jobs: 8 | issueCommands: 9 | # Skip this job if the comment was created/edited on a PR 10 | if: ${{ !github.event.issue.pull_request }} 11 | runs-on: ubuntu-latest 12 | permissions: 13 | issues: write 14 | pull-requests: none 15 | contents: read 16 | 17 | steps: 18 | - run: echo "command=false" >> $GITHUB_ENV 19 | 20 | - name: Check for /label command 21 | id: check_label_command 22 | run: | 23 | if [[ "${{ contains(github.event.comment.body, '/label') }}" == "true" ]]; then 24 | echo "command=true" >> $GITHUB_ENV 25 | LABEL_NAME=$(echo "${{ github.event.comment.body }}" | awk -F"/label" '/\/label/ { match($2, /'\''([^'\'']*)'\''/, arr); if (arr[1] != "") print arr[1] }') 26 | echo "label_command=true" >> $GITHUB_ENV 27 | echo "label_name=${LABEL_NAME}" >> $GITHUB_ENV 28 | else 29 | echo "label_command=false" >> $GITHUB_ENV 30 | fi 31 | 32 | - name: Check for /unlabel command 33 | id: check_unlabel_command 34 | run: | 35 | if [[ "${{ contains(github.event.comment.body, '/unlabel') }}" == "true" ]]; then 36 | echo "command=true" >> $GITHUB_ENV 37 | UNLABEL_NAME=$(echo "${{ github.event.comment.body }}" | awk -F"/unlabel" '/\/unlabel/ { match($2, /'\''([^'\'']*)'\''/, arr); if (arr[1] != "") print arr[1] }') 38 | echo "unlabel_command=true" >> $GITHUB_ENV 39 | echo "unlabel_name=${UNLABEL_NAME}" >> $GITHUB_ENV 40 | else 41 | echo "unlabel_command=false" >> $GITHUB_ENV 42 | fi 43 | 44 | - name: Check for /close command 45 | id: check_close_command 46 | run: | 47 | if [[ "${{ contains(github.event.comment.body, '/close') }}" == "true" ]]; then 48 | echo "command=true" >> $GITHUB_ENV 49 | echo "close_command=true" >> $GITHUB_ENV 50 | echo "reopen_command=false" >> $GITHUB_ENV 51 | else 52 | echo "close_command=false" >> $GITHUB_ENV 53 | fi 54 | 55 | - name: Check for /open or /reopen command 56 | id: check_reopen_command 57 | run: | 58 | if [[ "${{ contains(github.event.comment.body, '/open') }}" == "true" ]] || [[ "${{ contains(github.event.comment.body, '/reopen') }}" == "true" ]]; then 59 | echo "command=true" >> $GITHUB_ENV 60 | echo "reopen_command=true" >> $GITHUB_ENV 61 | echo "close_command=false" >> $GITHUB_ENV 62 | else 63 | echo "reopen_command=false" >> $GITHUB_ENV 64 | fi 65 | 66 | - name: Check if the user is allowed 67 | id: check_user 68 | if: env.command == 'true' 69 | run: | 70 | ALLOWED_USERS=("ChrisTitusTech" "og-mrk" "Marterich" "MyDrift-user" "Real-MullaC" "CodingWonders") 71 | if [[ " ${ALLOWED_USERS[@]} " =~ " ${{ github.event.comment.user.login }} " ]]; then 72 | echo "user=true" >> $GITHUB_ENV 73 | else 74 | exit 0 75 | fi 76 | 77 | - name: Close issue 78 | if: env.close_command == 'true' 79 | env: 80 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 81 | ISSUE_NUMBER: ${{ github.event.issue.number }} 82 | run: | 83 | echo Closing the issue... 84 | if [[ "${{ contains(github.event.comment.body, 'not planned') }}" == "true" ]]; then 85 | gh issue close $ISSUE_NUMBER --repo ${{ github.repository }} --reason 'not planned' 86 | else 87 | gh issue close $ISSUE_NUMBER --repo ${{ github.repository }} 88 | fi 89 | 90 | - name: Reopen issue 91 | if: env.reopen_command == 'true' 92 | env: 93 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 94 | ISSUE_NUMBER: ${{ github.event.issue.number }} 95 | run: | 96 | echo Reopening the issue... 97 | gh issue reopen $ISSUE_NUMBER --repo ${{ github.repository }} 98 | 99 | - name: Label issue 100 | if: env.label_command == 'true' 101 | env: 102 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 103 | ISSUE_NUMBER: ${{ github.event.issue.number }} 104 | run: | 105 | echo Labeling the issue... 106 | gh issue edit $ISSUE_NUMBER --repo ${{ github.repository }} --add-label "${{ env.label_name }}" 107 | 108 | - name: Remove labels 109 | if: env.unlabel_command == 'true' 110 | env: 111 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 112 | ISSUE_NUMBER: ${{ github.event.issue.number }} 113 | run: | 114 | echo Unlabeling the issue... 115 | gh issue edit $ISSUE_NUMBER --repo ${{ github.repository }} --remove-label "${{ env.unlabel_name }}" 116 | -------------------------------------------------------------------------------- /.github/workflows/pre-release.yaml: -------------------------------------------------------------------------------- 1 | name: Pre-Release WinUtil 2 | 3 | permissions: 4 | contents: write 5 | actions: read 6 | 7 | on: 8 | workflow_dispatch: # Manual trigger added 9 | 10 | jobs: 11 | build-runspace: 12 | runs-on: windows-latest 13 | env: 14 | CERTIFICATE_BASE64: ${{ secrets.CERTIFICATE_BASE64 }} 15 | steps: 16 | - name: Checkout Repository 17 | uses: actions/checkout@v4 18 | 19 | - name: Compile project 20 | shell: pwsh 21 | run: | 22 | Set-ExecutionPolicy Bypass -Scope Process -Force; ./Compile.ps1 23 | continue-on-error: false # Directly fail the job on error, removing the need for a separate check 24 | 25 | - name: Set Version to Todays Date 26 | id: extract_version 27 | run: | 28 | $version = (Get-Date -Format "yy.MM.dd") 29 | echo "VERSION=$version" >> $env:GITHUB_ENV 30 | shell: pwsh 31 | 32 | - name: Create Tag 33 | id: create_tag 34 | run: | 35 | $tagExists = git tag -l $env:VERSION 36 | if ($tagExists -eq "") { 37 | git tag $env:VERSION 38 | if ($LASTEXITCODE -ne 0) { 39 | Write-Error "Failed to create tag $env:VERSION" 40 | exit 1 41 | } 42 | git push origin $env:VERSION 43 | if ($LASTEXITCODE -ne 0) { 44 | Write-Error "Failed to push tag $env:VERSION" 45 | exit 1 46 | } 47 | } else { 48 | Write-Host "Tag $env:VERSION already exists, skipping tag creation" 49 | } 50 | shell: pwsh 51 | 52 | - name: Generate Release Notes 53 | id: generate_notes 54 | uses: release-drafter/release-drafter@v6 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 57 | with: 58 | config-name: release-drafter.yml 59 | version: ${{ env.VERSION }} # Pass the version variable 60 | 61 | - name: Create and Upload Release 62 | id: create_release 63 | uses: softprops/action-gh-release@v2 64 | with: 65 | tag_name: ${{ env.VERSION }} 66 | name: Pre-Release ${{ env.VERSION }} 67 | body: | 68 | ${{ steps.generate_notes.outputs.body }} 69 | 70 |  71 | append_body: false 72 | files: ./winutil.ps1 73 | prerelease: true 74 | env: 75 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 76 | -------------------------------------------------------------------------------- /.github/workflows/remove-winutil.yaml: -------------------------------------------------------------------------------- 1 | name: Remove winutil.ps1 if included in a Push 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | 8 | jobs: 9 | check-and-delete-file: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | 16 | - name: Check if winutil.ps1 exists 17 | id: check_existence 18 | run: | 19 | if [ -f "winutil.ps1" ]; then 20 | echo "winutil_exists=true" >> $GITHUB_OUTPUT 21 | else 22 | echo "winutil_exists=false" >> $GITHUB_OUTPUT 23 | fi 24 | 25 | - name: Delete winutil.ps1 if it exists 26 | if: steps.check_existence.outputs.winutil_exists == 'true' 27 | run: | 28 | git config --global user.email "winutil-action@noreply.github.com" 29 | git config --global user.name "winutil-action" 30 | git rm winutil.ps1 31 | git commit -m "Delete winutil.ps1 as it is not allowed" 32 | git push origin HEAD:${{ github.ref }} 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /.github/workflows/sponsors.yaml: -------------------------------------------------------------------------------- 1 | name: Generate Sponsors README 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: 30 15 * * 0-6 6 | permissions: 7 | contents: write 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | if: (github.event_name == 'schedule' && github.repository == 'ChrisTitusTech/winutil') || (github.event_name != 'schedule') 12 | steps: 13 | - name: Checkout 🛎️ 14 | uses: actions/checkout@v4 15 | 16 | - name: Generate Sponsors 💖 17 | uses: JamesIves/github-sponsors-readme-action@v1 18 | with: 19 | token: ${{ secrets.PAT }} 20 | file: 'README.md' 21 | 22 | - name: Deploy to GitHub Pages 🚀 23 | uses: JamesIves/github-pages-deploy-action@v4 24 | with: 25 | branch: main 26 | folder: '.' 27 | -------------------------------------------------------------------------------- /.github/workflows/unittests.yaml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | lint: 8 | name: PS Script Analyzer 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: lint 13 | uses: devblackops/github-action-psscriptanalyzer@master 14 | with: 15 | sendComment: false 16 | settingsPath: lint/PSScriptAnalyser.ps1 17 | failOnErrors: false 18 | failOnWarnings: false 19 | failOnInfos: false 20 | test: 21 | runs-on: windows-latest 22 | 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@v4 26 | 27 | - name: Install Pester 28 | run: | 29 | Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process 30 | Install-Module -Name Pester -Force -SkipPublisherCheck -AllowClobber 31 | shell: pwsh 32 | 33 | - name: Run Pester tests 34 | run: | 35 | Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process 36 | Invoke-Pester -Path 'pester/*.Tests.ps1' -Output Detailed 37 | 38 | shell: pwsh 39 | env: 40 | TEMP: ${{ runner.temp }} 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### VSCode ### 2 | 3 | # Configuration folder 4 | .vscode/ 5 | .idea/ 6 | 7 | ### Visual Studio ### 8 | 9 | # Visual Studio user-specific files 10 | .vs/ 11 | 12 | winutil.pdb 13 | 14 | ### Preprocessor Hashes ### 15 | .preprocessor_hashes.json 16 | 17 | ### Windows ### 18 | 19 | # Folder config file 20 | [Dd]esktop.ini 21 | 22 | # Ignore Generated XAML Files 23 | xaml/inputApp.xaml 24 | xaml/inputFeatures.xaml 25 | xaml/inputTweaks.xaml 26 | 27 | # Executables and Configs 28 | winget.msixbundle 29 | pester.ps1 30 | *.psd* 31 | ooshutup10.cfg 32 | winutil.exe.config 33 | Microsoft.UI.Xaml* 34 | license1.xml 35 | winutil.ps1 36 | 37 | # Libraries 38 | System.Management.Automation.dll 39 | Microsoft.PowerShell.ConsoleHost.dll 40 | 41 | # Compressed files 42 | *.zip 43 | 44 | ### MacOS ### 45 | 46 | # General 47 | .DS_Store 48 | microwin.log 49 | True 50 | test.ps1 51 | winutil.ps1 52 | 53 | # temporary excludes for docs 54 | .github/site/ 55 | 56 | binary/ 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 CT Tech Group LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Test-WingetInstall.ps1: -------------------------------------------------------------------------------- 1 | # Import the function (adjust the path according to your setup) 2 | . "./functions/private/Install-WinUtilWinget.ps1" 3 | . "./functions/private/Test-WinUtilPackageManager.ps1" 4 | 5 | # Set up Information stream to be visible 6 | $InformationPreference = "Continue" 7 | 8 | Write-Host "Starting Winget installation test..." -ForegroundColor Cyan 9 | 10 | try { 11 | Install-WinUtilWinget 12 | } catch { 13 | Write-Host "Error occurred during testing: $($_.Exception.Message)" -ForegroundColor Red 14 | Write-Host "Stack Trace:" -ForegroundColor Red 15 | $_.ScriptStackTrace 16 | } 17 | -------------------------------------------------------------------------------- /config/appnavigation.json: -------------------------------------------------------------------------------- 1 | { 2 | "WPFInstall": { 3 | "Content": "Install/Upgrade Applications", 4 | "Category": "____Actions", 5 | "Type": "Button", 6 | "Order": "1", 7 | "Description": "Install or upgrade the selected applications" 8 | }, 9 | "WPFUninstall": { 10 | "Content": "Uninstall Applications", 11 | "Category": "____Actions", 12 | "Type": "Button", 13 | "Order": "2", 14 | "Description": "Uninstall the selected applications" 15 | }, 16 | "WPFInstallUpgrade": { 17 | "Content": "Upgrade all Applications", 18 | "Category": "____Actions", 19 | "Type": "Button", 20 | "Order": "3", 21 | "Description": "Upgrade all applications to the latest version" 22 | }, 23 | "WingetRadioButton": { 24 | "Content": "Winget", 25 | "Category": "__Package Manager", 26 | "Type": "RadioButton", 27 | "GroupName": "PackageManagerGroup", 28 | "Checked": true, 29 | "Order": "1", 30 | "Description": "Use Winget for package management" 31 | }, 32 | "ChocoRadioButton": { 33 | "Content": "Chocolatey", 34 | "Category": "__Package Manager", 35 | "Type": "RadioButton", 36 | "GroupName": "PackageManagerGroup", 37 | "Checked": false, 38 | "Order": "2", 39 | "Description": "Use Chocolatey for package management" 40 | }, 41 | "WPFClearInstallSelection": { 42 | "Content": "Clear Selection", 43 | "Category": "__Selection", 44 | "Type": "Button", 45 | "Order": "1", 46 | "Description": "Clear the selection of applications" 47 | }, 48 | "WPFGetInstalled": { 49 | "Content": "Get Installed", 50 | "Category": "__Selection", 51 | "Type": "Button", 52 | "Order": "2", 53 | "Description": "Show installed applications" 54 | }, 55 | "WPFselectedAppsButton": { 56 | "Content": "Selected Apps: 0", 57 | "Category": "__Selection", 58 | "Type": "Button", 59 | "Order": "3", 60 | "Description": "Show the selected applications" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /config/dns.json: -------------------------------------------------------------------------------- 1 | { 2 | "Google":{ 3 | "Primary": "8.8.8.8", 4 | "Secondary": "8.8.4.4", 5 | "Primary6": "2001:4860:4860::8888", 6 | "Secondary6": "2001:4860:4860::8844" 7 | }, 8 | "Cloudflare":{ 9 | "Primary": "1.1.1.1", 10 | "Secondary": "1.0.0.1", 11 | "Primary6": "2606:4700:4700::1111", 12 | "Secondary6": "2606:4700:4700::1001" 13 | }, 14 | "Cloudflare_Malware":{ 15 | "Primary": "1.1.1.2", 16 | "Secondary": "1.0.0.2", 17 | "Primary6": "2606:4700:4700::1112", 18 | "Secondary6": "2606:4700:4700::1002" 19 | }, 20 | "Cloudflare_Malware_Adult":{ 21 | "Primary": "1.1.1.3", 22 | "Secondary": "1.0.0.3", 23 | "Primary6": "2606:4700:4700::1113", 24 | "Secondary6": "2606:4700:4700::1003" 25 | }, 26 | "Open_DNS":{ 27 | "Primary": "208.67.222.222", 28 | "Secondary": "208.67.220.220", 29 | "Primary6": "2620:119:35::35", 30 | "Secondary6": "2620:119:53::53" 31 | }, 32 | "Quad9":{ 33 | "Primary": "9.9.9.9", 34 | "Secondary": "149.112.112.112", 35 | "Primary6": "2620:fe::fe", 36 | "Secondary6": "2620:fe::9" 37 | }, 38 | "AdGuard_Ads_Trackers":{ 39 | "Primary": "94.140.14.14", 40 | "Secondary": "94.140.15.15", 41 | "Primary6": "2a10:50c0::ad1:ff", 42 | "Secondary6": "2a10:50c0::ad2:ff" 43 | }, 44 | "AdGuard_Ads_Trackers_Malware_Adult":{ 45 | "Primary": "94.140.14.15", 46 | "Secondary": "94.140.15.16", 47 | "Primary6": "2a10:50c0::bad1:ff", 48 | "Secondary6": "2a10:50c0::bad2:ff" 49 | }, 50 | "dns0.eu_Open":{ 51 | "Primary": "193.110.81.254", 52 | "Secondary": "185.253.5.254", 53 | "Primary6": "2a0f:fc80::ffff", 54 | "Secondary6": "2a0f:fc81::ffff" 55 | }, 56 | "dns0.eu_ZERO":{ 57 | "Primary": "193.110.81.9", 58 | "Secondary": "185.253.5.9", 59 | "Primary6": "2a0f:fc80::9", 60 | "Secondary6": "2a0f:fc81::9" 61 | }, 62 | "dns0.eu_KIDS":{ 63 | "Primary": "193.110.81.1", 64 | "Secondary": "185.253.5.1", 65 | "Primary6": "2a0f:fc80::1", 66 | "Secondary6": "2a0f:fc81::1" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /config/preset.json: -------------------------------------------------------------------------------- 1 | { 2 | "Standard": [ 3 | "WPFTweaksAH", 4 | "WPFTweaksConsumerFeatures", 5 | "WPFTweaksDisableExplorerAutoDiscovery", 6 | "WPFTweaksDVR", 7 | "WPFTweaksHome", 8 | "WPFTweaksLoc", 9 | "WPFTweaksServices", 10 | "WPFTweaksStorage", 11 | "WPFTweaksTele", 12 | "WPFTweaksWifi", 13 | "WPFTweaksDiskCleanup", 14 | "WPFTweaksDeleteTempFiles", 15 | "WPFTweaksEndTaskOnTaskbar", 16 | "WPFTweaksRestorePoint", 17 | "WPFTweaksPowershell7Tele" 18 | ], 19 | "Minimal": [ 20 | "WPFTweaksConsumerFeatures", 21 | "WPFTweaksDisableExplorerAutoDiscovery", 22 | "WPFTweaksHome", 23 | "WPFTweaksServices", 24 | "WPFTweaksTele" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /functions/microwin/Invoke-MicrowinBusyInfo.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-MicrowinBusyInfo { 2 | <# 3 | .DESCRIPTION 4 | Function to display the busy info for the Microwin process 5 | #> 6 | [CmdletBinding(DefaultParameterSetName='done')] 7 | param( 8 | [Parameter(ParameterSetName='wip', Mandatory, Position = 0)] 9 | [Parameter(ParameterSetName='warning', Mandatory, Position = 0)] 10 | [Parameter(ParameterSetName='done', Mandatory, Position = 0)] 11 | [Parameter(ParameterSetName='hide', Mandatory, Position = 0)] 12 | [ValidateSet('wip', 'warning', 'done', 'hide')] 13 | [string]$action, 14 | 15 | [Parameter(ParameterSetName='wip', Mandatory, Position = 1)] 16 | [Parameter(ParameterSetName='warning', Mandatory, Position = 1)] 17 | [Parameter(ParameterSetName='done', Mandatory, Position = 1)] 18 | [string]$message, 19 | 20 | [Parameter(ParameterSetName='wip', Position = 2)] [bool]$interactive = $false 21 | ) 22 | 23 | switch ($action) { 24 | "wip" { 25 | $sync.form.Dispatcher.BeginInvoke([action]{ 26 | $sync.MicrowinBusyIndicator.Visibility="Visible" 27 | $finalMessage = "" 28 | if ($interactive -eq $false) { 29 | $finalMessage += "Please wait. " 30 | } 31 | $finalMessage += $message 32 | $sync.BusyText.Text = $finalMessage 33 | $sync.BusyIcon.Foreground="#FFA500" 34 | $sync.BusyText.Foreground="#FFA500" 35 | }) 36 | } 37 | "warning" { 38 | $sync.form.Dispatcher.BeginInvoke([action]{ 39 | $sync.MicrowinBusyIndicator.Visibility="Visible" 40 | $sync.BusyText.Text=$message 41 | $sync.BusyText.Foreground="#FF0000" 42 | $sync.BusyIcon.Foreground="#FF0000" 43 | }) 44 | } 45 | "done" { 46 | $sync.form.Dispatcher.BeginInvoke([action]{ 47 | $sync.MicrowinBusyIndicator.Visibility="Visible" 48 | $sync.BusyText.Text=$message 49 | $sync.BusyText.Foreground="#00FF00" 50 | $sync.BusyIcon.Foreground="#00FF00" 51 | }) 52 | } 53 | "hide" { 54 | $sync.form.Dispatcher.BeginInvoke([action]{ 55 | $sync.MicrowinBusyIndicator.Visibility="Hidden" 56 | $sync.BusyText.Foreground=$sync.Form.Resources.MicrowinBusyColor 57 | $sync.BusyIcon.Foreground=$sync.Form.Resources.MicrowinBusyColor 58 | }) 59 | } 60 | } 61 | 62 | # Force the UI to process pending messages 63 | [System.Windows.Forms.Application]::DoEvents() 64 | Start-Sleep -Milliseconds 50 65 | } 66 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-Classes.ps1: -------------------------------------------------------------------------------- 1 | class ErroredPackage { 2 | [string]$PackageName 3 | [string]$ErrorMessage 4 | ErroredPackage() { $this.Init(@{} )} 5 | # Constructor for packages that have errored out 6 | ErroredPackage([string]$pkgName, [string]$reason) { 7 | $this.PackageName = $pkgName 8 | $this.ErrorMessage = $reason 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-CopyToUSB.ps1: -------------------------------------------------------------------------------- 1 | function Microwin-CopyToUSB([string]$fileToCopy) { 2 | foreach ($volume in Get-Volume) { 3 | if ($volume -and $volume.FileSystemLabel -ieq "ventoy") { 4 | $destinationPath = "$($volume.DriveLetter):\" 5 | #Copy-Item -Path $fileToCopy -Destination $destinationPath -Force 6 | # Get the total size of the file 7 | $totalSize = (Get-Item "$fileToCopy").length 8 | 9 | Copy-Item -Path "$fileToCopy" -Destination "$destinationPath" -Verbose -Force -Recurse -Container -PassThru | 10 | ForEach-Object { 11 | # Calculate the percentage completed 12 | $completed = ($_.BytesTransferred / $totalSize) * 100 13 | 14 | # Display the progress bar 15 | Write-Progress -Activity "Copying File" -Status "Progress" -PercentComplete $completed -CurrentOperation ("{0:N2} MB / {1:N2} MB" -f ($_.BytesTransferred / 1MB), ($totalSize / 1MB)) 16 | } 17 | 18 | Write-Host "File copied to Ventoy drive $($volume.DriveLetter)" 19 | 20 | # Detect if config files are present, move them if they are, and configure the Ventoy drive to not bypass the requirements 21 | $customVentoyConfig = @' 22 | { 23 | "control":[ 24 | { "VTOY_WIN11_BYPASS_CHECK": "0" }, 25 | { "VTOY_WIN11_BYPASS_NRO": "0" } 26 | ], 27 | "control_legacy":[ 28 | { "VTOY_WIN11_BYPASS_CHECK": "0" }, 29 | { "VTOY_WIN11_BYPASS_NRO": "0" } 30 | ], 31 | "control_uefi":[ 32 | { "VTOY_WIN11_BYPASS_CHECK": "0" }, 33 | { "VTOY_WIN11_BYPASS_NRO": "0" } 34 | ], 35 | "control_ia32":[ 36 | { "VTOY_WIN11_BYPASS_CHECK": "0" }, 37 | { "VTOY_WIN11_BYPASS_NRO": "0" } 38 | ], 39 | "control_aa64":[ 40 | { "VTOY_WIN11_BYPASS_CHECK": "0" }, 41 | { "VTOY_WIN11_BYPASS_NRO": "0" } 42 | ], 43 | "control_mips":[ 44 | { "VTOY_WIN11_BYPASS_CHECK": "0" }, 45 | { "VTOY_WIN11_BYPASS_NRO": "0" } 46 | ] 47 | } 48 | '@ 49 | 50 | try { 51 | Write-Host "Writing custom Ventoy configuration. Please wait..." 52 | if (Test-Path -Path "$($volume.DriveLetter):\ventoy\ventoy.json" -PathType Leaf) { 53 | Write-Host "A Ventoy configuration file exists. Moving it..." 54 | Move-Item -Path "$($volume.DriveLetter):\ventoy\ventoy.json" -Destination "$($volume.DriveLetter):\ventoy\ventoy.json.old" -Force 55 | Write-Host "Existing Ventoy configuration has been moved to `"ventoy.json.old`". Feel free to put your config back into the `"ventoy.json`" file." 56 | } 57 | if (-not (Test-Path -Path "$($volume.DriveLetter):\ventoy")) { 58 | New-Item -Path "$($volume.DriveLetter):\ventoy" -ItemType Directory -Force | Out-Null 59 | } 60 | $customVentoyConfig | Out-File -FilePath "$($volume.DriveLetter):\ventoy\ventoy.json" -Encoding utf8 -Force 61 | Write-Host "The Ventoy drive has been successfully configured." 62 | } catch { 63 | Write-Host "Could not configure Ventoy drive. Error: $($_.Exception.Message)`n" 64 | Write-Host "Be sure to add the following configuration to the Ventoy drive by either creating a `"ventoy.json`" file in the `"ventoy`" directory (create it if it doesn't exist) or by editing an existing one: `n`n$customVentoyConfig`n" 65 | Write-Host "Failure to do this will cause conflicts with your target ISO file." 66 | } 67 | return 68 | } 69 | } 70 | Write-Host "Ventoy USB Key is not inserted" 71 | } 72 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-CopyVirtIO.ps1: -------------------------------------------------------------------------------- 1 | function Microwin-CopyVirtIO { 2 | <# 3 | .SYNOPSIS 4 | Downloads and copies the VirtIO Guest Tools drivers to the target MicroWin ISO 5 | .NOTES 6 | A network connection must be available and the servers of Fedora People must be up. Automatic driver installation will not be added yet - I want this implementation to be reliable. 7 | #> 8 | 9 | try { 10 | Write-Host "Checking existing files..." 11 | if (Test-Path -Path "$($env:TEMP)\virtio.iso" -PathType Leaf) { 12 | Write-Host "VirtIO ISO has been detected. Deleting..." 13 | Remove-Item -Path "$($env:TEMP)\virtio.iso" -Force 14 | } 15 | Write-Host "Getting latest VirtIO drivers. Please wait. This can take some time, depending on your network connection speed and the speed of the servers..." 16 | Start-BitsTransfer -Source "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" -Destination "$($env:TEMP)\virtio.iso" -DisplayName "Downloading VirtIO drivers..." 17 | # Do everything else if the VirtIO ISO exists 18 | if (Test-Path -Path "$($env:TEMP)\virtio.iso" -PathType Leaf) { 19 | Write-Host "Mounting ISO. Please wait." 20 | $virtIO_ISO = Mount-DiskImage -PassThru "$($env:TEMP)\virtio.iso" 21 | $driveLetter = (Get-Volume -DiskImage $virtIO_ISO).DriveLetter 22 | # Create new directory for VirtIO on ISO 23 | New-Item -Path "$mountDir\VirtIO" -ItemType Directory | Out-Null 24 | $totalTime = Measure-Command { Copy-Files "$($driveLetter):" "$mountDir\VirtIO" -Recurse -Force } 25 | Write-Host "VirtIO contents have been successfully copied. Time taken: $($totalTime.Minutes) minutes, $($totalTime.Seconds) seconds`n" 26 | Get-Volume $driveLetter | Get-DiskImage | Dismount-DiskImage 27 | Remove-Item -Path "$($env:TEMP)\virtio.iso" -Force -ErrorAction SilentlyContinue 28 | Write-Host "To proceed with installation of the MicroWin image in QEMU/Proxmox VE:" 29 | Write-Host "1. Proceed with Setup until you reach the disk selection screen, in which you won't see any drives" 30 | Write-Host "2. Click `"Load Driver`" and click Browse" 31 | Write-Host "3. In the folder selection dialog, point to this path:`n`n `"D:\VirtIO\vioscsi\w11\amd64`" (replace amd64 with ARM64 if you are using Windows on ARM, and `"D:`" with the drive letter of the ISO)`n" 32 | Write-Host "4. Select all drivers that will appear in the list box and click OK" 33 | } else { 34 | throw "Could not download VirtIO drivers" 35 | } 36 | } catch { 37 | Write-Host "We could not download and/or prepare the VirtIO drivers. Error information: $_`n" 38 | Write-Host "You will need to download these drivers manually. Location: https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-GetLangFromCulture.ps1: -------------------------------------------------------------------------------- 1 | function Microwin-GetLangFromCulture { 2 | 3 | param ( 4 | [Parameter(Mandatory, Position = 0)] [string]$langName 5 | ) 6 | 7 | switch -Wildcard ($langName) 8 | { 9 | "ar*" { return "Arabic" } 10 | "pt-BR" { return "Brazilian Portuguese" } 11 | "bg*" { return "Bulgarian" } 12 | {($_ -eq "zh-CH") -or ($_ -like "zh-Hans*") -or ($_ -eq "zh-SG") -or ($_ -eq "zh-CHS")} { return "Chinese (Simplified)" } 13 | {($_ -eq "zh") -or ($_ -eq "zh-Hant") -or ($_ -eq "zh-HK") -or ($_ -eq "zh-MO") -or ($_ -eq "zh-TW") -or ($_ -eq "zh-CHT")} { return "Chinese (Traditional)" } 14 | "hr*" { return "Croatian" } 15 | "cs*" { return "Czech" } 16 | "da*" { return "Danish" } 17 | "nl*" { return "Dutch" } 18 | "en-US" { return "English" } 19 | {($_ -like "en*") -and ($_ -ne "en-US")} { return "English International" } 20 | "et*" { return "Estonian" } 21 | "fi*" { return "Finnish" } 22 | {($_ -like "fr*") -and ($_ -ne "fr-CA")} { return "French" } 23 | "fr-CA" { return "French Canadian" } 24 | "de*" { return "German" } 25 | "el*" { return "Greek" } 26 | "he*" { return "Hebrew" } 27 | "hu*" { return "Hungarian" } 28 | "it*" { return "Italian" } 29 | "ja*" { return "Japanese" } 30 | "ko*" { return "Korean" } 31 | "lv*" { return "Latvian" } 32 | "lt*" { return "Lituanian" } 33 | "nb*" { return "Norwegian" } 34 | "pl*" { return "Polish" } 35 | {($_ -like "pt*") -and ($_ -ne "pt-BR")} { return "Portuguese" } 36 | "ro*" { return "Romanian" } 37 | "ru*" { return "Russian" } 38 | "sr-Latn*" { return "Serbian Latin" } 39 | "sk*" { return "Slovak" } 40 | "sl*" { return "Slovenian" } 41 | {($_ -like "es*") -and ($_ -ne "es-MX")} { return "Spanish" } 42 | "es-MX" { return "Spanish (Mexico)" } 43 | "sv*" { return "Swedish" } 44 | "th*" { return "Thai" } 45 | "tr*" { return "Turkish" } 46 | "uk*" { return "Ukrainian" } 47 | default { return "English" } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-GetLocalizedUsers.ps1: -------------------------------------------------------------------------------- 1 | function Microwin-GetLocalizedUsers 2 | { 3 | <# 4 | .SYNOPSIS 5 | Gets a localized user group representation for ICACLS commands (Port from DISMTools PE Helper) 6 | .PARAMETER admins 7 | Determines whether to get a localized user group representation for the Administrators user group 8 | .OUTPUTS 9 | A string containing the localized user group 10 | .EXAMPLE 11 | Microwin-GetLocalizedUsers -admins $true 12 | #> 13 | param ( 14 | [Parameter(Mandatory = $true, Position = 0)] [bool]$admins 15 | ) 16 | if ($admins) { 17 | return (Get-LocalGroup | Where-Object { $_.SID.Value -like "S-1-5-32-544" }).Name 18 | } else { 19 | return (Get-LocalGroup | Where-Object { $_.SID.Value -like "S-1-5-32-545" }).Name 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-GetOscdimg.ps1: -------------------------------------------------------------------------------- 1 | function Microwin-GetOscdimg { 2 | <# 3 | .DESCRIPTION 4 | This function will download oscdimg file from github Release folders and put it into env:temp folder 5 | 6 | .EXAMPLE 7 | Microwin-GetOscdimg 8 | #> 9 | 10 | param( 11 | [Parameter(Mandatory, position=0)] 12 | [string]$oscdimgPath 13 | ) 14 | 15 | $oscdimgPath = "$env:TEMP\oscdimg.exe" 16 | $downloadUrl = "https://github.com/ChrisTitusTech/winutil/raw/main/releases/oscdimg.exe" 17 | Invoke-RestMethod -Uri $downloadUrl -OutFile $oscdimgPath 18 | $hashResult = Get-FileHash -Path $oscdimgPath -Algorithm SHA256 19 | $sha256Hash = $hashResult.Hash 20 | 21 | Write-Host "[INFO] oscdimg.exe SHA-256 Hash: $sha256Hash" 22 | 23 | $expectedHash = "AB9E161049D293B544961BFDF2D61244ADE79376D6423DF4F60BF9B147D3C78D" # Replace with the actual expected hash 24 | if ($sha256Hash -eq $expectedHash) { 25 | Write-Host "Hashes match. File is verified." 26 | } else { 27 | Write-Host "Hashes do not match. File may be corrupted or tampered with." 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-NewCheckInstall.ps1: -------------------------------------------------------------------------------- 1 | function Microwin-NewCheckInstall { 2 | 3 | # using here string to embedd firstrun 4 | $checkInstall = @' 5 | @echo off 6 | if exist "%HOMEDRIVE%\windows\cpu.txt" ( 7 | echo %HOMEDRIVE%\windows\cpu.txt exists 8 | ) else ( 9 | echo %HOMEDRIVE%\windows\cpu.txt does not exist 10 | ) 11 | if exist "%HOMEDRIVE%\windows\SerialNumber.txt" ( 12 | echo %HOMEDRIVE%\windows\SerialNumber.txt exists 13 | ) else ( 14 | echo %HOMEDRIVE%\windows\SerialNumber.txt does not exist 15 | ) 16 | if exist "%HOMEDRIVE%\unattend.xml" ( 17 | echo %HOMEDRIVE%\unattend.xml exists 18 | ) else ( 19 | echo %HOMEDRIVE%\unattend.xml does not exist 20 | ) 21 | if exist "%HOMEDRIVE%\Windows\Setup\Scripts\SetupComplete.cmd" ( 22 | echo %HOMEDRIVE%\Windows\Setup\Scripts\SetupComplete.cmd exists 23 | ) else ( 24 | echo %HOMEDRIVE%\Windows\Setup\Scripts\SetupComplete.cmd does not exist 25 | ) 26 | if exist "%HOMEDRIVE%\Windows\Panther\unattend.xml" ( 27 | echo %HOMEDRIVE%\Windows\Panther\unattend.xml exists 28 | ) else ( 29 | echo %HOMEDRIVE%\Windows\Panther\unattend.xml does not exist 30 | ) 31 | if exist "%HOMEDRIVE%\Windows\System32\Sysprep\unattend.xml" ( 32 | echo %HOMEDRIVE%\Windows\System32\Sysprep\unattend.xml exists 33 | ) else ( 34 | echo %HOMEDRIVE%\Windows\System32\Sysprep\unattend.xml does not exist 35 | ) 36 | if exist "%HOMEDRIVE%\Windows\FirstStartup.ps1" ( 37 | echo %HOMEDRIVE%\Windows\FirstStartup.ps1 exists 38 | ) else ( 39 | echo %HOMEDRIVE%\Windows\FirstStartup.ps1 does not exist 40 | ) 41 | if exist "%HOMEDRIVE%\Windows\winutil.ps1" ( 42 | echo %HOMEDRIVE%\Windows\winutil.ps1 exists 43 | ) else ( 44 | echo %HOMEDRIVE%\Windows\winutil.ps1 does not exist 45 | ) 46 | if exist "%HOMEDRIVE%\Windows\LogSpecialize.txt" ( 47 | echo %HOMEDRIVE%\Windows\LogSpecialize.txt exists 48 | ) else ( 49 | echo %HOMEDRIVE%\Windows\LogSpecialize.txt does not exist 50 | ) 51 | if exist "%HOMEDRIVE%\Windows\LogAuditUser.txt" ( 52 | echo %HOMEDRIVE%\Windows\LogAuditUser.txt exists 53 | ) else ( 54 | echo %HOMEDRIVE%\Windows\LogAuditUser.txt does not exist 55 | ) 56 | if exist "%HOMEDRIVE%\Windows\LogOobeSystem.txt" ( 57 | echo %HOMEDRIVE%\Windows\LogOobeSystem.txt exists 58 | ) else ( 59 | echo %HOMEDRIVE%\Windows\LogOobeSystem.txt does not exist 60 | ) 61 | if exist "%HOMEDRIVE%\windows\csup.txt" ( 62 | echo %HOMEDRIVE%\windows\csup.txt exists 63 | ) else ( 64 | echo %HOMEDRIVE%\windows\csup.txt does not exist 65 | ) 66 | if exist "%HOMEDRIVE%\windows\LogFirstRun.txt" ( 67 | echo %HOMEDRIVE%\windows\LogFirstRun.txt exists 68 | ) else ( 69 | echo %HOMEDRIVE%\windows\LogFirstRun.txt does not exist 70 | ) 71 | '@ 72 | $checkInstall | Out-File -FilePath "$env:temp\checkinstall.cmd" -Force -Encoding Ascii 73 | } 74 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-NewFirstRun.ps1: -------------------------------------------------------------------------------- 1 | function Microwin-NewFirstRun { 2 | 3 | # using here string to embedd firstrun 4 | $firstRun = @' 5 | # Set the global error action preference to continue 6 | $ErrorActionPreference = "Continue" 7 | function Remove-RegistryValue { 8 | param ( 9 | [Parameter(Mandatory = $true)] 10 | [string]$RegistryPath, 11 | 12 | [Parameter(Mandatory = $true)] 13 | [string]$ValueName 14 | ) 15 | 16 | # Check if the registry path exists 17 | if (Test-Path -Path $RegistryPath) { 18 | $registryValue = Get-ItemProperty -Path $RegistryPath -Name $ValueName -ErrorAction SilentlyContinue 19 | 20 | # Check if the registry value exists 21 | if ($registryValue) { 22 | # Remove the registry value 23 | Remove-ItemProperty -Path $RegistryPath -Name $ValueName -Force 24 | Write-Host "Registry value '$ValueName' removed from '$RegistryPath'." 25 | } else { 26 | Write-Host "Registry value '$ValueName' not found in '$RegistryPath'." 27 | } 28 | } else { 29 | Write-Host "Registry path '$RegistryPath' not found." 30 | } 31 | } 32 | 33 | "FirstStartup has worked" | Out-File -FilePath "$env:HOMEDRIVE\windows\LogFirstRun.txt" -Append -NoClobber 34 | 35 | $taskbarPath = "$env:AppData\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar" 36 | # Delete all files on the Taskbar 37 | Get-ChildItem -Path $taskbarPath -File | Remove-Item -Force 38 | Remove-RegistryValue -RegistryPath "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband" -ValueName "FavoritesRemovedChanges" 39 | Remove-RegistryValue -RegistryPath "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband" -ValueName "FavoritesChanges" 40 | Remove-RegistryValue -RegistryPath "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband" -ValueName "Favorites" 41 | 42 | # Delete Edge Icon from the desktop 43 | $edgeShortcutFiles = Get-ChildItem -Path $desktopPath -Filter "*Edge*.lnk" 44 | # Check if Edge shortcuts exist on the desktop 45 | if ($edgeShortcutFiles) { 46 | foreach ($shortcutFile in $edgeShortcutFiles) { 47 | # Remove each Edge shortcut 48 | Remove-Item -Path $shortcutFile.FullName -Force 49 | Write-Host "Edge shortcut '$($shortcutFile.Name)' removed from the desktop." 50 | } 51 | } 52 | Remove-Item -Path "$env:USERPROFILE\Desktop\*.lnk" 53 | Remove-Item -Path "$env:HOMEDRIVE\Users\Default\Desktop\*.lnk" 54 | 55 | try 56 | { 57 | if ((Get-WindowsOptionalFeature -Online | Where-Object { $_.State -eq 'Enabled' -and $_.FeatureName -like "Recall" }).Count -gt 0) 58 | { 59 | Disable-WindowsOptionalFeature -Online -FeatureName "Recall" -Remove 60 | } 61 | } 62 | catch 63 | { 64 | 65 | } 66 | 67 | # Get BCD entries and set bootmgr timeout accordingly 68 | try 69 | { 70 | # Check if the number of occurrences of "path" is 2 - this fixes the Boot Manager screen issue (#2562) 71 | if ((bcdedit | Select-String "path").Count -eq 2) 72 | { 73 | # Set bootmgr timeout to 0 74 | bcdedit /set `{bootmgr`} timeout 0 75 | } 76 | } 77 | catch 78 | { 79 | 80 | } 81 | 82 | reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Notifications\Settings\Windows.SystemToast.Suggested" /f 83 | reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Notifications\Settings\Windows.SystemToast.Suggested" /v Enabled /t REG_DWORD /d 0 /f 84 | reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Notifications\Settings\Windows.SystemToast.StartupApp" /f 85 | reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Notifications\Settings\Windows.SystemToast.StartupApp" /v Enabled /t REG_DWORD /d 0 /f 86 | reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Notifications\Settings\Microsoft.SkyDrive.Desktop" /f 87 | reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Notifications\Settings\Microsoft.SkyDrive.Desktop" /v Enabled /t REG_DWORD /d 0 /f 88 | 89 | '@ 90 | $firstRun | Out-File -FilePath "$env:temp\FirstStartup.ps1" -Force 91 | } 92 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-RemoveFeatures.ps1: -------------------------------------------------------------------------------- 1 | function Microwin-RemoveFeatures() { 2 | <# 3 | .SYNOPSIS 4 | Removes certain features from ISO image 5 | 6 | .PARAMETER UseCmdlets 7 | Determines whether or not to use the DISM cmdlets for processing. 8 | - If true, DISM cmdlets will be used 9 | - If false, calls to the DISM executable will be made whilst selecting bits and pieces from the output as a string (that was how MicroWin worked before 10 | the DISM conversion to cmdlets) 11 | 12 | .EXAMPLE 13 | Microwin-RemoveFeatures -UseCmdlets $true 14 | #> 15 | param ( 16 | [Parameter(Mandatory = $true, Position = 0)] [bool]$UseCmdlets 17 | ) 18 | try { 19 | if ($UseCmdlets) { 20 | $featlist = (Get-WindowsOptionalFeature -Path "$scratchDir") 21 | 22 | $featlist = $featlist | Where-Object { 23 | $_.FeatureName -NotLike "*Defender*" -AND 24 | $_.FeatureName -NotLike "*Printing*" -AND 25 | $_.FeatureName -NotLike "*TelnetClient*" -AND 26 | $_.FeatureName -NotLike "*PowerShell*" -AND 27 | $_.FeatureName -NotLike "*NetFx*" -AND 28 | $_.FeatureName -NotLike "*Media*" -AND 29 | $_.FeatureName -NotLike "*NFS*" -AND 30 | $_.FeatureName -NotLike "*SearchEngine*" -AND 31 | $_.FeatureName -NotLike "*RemoteDesktop*" -AND 32 | $_.State -ne "Disabled" 33 | } 34 | } else { 35 | $featList = dism /english /image="$scratchDir" /get-features | Select-String -Pattern "Feature Name : " -CaseSensitive -SimpleMatch 36 | if ($?) { 37 | $featList = $featList -split "Feature Name : " | Where-Object {$_} 38 | # Exclude the same items. Note: for now, this doesn't exclude those features that are disabled. 39 | # This will appear in the future 40 | $featList = $featList | Where-Object { 41 | $_ -NotLike "*Defender*" -AND 42 | $_ -NotLike "*Printing*" -AND 43 | $_ -NotLike "*TelnetClient*" -AND 44 | $_ -NotLike "*PowerShell*" -AND 45 | $_ -NotLike "*NetFx*" -AND 46 | $_ -NotLike "*Media*" -AND 47 | $_ -NotLike "*NFS*" -AND 48 | $_ -NotLike "*SearchEngine*" -AND 49 | $_ -NotLike "*RemoteDesktop*" 50 | } 51 | } else { 52 | Write-Host "Features could not be obtained with DISM. MicroWin processing will continue, but features will be skipped." 53 | return 54 | } 55 | } 56 | 57 | if ($UseCmdlets) { 58 | foreach ($feature in $featList) { 59 | $status = "Removing feature $($feature.FeatureName)" 60 | Write-Progress -Activity "Removing features" -Status $status -PercentComplete ($counter++/$featlist.Count*100) 61 | Write-Debug "Removing feature $($feature.FeatureName)" 62 | Disable-WindowsOptionalFeature -Path "$scratchDir" -FeatureName $($feature.FeatureName) -Remove -ErrorAction SilentlyContinue -NoRestart 63 | } 64 | } else { 65 | foreach ($feature in $featList) { 66 | $status = "Removing feature $feature" 67 | Write-Progress -Activity "Removing features" -Status $status -PercentComplete ($counter++/$featlist.Count*100) 68 | Write-Debug "Removing feature $feature" 69 | dism /english /image="$scratchDir" /disable-feature /featurename=$feature /remove /quiet /norestart | Out-Null 70 | if ($? -eq $false) { 71 | Write-Host "Feature $feature could not be disabled." 72 | } 73 | } 74 | } 75 | Write-Progress -Activity "Removing features" -Status "Ready" -Completed 76 | Write-Host "You can re-enable the disabled features at any time, using either Windows Update or the SxS folder in <installation media>\Sources." 77 | } catch { 78 | Write-Host "Unable to get information about the features. A fallback will be used..." 79 | Write-Host "Error information: $($_.Exception.Message)" -ForegroundColor Yellow 80 | Microwin-RemoveFeatures -UseCmdlets $false 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-RemoveFileOrDirectory.ps1: -------------------------------------------------------------------------------- 1 | function Microwin-RemoveFileOrDirectory([string]$pathToDelete, [string]$mask = "", [switch]$Directory = $false) { 2 | if(([string]::IsNullOrEmpty($pathToDelete))) { return } 3 | if (-not (Test-Path -Path "$($pathToDelete)")) { return } 4 | 5 | $yesNo = Get-LocalizedYesNo 6 | Write-Host "[INFO] In Your local takeown expects '$($yesNo[0])' as a Yes answer." 7 | 8 | $itemsToDelete = [System.Collections.ArrayList]::new() 9 | 10 | if ($mask -eq "") { 11 | Write-Debug "Adding $($pathToDelete) to array." 12 | [void]$itemsToDelete.Add($pathToDelete) 13 | } else { 14 | Write-Debug "Adding $($pathToDelete) to array and mask is $($mask)" 15 | if ($Directory) { 16 | $itemsToDelete = Get-ChildItem $pathToDelete -Include $mask -Recurse -Directory 17 | } else { 18 | $itemsToDelete = Get-ChildItem $pathToDelete -Include $mask -Recurse 19 | } 20 | } 21 | 22 | foreach($itemToDelete in $itemsToDelete) { 23 | $status = "Deleting $($itemToDelete)" 24 | Write-Progress -Activity "Removing Items" -Status $status -PercentComplete ($counter++/$itemsToDelete.Count*100) 25 | 26 | if (Test-Path -Path "$($itemToDelete)" -PathType Container) { 27 | $status = "Deleting directory: $($itemToDelete)" 28 | 29 | takeown /r /d $yesNo[0] /a /f "$($itemToDelete)" 30 | icacls "$($itemToDelete)" /q /c /t /reset 31 | icacls $itemToDelete /setowner "*S-1-5-32-544" 32 | icacls $itemToDelete /grant "*S-1-5-32-544:(OI)(CI)F" /t /c /q 33 | Remove-Item -Force -Recurse "$($itemToDelete)" 34 | } 35 | elseif (Test-Path -Path "$($itemToDelete)" -PathType Leaf) { 36 | $status = "Deleting file: $($itemToDelete)" 37 | 38 | takeown /a /f "$($itemToDelete)" 39 | icacls "$($itemToDelete)" /q /c /t /reset 40 | icacls "$($itemToDelete)" /setowner "*S-1-5-32-544" 41 | icacls "$($itemToDelete)" /grant "*S-1-5-32-544:(OI)(CI)F" /t /c /q 42 | Remove-Item -Force "$($itemToDelete)" 43 | } 44 | } 45 | Write-Progress -Activity "Removing Items" -Status "Ready" -Completed 46 | } 47 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-RemoveProvisionedPackages.ps1: -------------------------------------------------------------------------------- 1 | function Microwin-RemoveProvisionedPackages() { 2 | <# 3 | .SYNOPSIS 4 | Removes AppX packages from a Windows image during MicroWin processing 5 | 6 | .PARAMETER UseCmdlets 7 | Determines whether or not to use the DISM cmdlets for processing. 8 | - If true, DISM cmdlets will be used 9 | - If false, calls to the DISM executable will be made whilst selecting bits and pieces from the output as a string (that was how MicroWin worked before 10 | the DISM conversion to cmdlets) 11 | 12 | .EXAMPLE 13 | Microwin-RemoveProvisionedPackages 14 | #> 15 | param ( 16 | [Parameter(Mandatory = $true, Position = 0)] [bool]$UseCmdlets 17 | ) 18 | try 19 | { 20 | if ($UseCmdlets) { 21 | $appxProvisionedPackages = Get-AppxProvisionedPackage -Path "$($scratchDir)" | Where-Object { 22 | $_.PackageName -NotLike "*AppInstaller*" -AND 23 | $_.PackageName -NotLike "*Store*" -and 24 | $_.PackageName -NotLike "*Notepad*" -and 25 | $_.PackageName -NotLike "*Printing*" -and 26 | $_.PackageName -NotLike "*YourPhone*" -and 27 | $_.PackageName -NotLike "*Xbox*" -and 28 | $_.PackageName -NotLike "*WindowsTerminal*" -and 29 | $_.PackageName -NotLike "*Calculator*" -and 30 | $_.PackageName -NotLike "*Photos*" -and 31 | $_.PackageName -NotLike "*VCLibs*" -and 32 | $_.PackageName -NotLike "*Paint*" -and 33 | $_.PackageName -NotLike "*Gaming*" -and 34 | $_.PackageName -NotLike "*Extension*" -and 35 | $_.PackageName -NotLike "*SecHealthUI*" -and 36 | $_.PackageName -NotLike "*ScreenSketch*" 37 | } 38 | } else { 39 | $appxProvisionedPackages = dism /english /image="$scratchDir" /get-provisionedappxpackages | Select-String -Pattern "PackageName : " -CaseSensitive -SimpleMatch 40 | if ($?) { 41 | $appxProvisionedPackages = $appxProvisionedPackages -split "PackageName : " | Where-Object {$_} 42 | # Exclude the same items. 43 | $appxProvisionedPackages = $appxProvisionedPackages | Where-Object { 44 | $_ -NotLike "*AppInstaller*" -AND 45 | $_ -NotLike "*Store*" -and 46 | $_ -NotLike "*Notepad*" -and 47 | $_ -NotLike "*Printing*" -and 48 | $_ -NotLike "*YourPhone*" -and 49 | $_ -NotLike "*Xbox*" -and 50 | $_ -NotLike "*WindowsTerminal*" -and 51 | $_ -NotLike "*Calculator*" -and 52 | $_ -NotLike "*Photos*" -and 53 | $_ -NotLike "*VCLibs*" -and 54 | $_ -NotLike "*Paint*" -and 55 | $_ -NotLike "*Gaming*" -and 56 | $_ -NotLike "*Extension*" -and 57 | $_ -NotLike "*SecHealthUI*" -and 58 | $_ -NotLike "*ScreenSketch*" 59 | } 60 | } else { 61 | Write-Host "AppX packages could not be obtained with DISM. MicroWin processing will continue, but AppX packages will be skipped." 62 | return 63 | } 64 | } 65 | 66 | $counter = 0 67 | if ($UseCmdlets) { 68 | foreach ($appx in $appxProvisionedPackages) { 69 | $status = "Removing Provisioned $($appx.PackageName)" 70 | Write-Progress -Activity "Removing Provisioned Apps" -Status $status -PercentComplete ($counter++/$appxProvisionedPackages.Count*100) 71 | try { 72 | Remove-AppxProvisionedPackage -Path "$scratchDir" -PackageName $appx.PackageName -ErrorAction SilentlyContinue 73 | } catch { 74 | Write-Host "Application $($appx.PackageName) could not be removed" 75 | continue 76 | } 77 | } 78 | } else { 79 | foreach ($appx in $appxProvisionedPackages) { 80 | $status = "Removing Provisioned $appx" 81 | Write-Progress -Activity "Removing Provisioned Apps" -Status $status -PercentComplete ($counter++/$appxProvisionedPackages.Count*100) 82 | dism /english /image="$scratchDir" /remove-provisionedappxpackage /packagename=$appx /quiet /norestart | Out-Null 83 | if ($? -eq $false) { 84 | Write-Host "AppX package $appx could not be removed." 85 | } 86 | } 87 | } 88 | Write-Progress -Activity "Removing Provisioned Apps" -Status "Ready" -Completed 89 | } 90 | catch 91 | { 92 | Write-Host "Unable to get information about the AppX packages. A fallback will be used..." 93 | Write-Host "Error information: $($_.Exception.Message)" -ForegroundColor Yellow 94 | Microwin-RemoveProvisionedPackages -UseCmdlets $false 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /functions/microwin/Microwin-TestCompatibleImage.ps1: -------------------------------------------------------------------------------- 1 | function Microwin-TestCompatibleImage() { 2 | <# 3 | .SYNOPSIS 4 | Checks the version of a Windows image and determines whether or not it is compatible with a specific feature depending on a desired version 5 | 6 | .PARAMETER Name 7 | imgVersion - The version of the Windows image 8 | desiredVersion - The version to compare the image version with 9 | #> 10 | 11 | param 12 | ( 13 | [Parameter(Mandatory, position=0)] 14 | [string]$imgVersion, 15 | 16 | [Parameter(Mandatory, position=1)] 17 | [Version]$desiredVersion 18 | ) 19 | 20 | try { 21 | $version = [Version]$imgVersion 22 | return $version -ge $desiredVersion 23 | } catch { 24 | return $False 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /functions/microwin/Toggle-MicrowinPanel.ps1: -------------------------------------------------------------------------------- 1 | function Toggle-MicrowinPanel { 2 | <# 3 | .SYNOPSIS 4 | Toggles the visibility of the Microwin options and ISO panels in the GUI. 5 | .DESCRIPTION 6 | This function toggles the visibility of the Microwin options and ISO panels in the GUI. 7 | .PARAMETER MicrowinOptionsPanel 8 | The panel containing Microwin options. 9 | .PARAMETER MicrowinISOPanel 10 | The panel containing the Microwin ISO options. 11 | .EXAMPLE 12 | Toggle-MicrowinPanel 1 13 | #> 14 | param ( 15 | [Parameter(Mandatory = $true, Position = 0)] 16 | [ValidateSet(1, 2)] 17 | [int]$PanelNumber 18 | ) 19 | 20 | if ($PanelNumber -eq 1) { 21 | $sync.MicrowinISOPanel.Visibility = 'Visible' 22 | $sync.MicrowinOptionsPanel.Visibility = 'Collapsed' 23 | 24 | } elseif ($PanelNumber -eq 2) { 25 | $sync.MicrowinOptionsPanel.Visibility = 'Visible' 26 | $sync.MicrowinISOPanel.Visibility = 'Collapsed' 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /functions/private/Add-SelectedAppsMenuItem.ps1: -------------------------------------------------------------------------------- 1 | function Add-SelectedAppsMenuItem { 2 | <# 3 | .SYNOPSIS 4 | This is a helper function that generates and adds the Menu Items to the Selected Apps Popup. 5 | 6 | .Parameter name 7 | The actual Name of an App like "Chrome" or "Brave" 8 | This name is contained in the "Content" property inside the applications.json 9 | .PARAMETER key 10 | The key which identifies an app object in applications.json 11 | For Chrome this would be "WPFInstallchrome" because "WPFInstall" is prepended automatically for each key in applications.json 12 | #> 13 | 14 | param ([string]$name, [string]$key) 15 | 16 | $selectedAppGrid = New-Object Windows.Controls.Grid 17 | 18 | $selectedAppGrid.ColumnDefinitions.Add((New-Object System.Windows.Controls.ColumnDefinition -Property @{Width = "*"})) 19 | $selectedAppGrid.ColumnDefinitions.Add((New-Object System.Windows.Controls.ColumnDefinition -Property @{Width = "30"})) 20 | 21 | # Sets the name to the Content as well as the Tooltip, because the parent Popup Border has a fixed width and text could "overflow". 22 | # With the tooltip, you can still read the whole entry on hover 23 | $selectedAppLabel = New-Object Windows.Controls.Label 24 | $selectedAppLabel.Content = $name 25 | $selectedAppLabel.ToolTip = $name 26 | $selectedAppLabel.HorizontalAlignment = "Left" 27 | $selectedAppLabel.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor") 28 | [System.Windows.Controls.Grid]::SetColumn($selectedAppLabel, 0) 29 | $selectedAppGrid.Children.Add($selectedAppLabel) 30 | 31 | $selectedAppRemoveButton = New-Object Windows.Controls.Button 32 | $selectedAppRemoveButton.FontFamily = "Segoe MDL2 Assets" 33 | $selectedAppRemoveButton.Content = [string]([char]0xE711) 34 | $selectedAppRemoveButton.HorizontalAlignment = "Center" 35 | $selectedAppRemoveButton.Tag = $key 36 | $selectedAppRemoveButton.ToolTip = "Remove the App from Selection" 37 | $selectedAppRemoveButton.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor") 38 | $selectedAppRemoveButton.SetResourceReference([Windows.Controls.Control]::StyleProperty, "HoverButtonStyle") 39 | 40 | # Highlight the Remove icon on Hover 41 | $selectedAppRemoveButton.Add_MouseEnter({ $this.Foreground = "Red" }) 42 | $selectedAppRemoveButton.Add_MouseLeave({ $this.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor") }) 43 | $selectedAppRemoveButton.Add_Click({ 44 | $sync.($this.Tag).isChecked = $false # On click of the remove button, we only have to uncheck the corresponding checkbox. This will kick of all neccessary changes to update the UI 45 | }) 46 | [System.Windows.Controls.Grid]::SetColumn($selectedAppRemoveButton, 1) 47 | $selectedAppGrid.Children.Add($selectedAppRemoveButton) 48 | # Add new Element to Popup 49 | $sync.selectedAppsstackPanel.Children.Add($selectedAppGrid) 50 | } 51 | -------------------------------------------------------------------------------- /functions/private/Copy-Files.ps1: -------------------------------------------------------------------------------- 1 | function Copy-Files { 2 | <# 3 | 4 | .DESCRIPTION 5 | Copies the contents of a given ISO file to a given destination 6 | .PARAMETER Path 7 | The source of the files to copy 8 | .PARAMETER Destination 9 | The destination to copy the files to 10 | .PARAMETER Recurse 11 | Determines whether or not to copy all files of the ISO file, including those in subdirectories 12 | .PARAMETER Force 13 | Determines whether or not to overwrite existing files 14 | .EXAMPLE 15 | Copy-Files "D:" "C:\ISOFile" -Recurse -Force 16 | 17 | #> 18 | param ( 19 | [string]$Path, 20 | [string]$Destination, 21 | [switch]$Recurse = $false, 22 | [switch]$Force = $false 23 | ) 24 | 25 | try { 26 | 27 | $files = Get-ChildItem -Path $path -Recurse:$recurse 28 | Write-Host "Copy $($files.Count) file(s) from $path to $destination" 29 | 30 | foreach ($file in $files) { 31 | $status = "Copying file {0} of {1}: {2}" -f $counter, $files.Count, $file.Name 32 | Write-Progress -Activity "Copy disc image files" -Status $status -PercentComplete ($counter++/$files.count*100) 33 | $restpath = $file.FullName -Replace $path, '' 34 | 35 | if ($file.PSIsContainer -eq $true) { 36 | Write-Debug "Creating $($destination + $restpath)" 37 | New-Item ($destination+$restpath) -Force:$force -Type Directory -ErrorAction SilentlyContinue 38 | } else { 39 | Write-Debug "Copy from $($file.FullName) to $($destination+$restpath)" 40 | Copy-Item $file.FullName ($destination+$restpath) -ErrorAction SilentlyContinue -Force:$force 41 | Set-ItemProperty -Path ($destination+$restpath) -Name IsReadOnly -Value $false 42 | } 43 | } 44 | Write-Progress -Activity "Copy disc image files" -Status "Ready" -Completed 45 | } catch { 46 | Write-Host "Unable to Copy all the files due to an unhandled exception" -ForegroundColor Yellow 47 | Write-Host "Error information: $($_.Exception.Message)`n" -ForegroundColor Yellow 48 | Write-Host "Additional information:" -ForegroundColor Yellow 49 | Write-Host $PSItem.Exception.StackTrace 50 | # Write possible suggestions 51 | Write-Host "`nIf you are using an antivirus, try configuring exclusions" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /functions/private/Find-AppsByNameOrDescription.ps1: -------------------------------------------------------------------------------- 1 | function Find-AppsByNameOrDescription { 2 | <# 3 | .SYNOPSIS 4 | Searches through the Apps on the Install Tab and hides all entries that do not match the string 5 | 6 | .PARAMETER SearchString 7 | The string to be searched for 8 | #> 9 | param( 10 | [Parameter(Mandatory=$false)] 11 | [string]$SearchString = "" 12 | ) 13 | # Reset the visibility if the search string is empty or the search is cleared 14 | if ([string]::IsNullOrWhiteSpace($SearchString)) { 15 | $sync.ItemsControl.Items | ForEach-Object { 16 | $_.Visibility = [Windows.Visibility]::Visible 17 | $_.Children | ForEach-Object { 18 | if ($null -ne $_) { 19 | $_.Visibility = [Windows.Visibility]::Visible 20 | } 21 | 22 | } 23 | } 24 | return 25 | } 26 | $sync.ItemsControl.Items | ForEach-Object { 27 | # Ensure ToggleButtons remain visible 28 | if ($_.Tag -like "CategoryToggleButton") { 29 | $_.Visibility = [Windows.Visibility]::Visible 30 | return 31 | } 32 | # Hide all CategoryWrapPanel and ToggleButton 33 | $_.Visibility = [Windows.Visibility]::Collapsed 34 | if ($_.Tag -like "CategoryWrapPanel_*") { 35 | # Search for Apps that match the search string 36 | $_.Children | Foreach-Object { 37 | $appEntry = $sync.configs.applicationsHashtable.$($_.Tag) 38 | if ($appEntry.Content -like "*$SearchString*" -or $appEntry.Description -like "*$SearchString*") { 39 | # Show the App and the parent CategoryWrapPanel if the string is found 40 | $_.Visibility = [Windows.Visibility]::Visible 41 | $_.parent.Visibility = [Windows.Visibility]::Visible 42 | } 43 | else { 44 | $_.Visibility = [Windows.Visibility]::Collapsed 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /functions/private/Find-TweaksByNameOrDescription.ps1: -------------------------------------------------------------------------------- 1 | function Find-TweaksByNameOrDescription { 2 | <# 3 | .SYNOPSIS 4 | Searches through the Tweaks on the Tweaks Tab and hides all entries that do not match the search string 5 | 6 | .PARAMETER SearchString 7 | The string to be searched for 8 | #> 9 | param( 10 | [Parameter(Mandatory=$false)] 11 | [string]$SearchString = "" 12 | ) 13 | 14 | # Reset the visibility if the search string is empty or the search is cleared 15 | if ([string]::IsNullOrWhiteSpace($SearchString)) { 16 | # Show all categories 17 | $tweakspanel = $sync.Form.FindName("tweakspanel") 18 | $tweakspanel.Children | ForEach-Object { 19 | $_.Visibility = [Windows.Visibility]::Visible 20 | 21 | # Foreach category section, show all items 22 | if ($_ -is [Windows.Controls.Border]) { 23 | $_.Visibility = [Windows.Visibility]::Visible 24 | 25 | # Find ItemsControl 26 | $dockPanel = $_.Child 27 | if ($dockPanel -is [Windows.Controls.DockPanel]) { 28 | $itemsControl = $dockPanel.Children | Where-Object { $_ -is [Windows.Controls.ItemsControl] } 29 | if ($itemsControl) { 30 | # Show items in the category 31 | foreach ($item in $itemsControl.Items) { 32 | if ($item -is [Windows.Controls.Label]) { 33 | $item.Visibility = [Windows.Visibility]::Visible 34 | } elseif ($item -is [Windows.Controls.DockPanel] -or 35 | $item -is [Windows.Controls.StackPanel]) { 36 | $item.Visibility = [Windows.Visibility]::Visible 37 | } 38 | } 39 | } 40 | } 41 | } 42 | } 43 | return 44 | } 45 | 46 | # Search for matching tweaks when search string is not null 47 | $tweakspanel = $sync.Form.FindName("tweakspanel") 48 | 49 | $tweakspanel.Children | ForEach-Object { 50 | $categoryBorder = $_ 51 | $categoryVisible = $false 52 | 53 | if ($_ -is [Windows.Controls.Border]) { 54 | # Find the ItemsControl 55 | $dockPanel = $_.Child 56 | if ($dockPanel -is [Windows.Controls.DockPanel]) { 57 | $itemsControl = $dockPanel.Children | Where-Object { $_ -is [Windows.Controls.ItemsControl] } 58 | if ($itemsControl) { 59 | $categoryLabel = $null 60 | 61 | # Process all items in the ItemsControl 62 | for ($i = 0; $i -lt $itemsControl.Items.Count; $i++) { 63 | $item = $itemsControl.Items[$i] 64 | 65 | if ($item -is [Windows.Controls.Label]) { 66 | $categoryLabel = $item 67 | $item.Visibility = [Windows.Visibility]::Collapsed 68 | } elseif ($item -is [Windows.Controls.DockPanel]) { 69 | $checkbox = $item.Children | Where-Object { $_ -is [Windows.Controls.CheckBox] } | Select-Object -First 1 70 | $label = $item.Children | Where-Object { $_ -is [Windows.Controls.Label] } | Select-Object -First 1 71 | 72 | if ($label -and ($label.Content -like "*$SearchString*" -or $label.ToolTip -like "*$SearchString*")) { 73 | $item.Visibility = [Windows.Visibility]::Visible 74 | if ($categoryLabel) { $categoryLabel.Visibility = [Windows.Visibility]::Visible } 75 | $categoryVisible = $true 76 | } else { 77 | $item.Visibility = [Windows.Visibility]::Collapsed 78 | } 79 | } elseif ($item -is [Windows.Controls.StackPanel]) { 80 | # StackPanel which contain checkboxes or other elements 81 | $checkbox = $item.Children | Where-Object { $_ -is [Windows.Controls.CheckBox] } | Select-Object -First 1 82 | 83 | if ($checkbox -and ($checkbox.Content -like "*$SearchString*" -or $checkbox.ToolTip -like "*$SearchString*")) { 84 | $item.Visibility = [Windows.Visibility]::Visible 85 | if ($categoryLabel) { $categoryLabel.Visibility = [Windows.Visibility]::Visible } 86 | $categoryVisible = $true 87 | } else { 88 | $item.Visibility = [Windows.Visibility]::Collapsed 89 | } 90 | } 91 | } 92 | } 93 | } 94 | 95 | # Set the visibility based on if any item matched 96 | $categoryBorder.Visibility = if ($categoryVisible) { [Windows.Visibility]::Visible } else { [Windows.Visibility]::Collapsed } 97 | 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /functions/private/Get-LocalizedYesNo.ps1: -------------------------------------------------------------------------------- 1 | function Get-LocalizedYesNo { 2 | <# 3 | .SYNOPSIS 4 | This function runs choice.exe and captures its output to extract yes no in a localized Windows 5 | 6 | .DESCRIPTION 7 | The function retrieves the output of the command 'cmd /c "choice <nul 2>nul"' and converts the default output for Yes and No 8 | in the localized format, such as "Yes=<first character>, No=<second character>". 9 | 10 | .EXAMPLE 11 | $yesNoArray = Get-LocalizedYesNo 12 | Write-Host "Yes=$($yesNoArray[0]), No=$($yesNoArray[1])" 13 | #> 14 | 15 | # Run choice and capture its options as output 16 | # The output shows the options for Yes and No as "[Y,N]?" in the (partitially) localized format. 17 | # eg. English: [Y,N]? 18 | # Dutch: [Y,N]? 19 | # German: [J,N]? 20 | # French: [O,N]? 21 | # Spanish: [S,N]? 22 | # Italian: [S,N]? 23 | # Russian: [Y,N]? 24 | 25 | $line = cmd /c "choice <nul 2>nul" 26 | $charactersArray = @() 27 | $regexPattern = '([a-zA-Z])' 28 | $charactersArray = [regex]::Matches($line, $regexPattern) | ForEach-Object { $_.Groups[1].Value } 29 | 30 | Write-Debug "According to takeown.exe local Yes is $charactersArray[0]" 31 | # Return the array of characters 32 | return $charactersArray 33 | 34 | } 35 | -------------------------------------------------------------------------------- /functions/private/Get-WPFObjectName.ps1: -------------------------------------------------------------------------------- 1 | function Get-WPFObjectName { 2 | <# 3 | .SYNOPSIS 4 | This is a helper function that generates an objectname with the prefix WPF that can be used as a Powershell Variable after compilation. 5 | To achieve this, all characters that are not a-z, A-Z or 0-9 are simply removed from the name. 6 | 7 | .PARAMETER type 8 | The type of object for which the name should be generated. (e.g. Label, Button, CheckBox...) 9 | 10 | .PARAMETER name 11 | The name or description to be used for the object. (invalid characters are removed) 12 | 13 | .OUTPUTS 14 | A string that can be used as a object/variable name in powershell. 15 | For example: WPFLabelMicrosoftTools 16 | 17 | .EXAMPLE 18 | Get-WPFObjectName -type Label -name "Microsoft Tools" 19 | #> 20 | 21 | param( 22 | [Parameter(Mandatory, position=0)] 23 | [string]$type, 24 | 25 | [Parameter(position=1)] 26 | [string]$name 27 | ) 28 | 29 | $Output = $("WPF"+$type+$name) -replace '[^a-zA-Z0-9]', '' 30 | return $Output 31 | } 32 | -------------------------------------------------------------------------------- /functions/private/Get-WinUtilCheckBoxes.ps1: -------------------------------------------------------------------------------- 1 | Function Get-WinUtilCheckBoxes { 2 | 3 | <# 4 | 5 | .SYNOPSIS 6 | Finds all checkboxes that are checked on the specific tab and inputs them into a script. 7 | 8 | .PARAMETER unCheck 9 | Whether to uncheck the checkboxes that are checked. Defaults to true 10 | 11 | .OUTPUTS 12 | A List containing the name of each checked checkbox 13 | 14 | .EXAMPLE 15 | Get-WinUtilCheckBoxes "WPFInstall" 16 | 17 | #> 18 | 19 | Param( 20 | [boolean]$unCheck = $false 21 | ) 22 | 23 | $Output = @{ 24 | Install = @() 25 | WPFTweaks = @() 26 | WPFFeature = @() 27 | WPFInstall = @() 28 | } 29 | 30 | $CheckBoxes = $sync.GetEnumerator() | Where-Object { $_.Value -is [System.Windows.Controls.CheckBox] } 31 | 32 | # First check and add WPFTweaksRestorePoint if checked 33 | $RestorePoint = $CheckBoxes | Where-Object { $_.Key -eq 'WPFTweaksRestorePoint' -and $_.Value.IsChecked -eq $true } 34 | if ($RestorePoint) { 35 | $Output["WPFTweaks"] = @('WPFTweaksRestorePoint') 36 | Write-Debug "Adding WPFTweaksRestorePoint as first in WPFTweaks" 37 | 38 | if ($unCheck) { 39 | $RestorePoint.Value.IsChecked = $false 40 | } 41 | } 42 | 43 | foreach ($CheckBox in $CheckBoxes) { 44 | if ($CheckBox.Key -eq 'WPFTweaksRestorePoint') { continue } # Skip since it's already handled 45 | 46 | $group = if ($CheckBox.Key.StartsWith("WPFInstall")) { "Install" } 47 | elseif ($CheckBox.Key.StartsWith("WPFTweaks")) { "WPFTweaks" } 48 | elseif ($CheckBox.Key.StartsWith("WPFFeature")) { "WPFFeature" } 49 | if ($group) { 50 | if ($CheckBox.Value.IsChecked -eq $true) { 51 | $feature = switch ($group) { 52 | "Install" { 53 | # Get the winget value 54 | [PsCustomObject]@{ 55 | winget="$($sync.configs.applications.$($CheckBox.Name).winget)"; 56 | choco="$($sync.configs.applications.$($CheckBox.Name).choco)"; 57 | } 58 | 59 | } 60 | default { 61 | $CheckBox.Name 62 | } 63 | } 64 | 65 | if (-not $Output.ContainsKey($group)) { 66 | $Output[$group] = @() 67 | } 68 | if ($group -eq "Install") { 69 | $Output["WPFInstall"] += $CheckBox.Name 70 | Write-Debug "Adding: $($CheckBox.Name) under: WPFInstall" 71 | } 72 | 73 | Write-Debug "Adding: $($feature) under: $($group)" 74 | $Output[$group] += $feature 75 | 76 | if ($unCheck) { 77 | $CheckBox.Value.IsChecked = $false 78 | } 79 | } 80 | } 81 | } 82 | return $Output 83 | } 84 | -------------------------------------------------------------------------------- /functions/private/Get-WinUtilInstallerProcess.ps1: -------------------------------------------------------------------------------- 1 | function Get-WinUtilInstallerProcess { 2 | <# 3 | 4 | .SYNOPSIS 5 | Checks if the given process is running 6 | 7 | .PARAMETER Process 8 | The process to check 9 | 10 | .OUTPUTS 11 | Boolean - True if the process is running 12 | 13 | #> 14 | 15 | param($Process) 16 | 17 | if ($Null -eq $Process) { 18 | return $false 19 | } 20 | if (Get-Process -Id $Process.Id -ErrorAction SilentlyContinue) { 21 | return $true 22 | } 23 | return $false 24 | } 25 | -------------------------------------------------------------------------------- /functions/private/Get-WinUtilSelectedPackages.ps1: -------------------------------------------------------------------------------- 1 | function Get-WinUtilSelectedPackages 2 | { 3 | <# 4 | .SYNOPSIS 5 | Sorts given packages based on installer preference and availability. 6 | 7 | .OUTPUTS 8 | Hashtable. Key = Package Manager, Value = ArrayList of packages to install 9 | #> 10 | param ( 11 | [Parameter(Mandatory=$true)] 12 | $PackageList, 13 | [Parameter(Mandatory=$true)] 14 | [PackageManagers]$Preference 15 | ) 16 | 17 | if ($PackageList.count -eq 1) { 18 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }) 19 | } else { 20 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }) 21 | } 22 | 23 | $packages = [System.Collections.Hashtable]::new() 24 | $packagesWinget = [System.Collections.ArrayList]::new() 25 | $packagesChoco = [System.Collections.ArrayList]::new() 26 | $packages[[PackageManagers]::Winget] = $packagesWinget 27 | $packages[[PackageManagers]::Choco] = $packagesChoco 28 | 29 | Write-Debug "Checking packages using Preference '$($Preference)'" 30 | 31 | foreach ($package in $PackageList) { 32 | switch ($Preference) { 33 | "Choco" { 34 | if ($package.choco -eq "na") { 35 | Write-Debug "$($package.content) has no Choco value." 36 | $null = $packagesWinget.add($($package.winget)) 37 | Write-Host "Queueing $($package.winget) for Winget" 38 | } else { 39 | $null = $packagesChoco.add($package.choco) 40 | Write-Host "Queueing $($package.choco) for Chocolatey" 41 | } 42 | break 43 | } 44 | "Winget" { 45 | if ($package.winget -eq "na") { 46 | Write-Debug "$($package.content) has no Winget value." 47 | $null = $packagesChoco.add($package.choco) 48 | Write-Host "Queueing $($package.choco) for Chocolatey" 49 | } else { 50 | $null = $packagesWinget.add($($package.winget)) 51 | Write-Host "Queueing $($package.winget) for Winget" 52 | } 53 | break 54 | } 55 | } 56 | } 57 | 58 | return $packages 59 | } 60 | -------------------------------------------------------------------------------- /functions/private/Get-WinUtilToggleStatus.ps1: -------------------------------------------------------------------------------- 1 | Function Get-WinUtilToggleStatus { 2 | <# 3 | 4 | .SYNOPSIS 5 | Pulls the registry keys for the given toggle switch and checks whether the toggle should be checked or unchecked 6 | 7 | .PARAMETER ToggleSwitch 8 | The name of the toggle to check 9 | 10 | .OUTPUTS 11 | Boolean to set the toggle's status to 12 | 13 | #> 14 | 15 | Param($ToggleSwitch) 16 | 17 | $ToggleSwitchReg = $sync.configs.tweaks.$ToggleSwitch.registry 18 | 19 | try { 20 | if (($ToggleSwitchReg.path -imatch "hku") -and !(Get-PSDrive -Name HKU -ErrorAction SilentlyContinue)) { 21 | $null = (New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS) 22 | if (Get-PSDrive -Name HKU -ErrorAction SilentlyContinue) { 23 | Write-Debug "HKU drive created successfully" 24 | } else { 25 | Write-Debug "Failed to create HKU drive" 26 | } 27 | } 28 | } catch { 29 | Write-Error "An error occurred regarding the HKU Drive: $_" 30 | return $false 31 | } 32 | 33 | if ($ToggleSwitchReg) { 34 | $count = 0 35 | 36 | foreach ($regentry in $ToggleSwitchReg) { 37 | try { 38 | if (!(Test-Path $regentry.Path)) { 39 | New-Item -Path $regentry.Path -Force | Out-Null 40 | } 41 | $regstate = (Get-ItemProperty -path $regentry.Path).$($regentry.Name) 42 | if ($regstate -eq $regentry.Value) { 43 | $count += 1 44 | Write-Debug "$($regentry.Name) is true (state: $regstate, value: $($regentry.Value), original: $($regentry.OriginalValue))" 45 | } else { 46 | Write-Debug "$($regentry.Name) is false (state: $regstate, value: $($regentry.Value), original: $($regentry.OriginalValue))" 47 | } 48 | if (!$regstate) { 49 | switch ($regentry.DefaultState) { 50 | "true" { 51 | $regstate = $regentry.Value 52 | $count += 1 53 | } 54 | "false" { 55 | $regstate = $regentry.OriginalValue 56 | } 57 | default { 58 | Write-Error "Entry for $($regentry.Name) does not exist and no DefaultState is defined." 59 | $regstate = $regentry.OriginalValue 60 | } 61 | } 62 | } 63 | } catch { 64 | Write-Error "An unexpected error occurred: $_" 65 | } 66 | } 67 | 68 | if ($count -eq $ToggleSwitchReg.Count) { 69 | Write-Debug "$($ToggleSwitchReg.Name) is true (count: $count)" 70 | return $true 71 | } else { 72 | Write-Debug "$($ToggleSwitchReg.Name) is false (count: $count)" 73 | return $false 74 | } 75 | } else { 76 | return $false 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /functions/private/Get-WinUtilVariables.ps1: -------------------------------------------------------------------------------- 1 | function Get-WinUtilVariables { 2 | 3 | <# 4 | .SYNOPSIS 5 | Gets every form object of the provided type 6 | 7 | .OUTPUTS 8 | List containing every object that matches the provided type 9 | #> 10 | param ( 11 | [Parameter()] 12 | [string[]]$Type 13 | ) 14 | $keys = ($sync.keys).where{ $_ -like "WPF*" } 15 | if ($Type) { 16 | $output = $keys | ForEach-Object { 17 | try { 18 | $objType = $sync["$psitem"].GetType().Name 19 | if ($Type -contains $objType) { 20 | Write-Output $psitem 21 | } 22 | } catch { 23 | <#I am here so errors don't get outputted for a couple variables that don't have the .GetType() attribute#> 24 | } 25 | } 26 | return $output 27 | } 28 | return $keys 29 | } 30 | -------------------------------------------------------------------------------- /functions/private/Hide-WPFInstallAppBusy.ps1: -------------------------------------------------------------------------------- 1 | function Hide-WPFInstallAppBusy { 2 | <# 3 | .SYNOPSIS 4 | Hides the busy overlay in the install app area of the WPF form. 5 | This is used to indicate that an install or uninstall has finished. 6 | #> 7 | $sync.form.Dispatcher.Invoke([action]{ 8 | $sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Collapsed 9 | $sync.InstallAppAreaBorder.IsEnabled = $true 10 | $sync.InstallAppAreaScrollViewer.Effect.Radius = 0 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /functions/private/Initalize-InstallAppEntry.ps1: -------------------------------------------------------------------------------- 1 | function Initialize-InstallAppEntry { 2 | <# 3 | .SYNOPSIS 4 | Creates the app entry to be placed on the isntall tab for a given app 5 | Used to as part of the Install Tab UI generation 6 | .PARAMETER TargetElement 7 | The Element into which the Apps should be placed 8 | .PARAMETER appKey 9 | The Key of the app inside the $sync.configs.applicationsHashtable 10 | #> 11 | param( 12 | [Windows.Controls.WrapPanel]$TargetElement, 13 | $appKey 14 | ) 15 | 16 | # Create the outer Border for the application type 17 | $border = New-Object Windows.Controls.Border 18 | $border.Style = $sync.Form.Resources.AppEntryBorderStyle 19 | $border.Tag = $appKey 20 | $border.ToolTip = $Apps.$appKey.description 21 | $border.Add_MouseLeftButtonUp({ 22 | $childCheckbox = ($this.Child | Where-Object {$_.Template.TargetType -eq [System.Windows.Controls.Checkbox]})[0] 23 | $childCheckBox.isChecked = -not $childCheckbox.IsChecked 24 | }) 25 | $border.Add_MouseEnter({ 26 | if (($sync.$($this.Tag).IsChecked) -eq $false) { 27 | $this.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallHighlightedColor") 28 | } 29 | }) 30 | $border.Add_MouseLeave({ 31 | if (($sync.$($this.Tag).IsChecked) -eq $false) { 32 | $this.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallUnselectedColor") 33 | } 34 | }) 35 | $border.Add_MouseRightButtonUp({ 36 | # Store the selected app in a global variable so it can be used in the popup 37 | $sync.appPopupSelectedApp = $this.Tag 38 | # Set the popup position to the current mouse position 39 | $sync.appPopup.PlacementTarget = $this 40 | $sync.appPopup.IsOpen = $true 41 | }) 42 | 43 | $checkBox = New-Object Windows.Controls.CheckBox 44 | $checkBox.Name = $appKey 45 | $checkbox.Style = $sync.Form.Resources.AppEntryCheckboxStyle 46 | $checkbox.Add_Checked({ 47 | Invoke-WPFSelectedAppsUpdate -type "Add" -checkbox $this 48 | $borderElement = $this.Parent 49 | $borderElement.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallSelectedColor") 50 | }) 51 | 52 | $checkbox.Add_Unchecked({ 53 | Invoke-WPFSelectedAppsUpdate -type "Remove" -checkbox $this 54 | $borderElement = $this.Parent 55 | $borderElement.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallUnselectedColor") 56 | }) 57 | 58 | # Create the TextBlock for the application name 59 | $appName = New-Object Windows.Controls.TextBlock 60 | $appName.Style = $sync.Form.Resources.AppEntryNameStyle 61 | $appName.Text = $Apps.$appKey.content 62 | 63 | # Add the name to the Checkbox 64 | $checkBox.Content = $appName 65 | 66 | # Add accessibility properties to make the elements screen reader friendly 67 | $checkBox.SetValue([Windows.Automation.AutomationProperties]::NameProperty, $Apps.$appKey.content) 68 | $border.SetValue([Windows.Automation.AutomationProperties]::NameProperty, $Apps.$appKey.content) 69 | 70 | $border.Child = $checkBox 71 | # Add the border to the corresponding Category 72 | $TargetElement.Children.Add($border) | Out-Null 73 | return $checkbox 74 | } 75 | -------------------------------------------------------------------------------- /functions/private/Initialize-InstallCategoryAppList.ps1: -------------------------------------------------------------------------------- 1 | function Initialize-InstallCategoryAppList { 2 | <# 3 | .SYNOPSIS 4 | Clears the Target Element and sets up a "Loading" message. This is done, because loading of all apps can take a bit of time in some scenarios 5 | Iterates through all Categories and Apps and adds them to the UI 6 | Used to as part of the Install Tab UI generation 7 | .PARAMETER TargetElement 8 | The Element into which the Categories and Apps should be placed 9 | .PARAMETER Apps 10 | The Hashtable of Apps to be added to the UI 11 | The Categories are also extracted from the Apps Hashtable 12 | 13 | #> 14 | param( 15 | $TargetElement, 16 | $Apps 17 | ) 18 | function Add-Category { 19 | param( 20 | [string]$Category, 21 | [Windows.Controls.ItemsControl]$TargetElement 22 | ) 23 | 24 | $toggleButton = New-Object Windows.Controls.Label 25 | $toggleButton.Content = "$Category" 26 | $toggleButton.Tag = "CategoryToggleButton" 27 | $sync.$Category = $Category 28 | 29 | $null = $TargetElement.Items.Add($toggleButton) 30 | } 31 | 32 | 33 | # Pre-group apps by category 34 | $appsByCategory = @{} 35 | foreach ($appKey in $Apps.Keys) { 36 | $category = $Apps.$appKey.Category 37 | if (-not $appsByCategory.ContainsKey($category)) { 38 | $appsByCategory[$category] = @() 39 | } 40 | $appsByCategory[$category] += $appKey 41 | } 42 | foreach ($category in $($appsByCategory.Keys | Sort-Object)) { 43 | Add-Category -Category $category -TargetElement $TargetElement 44 | $wrapPanel = New-Object Windows.Controls.WrapPanel 45 | $wrapPanel.Orientation = "Horizontal" 46 | $wrapPanel.HorizontalAlignment = "Stretch" 47 | $wrapPanel.VerticalAlignment = "Center" 48 | $wrapPanel.Margin = New-Object Windows.Thickness(0, 0, 0, 20) 49 | $wrapPanel.Visibility = [Windows.Visibility]::Visible 50 | $wrapPanel.Tag = "CategoryWrapPanel_$category" 51 | $null = $TargetElement.Items.Add($wrapPanel) 52 | $appsByCategory[$category] |Sort-Object | ForEach-Object { 53 | $sync.$_ = $(Initialize-InstallAppEntry -TargetElement $wrapPanel -AppKey $_) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /functions/private/Install-WinUtilChoco.ps1: -------------------------------------------------------------------------------- 1 | function Install-WinUtilChoco { 2 | 3 | <# 4 | 5 | .SYNOPSIS 6 | Installs Chocolatey if it is not already installed 7 | 8 | #> 9 | 10 | try { 11 | Write-Host "Checking if Chocolatey is Installed..." 12 | 13 | if((Test-WinUtilPackageManager -choco) -eq "installed") { 14 | return 15 | } 16 | # Install logic taken from https://chocolatey.org/install#individual 17 | Write-Host "Seems Chocolatey is not installed, installing now." 18 | Set-ExecutionPolicy Bypass -Scope Process -Force; 19 | [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; 20 | Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) 21 | 22 | } catch { 23 | Write-Host "===========================================" -Foregroundcolor Red 24 | Write-Host "-- Chocolatey failed to install ---" -Foregroundcolor Red 25 | Write-Host "===========================================" -Foregroundcolor Red 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /functions/private/Install-WinUtilWinget.ps1: -------------------------------------------------------------------------------- 1 | function Install-WinUtilWinget { 2 | <# 3 | 4 | .SYNOPSIS 5 | Installs Winget if it is not already installed. 6 | 7 | .DESCRIPTION 8 | This function will download the latest version of Winget and install it. If Winget is already installed, it will do nothing. 9 | #> 10 | $isWingetInstalled = Test-WinUtilPackageManager -winget 11 | 12 | try { 13 | if ($isWingetInstalled -eq "installed") { 14 | Write-Host "`nWinget is already installed.`r" -ForegroundColor Green 15 | return 16 | } elseif ($isWingetInstalled -eq "outdated") { 17 | Write-Host "`nWinget is Outdated. Continuing with install.`r" -ForegroundColor Yellow 18 | } else { 19 | Write-Host "`nWinget is not Installed. Continuing with install.`r" -ForegroundColor Red 20 | } 21 | 22 | 23 | # Gets the computer's information 24 | if ($null -eq $sync.ComputerInfo) { 25 | $ComputerInfo = Get-ComputerInfo -ErrorAction Stop 26 | } else { 27 | $ComputerInfo = $sync.ComputerInfo 28 | } 29 | 30 | if (($ComputerInfo.WindowsVersion) -lt "1809") { 31 | # Checks if Windows Version is too old for Winget 32 | Write-Host "Winget is not supported on this version of Windows (Pre-1809)" -ForegroundColor Red 33 | return 34 | } 35 | 36 | Write-Host "Attempting to install/update Winget`r" 37 | try { 38 | $wingetCmd = Get-Command winget -ErrorAction Stop 39 | Write-Information "Attempting to update WinGet using WinGet..." 40 | $result = Start-Process -FilePath "`"$($wingetCmd.Source)`"" -ArgumentList "install -e --accept-source-agreements --accept-package-agreements Microsoft.AppInstaller" -Wait -NoNewWindow -PassThru 41 | if ($result.ExitCode -ne 0) { 42 | throw "WinGet update failed with exit code: $($result.ExitCode)" 43 | } 44 | Write-Output "Refreshing Environment Variables...`n" 45 | $ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") 46 | return 47 | } catch { 48 | Write-Information "WinGet not found or update failed. Attempting to install from Microsoft Store..." 49 | } 50 | try { 51 | Write-Host "Attempting to repair WinGet using Repair-WinGetPackageManager..." -ForegroundColor Yellow 52 | 53 | # Check if Windows version supports Repair-WinGetPackageManager (24H2 and above) 54 | if ([System.Environment]::OSVersion.Version.Build -ge 26100) { 55 | Repair-WinGetPackageManager -Force -Latest -Verbose 56 | # Verify if repair was successful 57 | $wingetCmd = Get-Command winget -ErrorAction Stop 58 | Write-Host "WinGet repair successful!" -ForegroundColor Green 59 | } else { 60 | Write-Host "Repair-WinGetPackageManager is only available on Windows 24H2 and above. Your version doesn't support this method." -ForegroundColor Yellow 61 | throw "Windows version not supported for repair method" 62 | } 63 | 64 | Write-Output "Refreshing Environment Variables...`n" 65 | $ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") 66 | return 67 | 68 | } catch { 69 | Write-Error "All installation methods failed. Unable to install WinGet." 70 | throw 71 | } 72 | } catch { 73 | Write-Error "An error occurred during WinGet installation: $_" 74 | throw 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /functions/private/Invoke-WinUtilCurrentSystem.ps1: -------------------------------------------------------------------------------- 1 | Function Invoke-WinUtilCurrentSystem { 2 | 3 | <# 4 | 5 | .SYNOPSIS 6 | Checks to see what tweaks have already been applied and what programs are installed, and checks the according boxes 7 | 8 | .EXAMPLE 9 | Get-WinUtilCheckBoxes "WPFInstall" 10 | 11 | #> 12 | 13 | param( 14 | $CheckBox 15 | ) 16 | if ($CheckBox -eq "choco") { 17 | $apps = (choco list | Select-String -Pattern "^\S+").Matches.Value 18 | $filter = Get-WinUtilVariables -Type Checkbox | Where-Object {$psitem -like "WPFInstall*"} 19 | $sync.GetEnumerator() | Where-Object {$psitem.Key -in $filter} | ForEach-Object { 20 | $dependencies = @($sync.configs.applications.$($psitem.Key).choco -split ";") 21 | if ($dependencies -in $apps) { 22 | Write-Output $psitem.name 23 | } 24 | } 25 | } 26 | 27 | if ($checkbox -eq "winget") { 28 | 29 | $originalEncoding = [Console]::OutputEncoding 30 | [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new() 31 | $Sync.InstalledPrograms = winget list -s winget | Select-Object -skip 3 | ConvertFrom-String -PropertyNames "Name", "Id", "Version", "Available" -Delimiter '\s{2,}' 32 | [Console]::OutputEncoding = $originalEncoding 33 | 34 | $filter = Get-WinUtilVariables -Type Checkbox | Where-Object {$psitem -like "WPFInstall*"} 35 | $sync.GetEnumerator() | Where-Object {$psitem.Key -in $filter} | ForEach-Object { 36 | $dependencies = @($sync.configs.applications.$($psitem.Key).winget -split ";") 37 | 38 | if ($dependencies[-1] -in $sync.InstalledPrograms.Id) { 39 | Write-Output $psitem.name 40 | } 41 | } 42 | } 43 | 44 | if($CheckBox -eq "tweaks") { 45 | 46 | if(!(Test-Path 'HKU:\')) {$null = (New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS)} 47 | $ScheduledTasks = Get-ScheduledTask 48 | 49 | $sync.configs.tweaks | Get-Member -MemberType NoteProperty | ForEach-Object { 50 | 51 | $Config = $psitem.Name 52 | #WPFEssTweaksTele 53 | $registryKeys = $sync.configs.tweaks.$Config.registry 54 | $scheduledtaskKeys = $sync.configs.tweaks.$Config.scheduledtask 55 | $serviceKeys = $sync.configs.tweaks.$Config.service 56 | 57 | if($registryKeys -or $scheduledtaskKeys -or $serviceKeys) { 58 | $Values = @() 59 | 60 | 61 | Foreach ($tweaks in $registryKeys) { 62 | Foreach($tweak in $tweaks) { 63 | 64 | if(test-path $tweak.Path) { 65 | $actualValue = Get-ItemProperty -Name $tweak.Name -Path $tweak.Path -ErrorAction SilentlyContinue | Select-Object -ExpandProperty $($tweak.Name) 66 | $expectedValue = $tweak.Value 67 | if ($expectedValue -notlike $actualValue) { 68 | $values += $False 69 | } 70 | } else { 71 | $values += $False 72 | } 73 | } 74 | } 75 | 76 | Foreach ($tweaks in $scheduledtaskKeys) { 77 | Foreach($tweak in $tweaks) { 78 | $task = $ScheduledTasks | Where-Object {$($psitem.TaskPath + $psitem.TaskName) -like "\$($tweak.name)"} 79 | 80 | if($task) { 81 | $actualValue = $task.State 82 | $expectedValue = $tweak.State 83 | if ($expectedValue -ne $actualValue) { 84 | $values += $False 85 | } 86 | } 87 | } 88 | } 89 | 90 | Foreach ($tweaks in $serviceKeys) { 91 | Foreach($tweak in $tweaks) { 92 | $Service = Get-Service -Name $tweak.Name 93 | 94 | if($Service) { 95 | $actualValue = $Service.StartType 96 | $expectedValue = $tweak.StartupType 97 | if ($expectedValue -ne $actualValue) { 98 | $values += $False 99 | } 100 | } 101 | } 102 | } 103 | 104 | if($values -notcontains $false) { 105 | Write-Output $Config 106 | } 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /functions/private/Invoke-WinUtilExplorerUpdate.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WinUtilExplorerUpdate { 2 | <# 3 | .SYNOPSIS 4 | Refreshes the Windows Explorer 5 | #> 6 | 7 | param ( 8 | [string]$action = "refresh" 9 | ) 10 | 11 | if ($action -eq "refresh") { 12 | Invoke-WPFRunspace -DebugPreference $DebugPreference -ScriptBlock { 13 | # Send the WM_SETTINGCHANGE message to all windows 14 | Add-Type -TypeDefinition @" 15 | using System; 16 | using System.Runtime.InteropServices; 17 | public class Win32 { 18 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] 19 | public static extern IntPtr SendMessageTimeout( 20 | IntPtr hWnd, 21 | uint Msg, 22 | IntPtr wParam, 23 | string lParam, 24 | uint fuFlags, 25 | uint uTimeout, 26 | out IntPtr lpdwResult); 27 | } 28 | "@ 29 | 30 | $HWND_BROADCAST = [IntPtr]0xffff 31 | $WM_SETTINGCHANGE = 0x1A 32 | $SMTO_ABORTIFHUNG = 0x2 33 | $timeout = 100 34 | 35 | # Send the broadcast message to all windows 36 | [Win32]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [IntPtr]::Zero, "ImmersiveColorSet", $SMTO_ABORTIFHUNG, $timeout, [ref]([IntPtr]::Zero)) 37 | } 38 | } elseif ($action -eq "restart") { 39 | # Restart the Windows Explorer 40 | taskkill.exe /F /IM "explorer.exe" 41 | Start-Process "explorer.exe" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /functions/private/Invoke-WinUtilFeatureInstall.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WinUtilFeatureInstall { 2 | <# 3 | 4 | .SYNOPSIS 5 | Converts all the values from the tweaks.json and routes them to the appropriate function 6 | 7 | #> 8 | 9 | param( 10 | $CheckBox 11 | ) 12 | 13 | $x = 0 14 | 15 | $CheckBox | ForEach-Object { 16 | if($sync.configs.feature.$psitem.feature) { 17 | Foreach( $feature in $sync.configs.feature.$psitem.feature ) { 18 | try { 19 | Write-Host "Installing $feature" 20 | Enable-WindowsOptionalFeature -Online -FeatureName $feature -All -NoRestart 21 | } catch { 22 | if ($psitem.Exception.Message -like "*requires elevation*") { 23 | Write-Warning "Unable to Install $feature due to permissions. Are you running as admin?" 24 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" }) 25 | } else { 26 | 27 | Write-Warning "Unable to Install $feature due to unhandled exception" 28 | Write-Warning $psitem.Exception.StackTrace 29 | } 30 | } 31 | } 32 | } 33 | if($sync.configs.feature.$psitem.InvokeScript) { 34 | Foreach( $script in $sync.configs.feature.$psitem.InvokeScript ) { 35 | try { 36 | $Scriptblock = [scriptblock]::Create($script) 37 | 38 | Write-Host "Running Script for $psitem" 39 | Invoke-Command $scriptblock -ErrorAction stop 40 | } catch { 41 | if ($psitem.Exception.Message -like "*requires elevation*") { 42 | Write-Warning "Unable to Install $feature due to permissions. Are you running as admin?" 43 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" }) 44 | } else { 45 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" }) 46 | Write-Warning "Unable to Install $feature due to unhandled exception" 47 | Write-Warning $psitem.Exception.StackTrace 48 | } 49 | } 50 | } 51 | } 52 | $X++ 53 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($x/$CheckBox.Count) }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /functions/private/Invoke-WinUtilGPU.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WinUtilGPU { 2 | $gpuInfo = Get-CimInstance Win32_VideoController 3 | 4 | # GPUs to blacklist from using Demanding Theming 5 | $lowPowerGPUs = ( 6 | "*NVIDIA GeForce*M*", 7 | "*NVIDIA GeForce*Laptop*", 8 | "*NVIDIA GeForce*GT*", 9 | "*AMD Radeon(TM)*", 10 | "*Intel(R) HD Graphics*", 11 | "*UHD*" 12 | 13 | ) 14 | 15 | foreach ($gpu in $gpuInfo) { 16 | foreach ($gpuPattern in $lowPowerGPUs) { 17 | if ($gpu.Name -like $gpuPattern) { 18 | return $false 19 | } 20 | } 21 | } 22 | return $true 23 | } 24 | -------------------------------------------------------------------------------- /functions/private/Invoke-WinUtilSSHServer.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WinUtilSSHServer { 2 | <# 3 | .SYNOPSIS 4 | Enables OpenSSH server to remote into your windows device 5 | #> 6 | 7 | # Get the latest version of OpenSSH Server 8 | $FeatureName = Get-WindowsCapability -Online | Where-Object { $_.Name -like "OpenSSH.Server*" } 9 | 10 | # Install the OpenSSH Server feature if not already installed 11 | if ($FeatureName.State -ne "Installed") { 12 | Write-Host "Enabling OpenSSH Server" 13 | Add-WindowsCapability -Online -Name $FeatureName.Name 14 | } 15 | 16 | # Sets up the OpenSSH Server service 17 | Write-Host "Starting the services" 18 | Start-Service -Name sshd 19 | Set-Service -Name sshd -StartupType Automatic 20 | 21 | # Sets up the ssh-agent service 22 | Start-Service 'ssh-agent' 23 | Set-Service -Name 'ssh-agent' -StartupType 'Automatic' 24 | 25 | # Confirm the required services are running 26 | $SSHDaemonService = Get-Service -Name sshd 27 | $SSHAgentService = Get-Service -Name 'ssh-agent' 28 | 29 | if ($SSHDaemonService.Status -eq 'Running') { 30 | Write-Host "OpenSSH Server is running." 31 | } else { 32 | try { 33 | Write-Host "OpenSSH Server is not running. Attempting to restart..." 34 | Restart-Service -Name sshd -Force 35 | Write-Host "OpenSSH Server has been restarted successfully." 36 | } catch { 37 | Write-Host "Failed to restart OpenSSH Server: $_" 38 | } 39 | } 40 | if ($SSHAgentService.Status -eq 'Running') { 41 | Write-Host "ssh-agent is running." 42 | } else { 43 | try { 44 | Write-Host "ssh-agent is not running. Attempting to restart..." 45 | Restart-Service -Name sshd -Force 46 | Write-Host "ssh-agent has been restarted successfully." 47 | } catch { 48 | Write-Host "Failed to restart ssh-agent : $_" 49 | } 50 | } 51 | 52 | #Adding Firewall rule for port 22 53 | Write-Host "Setting up firewall rules" 54 | $firewallRule = (Get-NetFirewallRule -Name 'sshd').Enabled 55 | if ($firewallRule) { 56 | Write-Host "Firewall rule for OpenSSH Server (sshd) already exists." 57 | } else { 58 | New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 59 | Write-Host "Firewall rule for OpenSSH Server created and enabled." 60 | } 61 | 62 | # Check for the authorized_keys file 63 | $sshFolderPath = "$env:HOMEDRIVE\$env:HOMEPATH\.ssh" 64 | $authorizedKeysPath = "$sshFolderPath\authorized_keys" 65 | 66 | if (-not (Test-Path -Path $sshFolderPath)) { 67 | Write-Host "Creating ssh directory..." 68 | New-Item -Path $sshFolderPath -ItemType Directory -Force 69 | } 70 | 71 | if (-not (Test-Path -Path $authorizedKeysPath)) { 72 | Write-Host "Creating authorized_keys file..." 73 | New-Item -Path $authorizedKeysPath -ItemType File -Force 74 | Write-Host "authorized_keys file created at $authorizedKeysPath." 75 | } else { 76 | Write-Host "authorized_keys file already exists at $authorizedKeysPath." 77 | } 78 | Write-Host "OpenSSH server was successfully enabled." 79 | Write-Host "The config file can be located at C:\ProgramData\ssh\sshd_config " 80 | Write-Host "Add your public keys to this file -> $authorizedKeysPath" 81 | } 82 | -------------------------------------------------------------------------------- /functions/private/Invoke-WinUtilScript.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WinUtilScript { 2 | <# 3 | 4 | .SYNOPSIS 5 | Invokes the provided scriptblock. Intended for things that can't be handled with the other functions. 6 | 7 | .PARAMETER Name 8 | The name of the scriptblock being invoked 9 | 10 | .PARAMETER scriptblock 11 | The scriptblock to be invoked 12 | 13 | .EXAMPLE 14 | $Scriptblock = [scriptblock]::Create({"Write-output 'Hello World'"}) 15 | Invoke-WinUtilScript -ScriptBlock $scriptblock -Name "Hello World" 16 | 17 | #> 18 | param ( 19 | $Name, 20 | [scriptblock]$scriptblock 21 | ) 22 | 23 | try { 24 | Write-Host "Running Script for $name" 25 | Invoke-Command $scriptblock -ErrorAction Stop 26 | } catch [System.Management.Automation.CommandNotFoundException] { 27 | Write-Warning "The specified command was not found." 28 | Write-Warning $PSItem.Exception.message 29 | } catch [System.Management.Automation.RuntimeException] { 30 | Write-Warning "A runtime exception occurred." 31 | Write-Warning $PSItem.Exception.message 32 | } catch [System.Security.SecurityException] { 33 | Write-Warning "A security exception occurred." 34 | Write-Warning $PSItem.Exception.message 35 | } catch [System.UnauthorizedAccessException] { 36 | Write-Warning "Access denied. You do not have permission to perform this operation." 37 | Write-Warning $PSItem.Exception.message 38 | } catch { 39 | # Generic catch block to handle any other type of exception 40 | Write-Warning "Unable to run script for $name due to unhandled exception" 41 | Write-Warning $psitem.Exception.StackTrace 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /functions/private/Invoke-WinUtilSponsors.ps1: -------------------------------------------------------------------------------- 1 | Function Invoke-WinUtilSponsors { 2 | <# 3 | .SYNOPSIS 4 | Lists Sponsors from ChrisTitusTech 5 | .DESCRIPTION 6 | Lists Sponsors from ChrisTitusTech 7 | .EXAMPLE 8 | Invoke-WinUtilSponsors 9 | .NOTES 10 | This function is used to list sponsors from ChrisTitusTech 11 | #> 12 | try { 13 | # Define the URL and headers 14 | $url = "https://github.com/sponsors/ChrisTitusTech" 15 | $headers = @{ 16 | "User-Agent" = "Chrome/58.0.3029.110" 17 | } 18 | 19 | # Fetch the webpage content 20 | try { 21 | $html = Invoke-RestMethod -Uri $url -Headers $headers 22 | } catch { 23 | Write-Output $_.Exception.Message 24 | exit 25 | } 26 | 27 | # Use regex to extract the content between "Current sponsors" and "Past sponsors" 28 | $currentSponsorsPattern = '(?s)(?<=Current sponsors).*?(?=Past sponsors)' 29 | $currentSponsorsHtml = [regex]::Match($html, $currentSponsorsPattern).Value 30 | 31 | # Use regex to extract the sponsor usernames from the alt attributes in the "Current Sponsors" section 32 | $sponsorPattern = '(?<=alt="@)[^"]+' 33 | $sponsors = [regex]::Matches($currentSponsorsHtml, $sponsorPattern) | ForEach-Object { $_.Value } 34 | 35 | # Exclude "ChrisTitusTech" from the sponsors 36 | $sponsors = $sponsors | Where-Object { $_ -ne "ChrisTitusTech" } 37 | 38 | # Return the sponsors 39 | return $sponsors 40 | } catch { 41 | Write-Error "An error occurred while fetching or processing the sponsors: $_" 42 | return $null 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /functions/private/Invoke-WinUtilTweaks.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WinUtilTweaks { 2 | <# 3 | 4 | .SYNOPSIS 5 | Invokes the function associated with each provided checkbox 6 | 7 | .PARAMETER CheckBox 8 | The checkbox to invoke 9 | 10 | .PARAMETER undo 11 | Indicates whether to undo the operation contained in the checkbox 12 | 13 | .PARAMETER KeepServiceStartup 14 | Indicates whether to override the startup of a service with the one given from WinUtil, 15 | or to keep the startup of said service, if it was changed by the user, or another program, from its default value. 16 | #> 17 | 18 | param( 19 | $CheckBox, 20 | $undo = $false, 21 | $KeepServiceStartup = $true 22 | ) 23 | 24 | if ($Checkbox -contains "Toggle") { 25 | $CheckBox = $sync.configs.tweaks.$CheckBox 26 | } 27 | 28 | Write-Debug "Tweaks: $($CheckBox)" 29 | if($undo) { 30 | $Values = @{ 31 | Registry = "OriginalValue" 32 | ScheduledTask = "OriginalState" 33 | Service = "OriginalType" 34 | ScriptType = "UndoScript" 35 | } 36 | 37 | } else { 38 | $Values = @{ 39 | Registry = "Value" 40 | ScheduledTask = "State" 41 | Service = "StartupType" 42 | OriginalService = "OriginalType" 43 | ScriptType = "InvokeScript" 44 | } 45 | } 46 | if($sync.configs.tweaks.$CheckBox.ScheduledTask) { 47 | $sync.configs.tweaks.$CheckBox.ScheduledTask | ForEach-Object { 48 | Write-Debug "$($psitem.Name) and state is $($psitem.$($values.ScheduledTask))" 49 | Set-WinUtilScheduledTask -Name $psitem.Name -State $psitem.$($values.ScheduledTask) 50 | } 51 | } 52 | if($sync.configs.tweaks.$CheckBox.service) { 53 | Write-Debug "KeepServiceStartup is $KeepServiceStartup" 54 | $sync.configs.tweaks.$CheckBox.service | ForEach-Object { 55 | $changeservice = $true 56 | 57 | # The check for !($undo) is required, without it the script will throw an error for accessing unavailable memeber, which's the 'OriginalService' Property 58 | if($KeepServiceStartup -AND !($undo)) { 59 | try { 60 | # Check if the service exists 61 | $service = Get-Service -Name $psitem.Name -ErrorAction Stop 62 | if(!($service.StartType.ToString() -eq $psitem.$($values.OriginalService))) { 63 | Write-Debug "Service $($service.Name) was changed in the past to $($service.StartType.ToString()) from it's original type of $($psitem.$($values.OriginalService)), will not change it to $($psitem.$($values.service))" 64 | $changeservice = $false 65 | } 66 | } catch [System.ServiceProcess.ServiceNotFoundException] { 67 | Write-Warning "Service $($psitem.Name) was not found" 68 | } 69 | } 70 | 71 | if($changeservice) { 72 | Write-Debug "$($psitem.Name) and state is $($psitem.$($values.service))" 73 | Set-WinUtilService -Name $psitem.Name -StartupType $psitem.$($values.Service) 74 | } 75 | } 76 | } 77 | if($sync.configs.tweaks.$CheckBox.registry) { 78 | $sync.configs.tweaks.$CheckBox.registry | ForEach-Object { 79 | Write-Debug "$($psitem.Name) and state is $($psitem.$($values.registry))" 80 | if (($psitem.Path -imatch "hku") -and !(Get-PSDrive -Name HKU -ErrorAction SilentlyContinue)) { 81 | $null = (New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS) 82 | if (Get-PSDrive -Name HKU -ErrorAction SilentlyContinue) { 83 | Write-Debug "HKU drive created successfully" 84 | } else { 85 | Write-Debug "Failed to create HKU drive" 86 | } 87 | } 88 | Set-WinUtilRegistry -Name $psitem.Name -Path $psitem.Path -Type $psitem.Type -Value $psitem.$($values.registry) 89 | } 90 | } 91 | if($sync.configs.tweaks.$CheckBox.$($values.ScriptType)) { 92 | $sync.configs.tweaks.$CheckBox.$($values.ScriptType) | ForEach-Object { 93 | Write-Debug "$($psitem) and state is $($psitem.$($values.ScriptType))" 94 | $Scriptblock = [scriptblock]::Create($psitem) 95 | Invoke-WinUtilScript -ScriptBlock $scriptblock -Name $CheckBox 96 | } 97 | } 98 | 99 | if(!$undo) { 100 | if($sync.configs.tweaks.$CheckBox.appx) { 101 | $sync.configs.tweaks.$CheckBox.appx | ForEach-Object { 102 | Write-Debug "UNDO $($psitem.Name)" 103 | Remove-WinUtilAPPX -Name $psitem 104 | } 105 | } 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /functions/private/Remove-WinUtilAPPX.ps1: -------------------------------------------------------------------------------- 1 | function Remove-WinUtilAPPX { 2 | <# 3 | 4 | .SYNOPSIS 5 | Removes all APPX packages that match the given name 6 | 7 | .PARAMETER Name 8 | The name of the APPX package to remove 9 | 10 | .EXAMPLE 11 | Remove-WinUtilAPPX -Name "Microsoft.Microsoft3DViewer" 12 | 13 | #> 14 | param ( 15 | $Name 16 | ) 17 | 18 | try { 19 | Write-Host "Removing $Name" 20 | Get-AppxPackage "*$Name*" | Remove-AppxPackage -ErrorAction SilentlyContinue 21 | Get-AppxProvisionedPackage -Online | Where-Object DisplayName -like "*$Name*" | Remove-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue 22 | } catch [System.Exception] { 23 | if ($psitem.Exception.Message -like "*The requested operation requires elevation*") { 24 | Write-Warning "Unable to uninstall $name due to a Security Exception" 25 | } else { 26 | Write-Warning "Unable to uninstall $name due to unhandled exception" 27 | Write-Warning $psitem.Exception.StackTrace 28 | } 29 | } catch { 30 | Write-Warning "Unable to uninstall $name due to unhandled exception" 31 | Write-Warning $psitem.Exception.StackTrace 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /functions/private/Set-PackageManagerPreference.ps1: -------------------------------------------------------------------------------- 1 | function Set-PackageManagerPreference { 2 | <# 3 | .SYNOPSIS 4 | Sets the currently selected package manager to global "ManagerPreference" in sync. 5 | Also persists preference across Winutil restarts via preference.ini. 6 | 7 | Reads from preference.ini if no argument sent. 8 | 9 | .PARAMETER preferedPackageManager 10 | The PackageManager that was selected. 11 | #> 12 | param( 13 | [Parameter(Position=0, Mandatory=$false)] 14 | [PackageManagers]$preferedPackageManager 15 | ) 16 | 17 | $preferencePath = "$env:LOCALAPPDATA\winutil\preferences.ini" 18 | $oldChocoPath = "$env:LOCALAPPDATA\winutil\preferChocolatey.ini" 19 | 20 | #Try loading from file if no argument given. 21 | if ($null -eq $preferedPackageManager) { 22 | # Backwards compat for preferChocolatey.ini 23 | if (Test-Path -Path $oldChocoPath) { 24 | $preferedPackageManager = [PackageManagers]::Choco 25 | Remove-Item -Path $oldChocoPath 26 | } 27 | elseif (Test-Path -Path $preferencePath) { 28 | $potential = Get-Content -Path $preferencePath -TotalCount 1 29 | $preferedPackageManager = [PackageManagers]$potential 30 | } 31 | else { 32 | Write-Debug "Creating new preference file, defaulting to winget." 33 | $preferedPackageManager = [PackageManagers]::Winget 34 | } 35 | } 36 | 37 | $sync["ManagerPreference"] = [PackageManagers]::$preferedPackageManager 38 | Write-Debug "Manager Preference changed to '$($sync["ManagerPreference"])'" 39 | 40 | 41 | # Write preference to file to persist across restarts. 42 | Out-File -FilePath $preferencePath -InputObject $sync["ManagerPreference"] 43 | } 44 | -------------------------------------------------------------------------------- /functions/private/Set-WinUtilDNS.ps1: -------------------------------------------------------------------------------- 1 | function Set-WinUtilDNS { 2 | <# 3 | 4 | .SYNOPSIS 5 | Sets the DNS of all interfaces that are in the "Up" state. It will lookup the values from the DNS.Json file 6 | 7 | .PARAMETER DNSProvider 8 | The DNS provider to set the DNS server to 9 | 10 | .EXAMPLE 11 | Set-WinUtilDNS -DNSProvider "google" 12 | 13 | #> 14 | param($DNSProvider) 15 | if($DNSProvider -eq "Default") {return} 16 | try { 17 | $Adapters = Get-NetAdapter | Where-Object {$_.Status -eq "Up"} 18 | Write-Host "Ensuring DNS is set to $DNSProvider on the following interfaces" 19 | Write-Host $($Adapters | Out-String) 20 | 21 | Foreach ($Adapter in $Adapters) { 22 | if($DNSProvider -eq "DHCP") { 23 | Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ResetServerAddresses 24 | } else { 25 | Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ServerAddresses ("$($sync.configs.dns.$DNSProvider.Primary)", "$($sync.configs.dns.$DNSProvider.Secondary)") 26 | Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ServerAddresses ("$($sync.configs.dns.$DNSProvider.Primary6)", "$($sync.configs.dns.$DNSProvider.Secondary6)") 27 | } 28 | } 29 | } catch { 30 | Write-Warning "Unable to set DNS Provider due to an unhandled exception" 31 | Write-Warning $psitem.Exception.StackTrace 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /functions/private/Set-WinUtilProgressbar.ps1: -------------------------------------------------------------------------------- 1 | function Set-WinUtilProgressbar{ 2 | <# 3 | .SYNOPSIS 4 | This function is used to Update the Progress Bar displayed in the winutil GUI. 5 | It will be automatically hidden if the user clicks something and no process is running 6 | .PARAMETER Label 7 | The Text to be overlayed onto the Progress Bar 8 | .PARAMETER PERCENT 9 | The percentage of the Progress Bar that should be filled (0-100) 10 | #> 11 | param( 12 | [string]$Label, 13 | [ValidateRange(0,100)] 14 | [int]$Percent 15 | ) 16 | 17 | $sync.form.Dispatcher.Invoke([action]{$sync.progressBarTextBlock.Text = $label}) 18 | $sync.form.Dispatcher.Invoke([action]{$sync.progressBarTextBlock.ToolTip = $label}) 19 | if ($percent -lt 5 ) { 20 | $percent = 5 # Ensure the progress bar is not empty, as it looks weird 21 | } 22 | $sync.form.Dispatcher.Invoke([action]{ $sync.ProgressBar.Value = $percent}) 23 | 24 | } 25 | -------------------------------------------------------------------------------- /functions/private/Set-WinUtilRegistry.ps1: -------------------------------------------------------------------------------- 1 | function Set-WinUtilRegistry { 2 | <# 3 | 4 | .SYNOPSIS 5 | Modifies the registry based on the given inputs 6 | 7 | .PARAMETER Name 8 | The name of the key to modify 9 | 10 | .PARAMETER Path 11 | The path to the key 12 | 13 | .PARAMETER Type 14 | The type of value to set the key to 15 | 16 | .PARAMETER Value 17 | The value to set the key to 18 | 19 | .EXAMPLE 20 | Set-WinUtilRegistry -Name "PublishUserActivities" -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\System" -Type "DWord" -Value "0" 21 | 22 | #> 23 | param ( 24 | $Name, 25 | $Path, 26 | $Type, 27 | $Value 28 | ) 29 | 30 | try { 31 | if(!(Test-Path 'HKU:\')) {New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS} 32 | 33 | If (!(Test-Path $Path)) { 34 | Write-Host "$Path was not found, Creating..." 35 | New-Item -Path $Path -Force -ErrorAction Stop | Out-Null 36 | } 37 | 38 | if ($Value -ne "<RemoveEntry>") { 39 | Write-Host "Set $Path\$Name to $Value" 40 | Set-ItemProperty -Path $Path -Name $Name -Type $Type -Value $Value -Force -ErrorAction Stop | Out-Null 41 | } 42 | else{ 43 | Write-Host "Remove $Path\$Name" 44 | Remove-ItemProperty -Path $Path -Name $Name -Force -ErrorAction Stop | Out-Null 45 | } 46 | } catch [System.Security.SecurityException] { 47 | Write-Warning "Unable to set $Path\$Name to $Value due to a Security Exception" 48 | } catch [System.Management.Automation.ItemNotFoundException] { 49 | Write-Warning $psitem.Exception.ErrorRecord 50 | } catch [System.UnauthorizedAccessException] { 51 | Write-Warning $psitem.Exception.Message 52 | } catch { 53 | Write-Warning "Unable to set $Name due to unhandled exception" 54 | Write-Warning $psitem.Exception.StackTrace 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /functions/private/Set-WinUtilScheduledTask.ps1: -------------------------------------------------------------------------------- 1 | function Set-WinUtilScheduledTask { 2 | <# 3 | 4 | .SYNOPSIS 5 | Enables/Disables the provided Scheduled Task 6 | 7 | .PARAMETER Name 8 | The path to the Scheduled Task 9 | 10 | .PARAMETER State 11 | The State to set the Task to 12 | 13 | .EXAMPLE 14 | Set-WinUtilScheduledTask -Name "Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser" -State "Disabled" 15 | 16 | #> 17 | param ( 18 | $Name, 19 | $State 20 | ) 21 | 22 | try { 23 | if($State -eq "Disabled") { 24 | Write-Host "Disabling Scheduled Task $Name" 25 | Disable-ScheduledTask -TaskName $Name -ErrorAction Stop 26 | } 27 | if($State -eq "Enabled") { 28 | Write-Host "Enabling Scheduled Task $Name" 29 | Enable-ScheduledTask -TaskName $Name -ErrorAction Stop 30 | } 31 | } catch [System.Exception] { 32 | if($psitem.Exception.Message -like "*The system cannot find the file specified*") { 33 | Write-Warning "Scheduled Task $name was not Found" 34 | } else { 35 | Write-Warning "Unable to set $Name due to unhandled exception" 36 | Write-Warning $psitem.Exception.Message 37 | } 38 | } catch { 39 | Write-Warning "Unable to run script for $name due to unhandled exception" 40 | Write-Warning $psitem.Exception.StackTrace 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /functions/private/Set-WinUtilService.ps1: -------------------------------------------------------------------------------- 1 | Function Set-WinUtilService { 2 | <# 3 | 4 | .SYNOPSIS 5 | Changes the startup type of the given service 6 | 7 | .PARAMETER Name 8 | The name of the service to modify 9 | 10 | .PARAMETER StartupType 11 | The startup type to set the service to 12 | 13 | .EXAMPLE 14 | Set-WinUtilService -Name "HomeGroupListener" -StartupType "Manual" 15 | 16 | #> 17 | param ( 18 | $Name, 19 | $StartupType 20 | ) 21 | try { 22 | Write-Host "Setting Service $Name to $StartupType" 23 | 24 | # Check if the service exists 25 | $service = Get-Service -Name $Name -ErrorAction Stop 26 | 27 | # Service exists, proceed with changing properties 28 | $service | Set-Service -StartupType $StartupType -ErrorAction Stop 29 | } catch [System.ServiceProcess.ServiceNotFoundException] { 30 | Write-Warning "Service $Name was not found" 31 | } catch { 32 | Write-Warning "Unable to set $Name due to unhandled exception" 33 | Write-Warning $_.Exception.Message 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /functions/private/Set-WinUtilTaskbarItem.ps1: -------------------------------------------------------------------------------- 1 | function Set-WinUtilTaskbaritem { 2 | <# 3 | 4 | .SYNOPSIS 5 | Modifies the Taskbaritem of the WPF Form 6 | 7 | .PARAMETER value 8 | Value can be between 0 and 1, 0 being no progress done yet and 1 being fully completed 9 | Value does not affect item without setting the state to 'Normal', 'Error' or 'Paused' 10 | Set-WinUtilTaskbaritem -value 0.5 11 | 12 | .PARAMETER state 13 | State can be 'None' > No progress, 'Indeterminate' > inf. loading gray, 'Normal' > Gray, 'Error' > Red, 'Paused' > Yellow 14 | no value needed: 15 | - Set-WinUtilTaskbaritem -state "None" 16 | - Set-WinUtilTaskbaritem -state "Indeterminate" 17 | value needed: 18 | - Set-WinUtilTaskbaritem -state "Error" 19 | - Set-WinUtilTaskbaritem -state "Normal" 20 | - Set-WinUtilTaskbaritem -state "Paused" 21 | 22 | .PARAMETER overlay 23 | Overlay icon to display on the taskbar item, there are the presets 'None', 'logo' and 'checkmark' or you can specify a path/link to an image file. 24 | CTT logo preset: 25 | - Set-WinUtilTaskbaritem -overlay "logo" 26 | Checkmark preset: 27 | - Set-WinUtilTaskbaritem -overlay "checkmark" 28 | Warning preset: 29 | - Set-WinUtilTaskbaritem -overlay "warning" 30 | No overlay: 31 | - Set-WinUtilTaskbaritem -overlay "None" 32 | Custom icon (needs to be supported by WPF): 33 | - Set-WinUtilTaskbaritem -overlay "C:\path\to\icon.png" 34 | 35 | .PARAMETER description 36 | Description to display on the taskbar item preview 37 | Set-WinUtilTaskbaritem -description "This is a description" 38 | #> 39 | param ( 40 | [string]$state, 41 | [double]$value, 42 | [string]$overlay, 43 | [string]$description 44 | ) 45 | 46 | if ($value) { 47 | $sync["Form"].taskbarItemInfo.ProgressValue = $value 48 | } 49 | 50 | if ($state) { 51 | switch ($state) { 52 | 'None' { $sync["Form"].taskbarItemInfo.ProgressState = "None" } 53 | 'Indeterminate' { $sync["Form"].taskbarItemInfo.ProgressState = "Indeterminate" } 54 | 'Normal' { $sync["Form"].taskbarItemInfo.ProgressState = "Normal" } 55 | 'Error' { $sync["Form"].taskbarItemInfo.ProgressState = "Error" } 56 | 'Paused' { $sync["Form"].taskbarItemInfo.ProgressState = "Paused" } 57 | default { throw "[Set-WinUtilTaskbarItem] Invalid state" } 58 | } 59 | } 60 | 61 | if ($overlay) { 62 | switch ($overlay) { 63 | 'logo' { 64 | $sync["Form"].taskbarItemInfo.Overlay = $sync["logorender"] 65 | } 66 | 'checkmark' { 67 | $sync["Form"].taskbarItemInfo.Overlay = $sync["checkmarkrender"] 68 | } 69 | 'warning' { 70 | $sync["Form"].taskbarItemInfo.Overlay = $sync["warningrender"] 71 | } 72 | 'None' { 73 | $sync["Form"].taskbarItemInfo.Overlay = $null 74 | } 75 | default { 76 | if (Test-Path $overlay) { 77 | $sync["Form"].taskbarItemInfo.Overlay = $overlay 78 | } 79 | } 80 | } 81 | } 82 | 83 | if ($description) { 84 | $sync["Form"].taskbarItemInfo.Description = $description 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /functions/private/Show-WPFInstallAppBusy.ps1: -------------------------------------------------------------------------------- 1 | function Show-WPFInstallAppBusy { 2 | <# 3 | .SYNOPSIS 4 | Displays a busy overlay in the install app area of the WPF form. 5 | This is used to indicate that an install or uninstall is in progress. 6 | Dynamically updates the size of the overlay based on the app area on each invocation. 7 | .PARAMETER text 8 | The text to display in the busy overlay. Defaults to "Installing apps...". 9 | #> 10 | param ( 11 | $text = "Installing apps..." 12 | ) 13 | $sync.form.Dispatcher.Invoke([action]{ 14 | $sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Visible 15 | $sync.InstallAppAreaOverlay.Width = $($sync.InstallAppAreaScrollViewer.ActualWidth * 0.4) 16 | $sync.InstallAppAreaOverlay.Height = $($sync.InstallAppAreaScrollViewer.ActualWidth * 0.4) 17 | $sync.InstallAppAreaOverlayText.Text = $text 18 | $sync.InstallAppAreaBorder.IsEnabled = $false 19 | $sync.InstallAppAreaScrollViewer.Effect.Radius = 5 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /functions/private/Update-WinUtilProgramWinget.ps1: -------------------------------------------------------------------------------- 1 | Function Update-WinUtilProgramWinget { 2 | 3 | <# 4 | 5 | .SYNOPSIS 6 | This will update all programs using Winget 7 | 8 | #> 9 | 10 | [ScriptBlock]$wingetinstall = { 11 | 12 | $host.ui.RawUI.WindowTitle = """Winget Install""" 13 | 14 | Start-Transcript "$logdir\winget-update_$dateTime.log" -Append 15 | winget upgrade --all --accept-source-agreements --accept-package-agreements --scope=machine --silent 16 | 17 | } 18 | 19 | $global:WinGetInstall = Start-Process -Verb runas powershell -ArgumentList "-command invoke-command -scriptblock {$wingetinstall} -argumentlist '$($ProgramsToInstall -join ",")'" -PassThru 20 | 21 | } 22 | -------------------------------------------------------------------------------- /functions/public/Invoke-ScratchDialog.ps1: -------------------------------------------------------------------------------- 1 | 2 | function Invoke-ScratchDialog { 3 | 4 | <# 5 | 6 | .SYNOPSIS 7 | Enable Editable Text box Alternate Scartch path 8 | 9 | .PARAMETER Button 10 | #> 11 | $sync.WPFMicrowinISOScratchDir.IsChecked 12 | 13 | 14 | [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null 15 | $Dialog = New-Object System.Windows.Forms.FolderBrowserDialog 16 | $Dialog.SelectedPath = $sync.MicrowinScratchDirBox.Text 17 | $Dialog.ShowDialog() 18 | $filePath = $Dialog.SelectedPath 19 | Write-Host "No ISO is chosen+ $filePath" 20 | 21 | if ([string]::IsNullOrEmpty($filePath)) { 22 | Write-Host "No Folder had chosen" 23 | return 24 | } 25 | 26 | $sync.MicrowinScratchDirBox.Text = Join-Path $filePath "\" 27 | 28 | } 29 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFButton.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFButton { 2 | 3 | <# 4 | 5 | .SYNOPSIS 6 | Invokes the function associated with the clicked button 7 | 8 | .PARAMETER Button 9 | The name of the button that was clicked 10 | 11 | #> 12 | 13 | Param ([string]$Button) 14 | 15 | # Use this to get the name of the button 16 | #[System.Windows.MessageBox]::Show("$Button","Chris Titus Tech's Windows Utility","OK","Info") 17 | if (-not $sync.ProcessRunning) { 18 | Set-WinUtilProgressBar -label "" -percent 0 19 | } 20 | 21 | Switch -Wildcard ($Button) { 22 | "WPFTab?BT" {Invoke-WPFTab $Button} 23 | "WPFInstall" {Invoke-WPFInstall} 24 | "WPFUninstall" {Invoke-WPFUnInstall} 25 | "WPFInstallUpgrade" {Invoke-WPFInstallUpgrade} 26 | "WPFStandard" {Invoke-WPFPresets "Standard" -checkboxfilterpattern "WPFTweak*"} 27 | "WPFMinimal" {Invoke-WPFPresets "Minimal" -checkboxfilterpattern "WPFTweak*"} 28 | "WPFClearTweaksSelection" {Invoke-WPFPresets -imported $true -checkboxfilterpattern "WPFTweak*"} 29 | "WPFClearInstallSelection" {Invoke-WPFPresets -imported $true -checkboxfilterpattern "WPFInstall*"} 30 | "WPFtweaksbutton" {Invoke-WPFtweaksbutton} 31 | "WPFOOSUbutton" {Invoke-WPFOOSU} 32 | "WPFAddUltPerf" {Invoke-WPFUltimatePerformance -State "Enable"} 33 | "WPFRemoveUltPerf" {Invoke-WPFUltimatePerformance -State "Disable"} 34 | "WPFundoall" {Invoke-WPFundoall} 35 | "WPFFeatureInstall" {Invoke-WPFFeatureInstall} 36 | "WPFPanelDISM" {Invoke-WPFSystemRepair} 37 | "WPFPanelAutologin" {Invoke-WPFPanelAutologin} 38 | "WPFPanelcomputer" {Invoke-WPFControlPanel -Panel $button} 39 | "WPFPanelcontrol" {Invoke-WPFControlPanel -Panel $button} 40 | "WPFPanelnetwork" {Invoke-WPFControlPanel -Panel $button} 41 | "WPFPanelpower" {Invoke-WPFControlPanel -Panel $button} 42 | "WPFPanelregion" {Invoke-WPFControlPanel -Panel $button} 43 | "WPFPanelsound" {Invoke-WPFControlPanel -Panel $button} 44 | "WPFPanelprinter" {Invoke-WPFControlPanel -Panel $button} 45 | "WPFPanelsystem" {Invoke-WPFControlPanel -Panel $button} 46 | "WPFPaneluser" {Invoke-WPFControlPanel -Panel $button} 47 | "WPFPanelGodMode" {Invoke-WPFControlPanel -Panel $button} 48 | "WPFUpdatesdefault" {Invoke-WPFFixesUpdate} 49 | "WPFFixesUpdate" {Invoke-WPFFixesUpdate} 50 | "WPFFixesWinget" {Invoke-WPFFixesWinget} 51 | "WPFRunAdobeCCCleanerTool" {Invoke-WPFRunAdobeCCCleanerTool} 52 | "WPFFixesNetwork" {Invoke-WPFFixesNetwork} 53 | "WPFUpdatesdisable" {Invoke-WPFUpdatesdisable} 54 | "WPFUpdatessecurity" {Invoke-WPFUpdatessecurity} 55 | "WPFWinUtilShortcut" {Invoke-WPFShortcut -ShortcutToAdd "WinUtil" -RunAsAdmin $true} 56 | "WPFGetInstalled" {Invoke-WPFGetInstalled -CheckBox "winget"} 57 | "WPFGetInstalledTweaks" {Invoke-WPFGetInstalled -CheckBox "tweaks"} 58 | "WPFGetIso" {Invoke-MicrowinGetIso} 59 | "WPFMicrowin" {Invoke-Microwin} 60 | "WPFCloseButton" {Invoke-WPFCloseButton} 61 | "MicrowinScratchDirBT" {Invoke-ScratchDialog} 62 | "WPFWinUtilInstallPSProfile" {Invoke-WinUtilInstallPSProfile} 63 | "WPFWinUtilUninstallPSProfile" {Invoke-WinUtilUninstallPSProfile} 64 | "WPFWinUtilSSHServer" {Invoke-WPFSSHServer} 65 | "WPFselectedAppsButton" {$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen} 66 | "WPFMicrowinPanelBack" {Toggle-MicrowinPanel 1} 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFCloseButton.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFCloseButton { 2 | 3 | <# 4 | 5 | .SYNOPSIS 6 | Close application 7 | 8 | .PARAMETER Button 9 | #> 10 | $sync["Form"].Close() 11 | Write-Host "Bye bye!" 12 | } 13 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFControlPanel.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFControlPanel { 2 | <# 3 | 4 | .SYNOPSIS 5 | Opens the requested legacy panel 6 | 7 | .PARAMETER Panel 8 | The panel to open 9 | 10 | #> 11 | param($Panel) 12 | 13 | switch ($Panel) { 14 | "WPFPanelcontrol" {control} 15 | "WPFPanelcomputer" {compmgmt.msc} 16 | "WPFPanelnetwork" {ncpa.cpl} 17 | "WPFPanelpower" {powercfg.cpl} 18 | "WPFPanelregion" {intl.cpl} 19 | "WPFPanelsound" {mmsys.cpl} 20 | "WPFPanelprinter" {Start-Process "shell:::{A8A91A66-3A7D-4424-8D24-04E180695C7A}"} 21 | "WPFPanelsystem" {sysdm.cpl} 22 | "WPFPaneluser" {control userpasswords2} 23 | "WPFPanelGodMode" {Start-Process "shell:::{ED7BA470-8E54-465E-825C-99712043E01C}"} 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFFeatureInstall.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFFeatureInstall { 2 | <# 3 | 4 | .SYNOPSIS 5 | Installs selected Windows Features 6 | 7 | #> 8 | 9 | if($sync.ProcessRunning) { 10 | $msg = "[Invoke-WPFFeatureInstall] Install process is currently running." 11 | [System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) 12 | return 13 | } 14 | 15 | $Features = (Get-WinUtilCheckBoxes)["WPFFeature"] 16 | 17 | Invoke-WPFRunspace -ArgumentList $Features -DebugPreference $DebugPreference -ScriptBlock { 18 | param($Features, $DebugPreference) 19 | $sync.ProcessRunning = $true 20 | if ($Features.count -eq 1) { 21 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }) 22 | } else { 23 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }) 24 | } 25 | 26 | Invoke-WinUtilFeatureInstall $Features 27 | 28 | $sync.ProcessRunning = $false 29 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }) 30 | 31 | Write-Host "===================================" 32 | Write-Host "--- Features are Installed ---" 33 | Write-Host "--- A Reboot may be required ---" 34 | Write-Host "===================================" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFFixesNetwork.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFFixesNetwork { 2 | <# 3 | 4 | .SYNOPSIS 5 | Resets various network configurations 6 | 7 | #> 8 | 9 | Write-Host "Resetting Network with netsh" 10 | 11 | Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" 12 | # Reset WinSock catalog to a clean state 13 | Start-Process -NoNewWindow -FilePath "netsh" -ArgumentList "winsock", "reset" 14 | 15 | Set-WinUtilTaskbaritem -state "Normal" -value 0.35 -overlay "logo" 16 | # Resets WinHTTP proxy setting to DIRECT 17 | Start-Process -NoNewWindow -FilePath "netsh" -ArgumentList "winhttp", "reset", "proxy" 18 | 19 | Set-WinUtilTaskbaritem -state "Normal" -value 0.7 -overlay "logo" 20 | # Removes all user configured IP settings 21 | Start-Process -NoNewWindow -FilePath "netsh" -ArgumentList "int", "ip", "reset" 22 | 23 | Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" 24 | 25 | Write-Host "Process complete. Please reboot your computer." 26 | 27 | $ButtonType = [System.Windows.MessageBoxButton]::OK 28 | $MessageboxTitle = "Network Reset " 29 | $Messageboxbody = ("Stock settings loaded.`n Please reboot your computer") 30 | $MessageIcon = [System.Windows.MessageBoxImage]::Information 31 | 32 | [System.Windows.MessageBox]::Show($Messageboxbody, $MessageboxTitle, $ButtonType, $MessageIcon) 33 | Write-Host "==========================================" 34 | Write-Host "-- Network Configuration has been Reset --" 35 | Write-Host "==========================================" 36 | } 37 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFFixesWinget.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFFixesWinget { 2 | 3 | <# 4 | 5 | .SYNOPSIS 6 | Fixes Winget by running choco install winget 7 | .DESCRIPTION 8 | BravoNorris for the fantastic idea of a button to reinstall winget 9 | #> 10 | # Install Choco if not already present 11 | try { 12 | Set-WinUtilTaskbaritem -state "Indeterminate" -overlay "logo" 13 | Install-WinUtilChoco 14 | Start-Process -FilePath "choco" -ArgumentList "install winget -y --force" -NoNewWindow -Wait 15 | } catch { 16 | Write-Error "Failed to install winget: $_" 17 | Set-WinUtilTaskbaritem -state "Error" -overlay "warning" 18 | } finally { 19 | Write-Host "==> Finished Winget Repair" 20 | Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFGetInstalled.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFGetInstalled { 2 | <# 3 | TODO: Add the Option to use Chocolatey as Engine 4 | .SYNOPSIS 5 | Invokes the function that gets the checkboxes to check in a new runspace 6 | 7 | .PARAMETER checkbox 8 | Indicates whether to check for installed 'winget' programs or applied 'tweaks' 9 | 10 | #> 11 | param($checkbox) 12 | if ($sync.ProcessRunning) { 13 | $msg = "[Invoke-WPFGetInstalled] Install process is currently running." 14 | [System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) 15 | return 16 | } 17 | 18 | if (($sync.ChocoRadioButton.IsChecked -eq $false) -and ((Test-WinUtilPackageManager -winget) -eq "not-installed") -and $checkbox -eq "winget") { 19 | return 20 | } 21 | $managerPreference = $sync["ManagerPreference"] 22 | 23 | Invoke-WPFRunspace -ParameterList @(("managerPreference", $managerPreference),("checkbox", $checkbox)) -DebugPreference $DebugPreference -ScriptBlock { 24 | param ( 25 | [string]$checkbox, 26 | [PackageManagers]$managerPreference 27 | ) 28 | $sync.ProcessRunning = $true 29 | $sync.form.Dispatcher.Invoke([action] { Set-WinUtilTaskbaritem -state "Indeterminate" }) 30 | 31 | if ($checkbox -eq "winget") { 32 | Write-Host "Getting Installed Programs..." 33 | switch ($managerPreference) { 34 | "Choco"{$Checkboxes = Invoke-WinUtilCurrentSystem -CheckBox "choco"; break} 35 | "Winget"{$Checkboxes = Invoke-WinUtilCurrentSystem -CheckBox $checkbox; break} 36 | } 37 | } 38 | elseif ($checkbox -eq "tweaks") { 39 | Write-Host "Getting Installed Tweaks..." 40 | $Checkboxes = Invoke-WinUtilCurrentSystem -CheckBox $checkbox 41 | } 42 | 43 | $sync.form.Dispatcher.invoke({ 44 | foreach ($checkbox in $Checkboxes) { 45 | $sync.$checkbox.ischecked = $True 46 | } 47 | }) 48 | 49 | Write-Host "Done..." 50 | $sync.ProcessRunning = $false 51 | $sync.form.Dispatcher.Invoke([action] { Set-WinUtilTaskbaritem -state "None" }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFImpex.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFImpex { 2 | <# 3 | 4 | .SYNOPSIS 5 | Handles importing and exporting of the checkboxes checked for the tweaks section 6 | 7 | .PARAMETER type 8 | Indicates whether to 'import' or 'export' 9 | 10 | .PARAMETER checkbox 11 | The checkbox to export to a file or apply the imported file to 12 | 13 | .EXAMPLE 14 | Invoke-WPFImpex -type "export" 15 | 16 | #> 17 | param( 18 | $type, 19 | $Config = $null 20 | ) 21 | 22 | function ConfigDialog { 23 | if (!$Config) { 24 | switch ($type) { 25 | "export" { $FileBrowser = New-Object System.Windows.Forms.SaveFileDialog } 26 | "import" { $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog } 27 | } 28 | $FileBrowser.InitialDirectory = [Environment]::GetFolderPath('Desktop') 29 | $FileBrowser.Filter = "JSON Files (*.json)|*.json" 30 | $FileBrowser.ShowDialog() | Out-Null 31 | 32 | if ($FileBrowser.FileName -eq "") { 33 | return $null 34 | } else { 35 | return $FileBrowser.FileName 36 | } 37 | } else { 38 | return $Config 39 | } 40 | } 41 | 42 | switch ($type) { 43 | "export" { 44 | try { 45 | $Config = ConfigDialog 46 | if ($Config) { 47 | $jsonFile = Get-WinUtilCheckBoxes -unCheck $false | ConvertTo-Json 48 | $jsonFile | Out-File $Config -Force 49 | "iex ""& { `$(irm https://christitus.com/win) } -Config '$Config'""" | Set-Clipboard 50 | } 51 | } catch { 52 | Write-Error "An error occurred while exporting: $_" 53 | } 54 | } 55 | "import" { 56 | try { 57 | $Config = ConfigDialog 58 | if ($Config) { 59 | try { 60 | if ($Config -match '^https?://') { 61 | $jsonFile = (Invoke-WebRequest "$Config").Content | ConvertFrom-Json 62 | } else { 63 | $jsonFile = Get-Content $Config | ConvertFrom-Json 64 | } 65 | } catch { 66 | Write-Error "Failed to load the JSON file from the specified path or URL: $_" 67 | return 68 | } 69 | $flattenedJson = $jsonFile.PSObject.Properties.Where({ $_.Name -ne "Install" }).ForEach({ $_.Value }) 70 | Invoke-WPFPresets -preset $flattenedJson -imported $true 71 | } 72 | } catch { 73 | Write-Error "An error occurred while importing: $_" 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFInstall.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFInstall { 2 | param ( 3 | [Parameter(Mandatory=$false)] 4 | [PSObject[]]$PackagesToInstall = $($sync.selectedApps | Foreach-Object { $sync.configs.applicationsHashtable.$_ }) 5 | ) 6 | <# 7 | 8 | .SYNOPSIS 9 | Installs the selected programs using winget, if one or more of the selected programs are already installed on the system, winget will try and perform an upgrade if there's a newer version to install. 10 | 11 | #> 12 | 13 | if($sync.ProcessRunning) { 14 | $msg = "[Invoke-WPFInstall] An Install process is currently running." 15 | [System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) 16 | return 17 | } 18 | 19 | if ($PackagesToInstall.Count -eq 0) { 20 | $WarningMsg = "Please select the program(s) to install or upgrade" 21 | [System.Windows.MessageBox]::Show($WarningMsg, $AppTitle, [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) 22 | return 23 | } 24 | 25 | $ManagerPreference = $sync["ManagerPreference"] 26 | 27 | Invoke-WPFRunspace -ParameterList @(("PackagesToInstall", $PackagesToInstall),("ManagerPreference", $ManagerPreference)) -DebugPreference $DebugPreference -ScriptBlock { 28 | param($PackagesToInstall, $ManagerPreference, $DebugPreference) 29 | 30 | $packagesSorted = Get-WinUtilSelectedPackages -PackageList $PackagesToInstall -Preference $ManagerPreference 31 | 32 | $packagesWinget = $packagesSorted[[PackageManagers]::Winget] 33 | $packagesChoco = $packagesSorted[[PackageManagers]::Choco] 34 | 35 | try { 36 | $sync.ProcessRunning = $true 37 | Show-WPFInstallAppBusy -text "Installing apps..." 38 | 39 | if($packagesWinget.Count -gt 0) { 40 | Install-WinUtilWinget 41 | Install-WinUtilProgramWinget -Action Install -Programs $packagesWinget 42 | 43 | } 44 | if($packagesChoco.Count -gt 0) { 45 | Install-WinUtilChoco 46 | Install-WinUtilProgramChoco -Action Install -Programs $packagesChoco 47 | } 48 | Hide-WPFInstallAppBusy 49 | Write-Host "===========================================" 50 | Write-Host "-- Installs have finished ---" 51 | Write-Host "===========================================" 52 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }) 53 | } catch { 54 | Write-Host "===========================================" 55 | Write-Host "Error: $_" 56 | Write-Host "===========================================" 57 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }) 58 | } 59 | $sync.ProcessRunning = $False 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFInstallUpgrade.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFInstallUpgrade { 2 | <# 3 | 4 | .SYNOPSIS 5 | Invokes the function that upgrades all installed programs 6 | 7 | #> 8 | if ($sync.ChocoRadioButton.IsChecked) { 9 | Install-WinUtilChoco 10 | $chocoUpgradeStatus = (Start-Process "choco" -ArgumentList "upgrade all -y" -Wait -PassThru -NoNewWindow).ExitCode 11 | if ($chocoUpgradeStatus -eq 0) { 12 | Write-Host "Upgrade Successful" 13 | } 14 | else{ 15 | Write-Host "Error Occured. Return Code: $chocoUpgradeStatus" 16 | } 17 | } 18 | else{ 19 | if((Test-WinUtilPackageManager -winget) -eq "not-installed") { 20 | return 21 | } 22 | 23 | if(Get-WinUtilInstallerProcess -Process $global:WinGetInstall) { 24 | $msg = "[Invoke-WPFInstallUpgrade] Install process is currently running. Please check for a powershell window labeled 'Winget Install'" 25 | [System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) 26 | return 27 | } 28 | 29 | Update-WinUtilProgramWinget 30 | 31 | Write-Host "===========================================" 32 | Write-Host "-- Updates started ---" 33 | Write-Host "-- You can close this window if desired ---" 34 | Write-Host "===========================================" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFOOSU.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFOOSU { 2 | <# 3 | .SYNOPSIS 4 | Downloads and runs OO Shutup 10 5 | #> 6 | try { 7 | $OOSU_filepath = "$ENV:temp\OOSU10.exe" 8 | $Initial_ProgressPreference = $ProgressPreference 9 | $ProgressPreference = "SilentlyContinue" # Disables the Progress Bar to drasticly speed up Invoke-WebRequest 10 | Invoke-WebRequest -Uri "https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe" -OutFile $OOSU_filepath 11 | Write-Host "Starting OO Shutup 10 ..." 12 | Start-Process $OOSU_filepath 13 | } catch { 14 | Write-Host "Error Downloading and Running OO Shutup 10" -ForegroundColor Red 15 | } 16 | finally { 17 | $ProgressPreference = $Initial_ProgressPreference 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFPanelAutologin.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFPanelAutologin { 2 | <# 3 | 4 | .SYNOPSIS 5 | Enables autologin using Sysinternals Autologon.exe 6 | 7 | #> 8 | 9 | # Official Microsoft recommendation: https://learn.microsoft.com/en-us/sysinternals/downloads/autologon 10 | Invoke-WebRequest -Uri "https://live.sysinternals.com/Autologon.exe" -OutFile "$env:temp\autologin.exe" 11 | cmd /c "$env:temp\autologin.exe" /accepteula 12 | } 13 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFPopup.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFPopup { 2 | param ( 3 | [ValidateSet("Show", "Hide", "Toggle")] 4 | [string]$Action = "", 5 | 6 | [string[]]$Popups = @(), 7 | 8 | [ValidateScript({ 9 | $invalid = $_.GetEnumerator() | Where-Object { $_.Value -notin @("Show", "Hide", "Toggle") } 10 | if ($invalid) { 11 | throw "Found invalid Popup-Action pair(s): " + ($invalid | ForEach-Object { "$($_.Key) = $($_.Value)" } -join "; ") 12 | } 13 | $true 14 | })] 15 | [hashtable]$PopupActionTable = @{} 16 | ) 17 | 18 | if (-not $PopupActionTable.Count -and (-not $Action -or -not $Popups.Count)) { 19 | throw "Provide either 'PopupActionTable' or both 'Action' and 'Popups'." 20 | } 21 | 22 | if ($PopupActionTable.Count -and ($Action -or $Popups.Count)) { 23 | throw "Use 'PopupActionTable' on its own, or 'Action' with 'Popups'." 24 | } 25 | 26 | # Collect popups and actions 27 | $PopupsToProcess = if ($PopupActionTable.Count) { 28 | $PopupActionTable.GetEnumerator() | ForEach-Object { [PSCustomObject]@{ Name = "$($_.Key)Popup"; Action = $_.Value } } 29 | } else { 30 | $Popups | ForEach-Object { [PSCustomObject]@{ Name = "$_`Popup"; Action = $Action } } 31 | } 32 | 33 | $PopupsNotFound = @() 34 | 35 | # Apply actions 36 | foreach ($popupEntry in $PopupsToProcess) { 37 | $popupName = $popupEntry.Name 38 | 39 | if (-not $sync.$popupName) { 40 | $PopupsNotFound += $popupName 41 | continue 42 | } 43 | 44 | $sync.$popupName.IsOpen = switch ($popupEntry.Action) { 45 | "Show" { $true } 46 | "Hide" { $false } 47 | "Toggle" { -not $sync.$popupName.IsOpen } 48 | } 49 | } 50 | 51 | if ($PopupsNotFound.Count -gt 0) { 52 | throw "Could not find the following popups: $($PopupsNotFound -join ', ')" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFPresets.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFPresets { 2 | <# 3 | 4 | .SYNOPSIS 5 | Sets the options in the tweaks panel to the given preset 6 | 7 | .PARAMETER preset 8 | The preset to set the options to 9 | 10 | .PARAMETER imported 11 | If the preset is imported from a file, defaults to false 12 | 13 | .PARAMETER checkboxfilterpattern 14 | The Pattern to use when filtering through CheckBoxes, defaults to "**" 15 | 16 | #> 17 | 18 | param ( 19 | [Parameter(position=0)] 20 | [Array]$preset = "", 21 | 22 | [Parameter(position=1)] 23 | [bool]$imported = $false, 24 | 25 | [Parameter(position=2)] 26 | [string]$checkboxfilterpattern = "**" 27 | ) 28 | 29 | if ($imported -eq $true) { 30 | $CheckBoxesToCheck = $preset 31 | } else { 32 | $CheckBoxesToCheck = $sync.configs.preset.$preset 33 | } 34 | 35 | $CheckBoxes = ($sync.GetEnumerator()).where{ $_.Value -is [System.Windows.Controls.CheckBox] -and $_.Name -notlike "WPFToggle*" -and $_.Name -like "$checkboxfilterpattern"} 36 | Write-Debug "Getting checkboxes to set, number of checkboxes: $($CheckBoxes.Count)" 37 | 38 | if ($CheckBoxesToCheck -ne "") { 39 | $debugMsg = "CheckBoxes to Check are: " 40 | $CheckBoxesToCheck | ForEach-Object { $debugMsg += "$_, " } 41 | $debugMsg = $debugMsg -replace (',\s*#39;, '') 42 | Write-Debug "$debugMsg" 43 | } 44 | 45 | foreach ($CheckBox in $CheckBoxes) { 46 | $checkboxName = $CheckBox.Key 47 | 48 | if (-not $CheckBoxesToCheck) { 49 | $sync.$checkboxName.IsChecked = $false 50 | continue 51 | } 52 | 53 | # Check if the checkbox name exists in the flattened JSON hashtable 54 | if ($CheckBoxesToCheck -contains $checkboxName) { 55 | # If it exists, set IsChecked to true 56 | $sync.$checkboxName.IsChecked = $true 57 | Write-Debug "$checkboxName is checked" 58 | } else { 59 | # If it doesn't exist, set IsChecked to false 60 | $sync.$checkboxName.IsChecked = $false 61 | Write-Debug "$checkboxName is not checked" 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFRunAdobeCCCleanerTool.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFRunAdobeCCCleanerTool { 2 | <# 3 | .SYNOPSIS 4 | It removes or fixes problem files and resolves permission issues in registry keys. 5 | .DESCRIPTION 6 | The Creative Cloud Cleaner tool is a utility for experienced users to clean up corrupted installations. 7 | #> 8 | 9 | [string]$url="https://swupmf.adobe.com/webfeed/CleanerTool/win/AdobeCreativeCloudCleanerTool.exe" 10 | 11 | Write-Host "The Adobe Creative Cloud Cleaner tool is hosted at" 12 | Write-Host "$url" 13 | 14 | try { 15 | # Don't show the progress because it will slow down the download speed 16 | $ProgressPreference='SilentlyContinue' 17 | 18 | Invoke-WebRequest -Uri $url -OutFile "$env:TEMP\AdobeCreativeCloudCleanerTool.exe" -UseBasicParsing -ErrorAction SilentlyContinue -Verbose 19 | 20 | # Revert back the ProgressPreference variable to the default value since we got the file desired 21 | $ProgressPreference='Continue' 22 | 23 | Start-Process -FilePath "$env:TEMP\AdobeCreativeCloudCleanerTool.exe" -Wait -ErrorAction SilentlyContinue -Verbose 24 | } catch { 25 | Write-Error $_.Exception.Message 26 | } finally { 27 | if (Test-Path -Path "$env:TEMP\AdobeCreativeCloudCleanerTool.exe") { 28 | Write-Host "Cleaning up..." 29 | Remove-Item -Path "$env:TEMP\AdobeCreativeCloudCleanerTool.exe" -Verbose 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFRunspace.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFRunspace { 2 | 3 | <# 4 | 5 | .SYNOPSIS 6 | Creates and invokes a runspace using the given scriptblock and argumentlist 7 | 8 | .PARAMETER ScriptBlock 9 | The scriptblock to invoke in the runspace 10 | 11 | .PARAMETER ArgumentList 12 | A list of arguments to pass to the runspace 13 | 14 | .PARAMETER ParameterList 15 | A list of named parameters that should be provided. 16 | .EXAMPLE 17 | Invoke-WPFRunspace ` 18 | -ScriptBlock $sync.ScriptsInstallPrograms ` 19 | -ArgumentList "Installadvancedip,Installbitwarden" ` 20 | 21 | Invoke-WPFRunspace` 22 | -ScriptBlock $sync.ScriptsInstallPrograms ` 23 | -ParameterList @(("PackagesToInstall", @("Installadvancedip,Installbitwarden")),("ChocoPreference", $true)) 24 | #> 25 | 26 | [CmdletBinding()] 27 | Param ( 28 | $ScriptBlock, 29 | $ArgumentList, 30 | $ParameterList, 31 | $DebugPreference 32 | ) 33 | 34 | # Create a PowerShell instance 35 | $script:powershell = [powershell]::Create() 36 | 37 | # Add Scriptblock and Arguments to runspace 38 | $script:powershell.AddScript($ScriptBlock) 39 | $script:powershell.AddArgument($ArgumentList) 40 | 41 | foreach ($parameter in $ParameterList) { 42 | $script:powershell.AddParameter($parameter[0], $parameter[1]) 43 | } 44 | $script:powershell.AddArgument($DebugPreference) # Pass DebugPreference to the script block 45 | $script:powershell.RunspacePool = $sync.runspace 46 | 47 | # Execute the RunspacePool 48 | $script:handle = $script:powershell.BeginInvoke() 49 | 50 | # Clean up the RunspacePool threads when they are complete, and invoke the garbage collector to clean up the memory 51 | if ($script:handle.IsCompleted) { 52 | $script:powershell.EndInvoke($script:handle) 53 | $script:powershell.Dispose() 54 | $sync.runspace.Dispose() 55 | $sync.runspace.Close() 56 | [System.GC]::Collect() 57 | } 58 | # Return the handle 59 | return $handle 60 | } 61 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFSSHServer.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFSSHServer { 2 | <# 3 | 4 | .SYNOPSIS 5 | Invokes the OpenSSH Server install in a runspace 6 | 7 | #> 8 | 9 | Invoke-WPFRunspace -DebugPreference $DebugPreference -ScriptBlock { 10 | 11 | Invoke-WinUtilSSHServer 12 | 13 | Write-Host "=======================================" 14 | Write-Host "-- OpenSSH Server installed! ---" 15 | Write-Host "=======================================" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFSelectedAppsUpdate.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFSelectedAppsUpdate { 2 | <# 3 | .SYNOPSIS 4 | This is a helper function that is called by the Checked and Unchecked events of the Checkboxes on the install tab. 5 | It Updates the "Selected Apps" selectedAppLabel on the Install Tab to represent the current collection 6 | .PARAMETER type 7 | Eigther: Add | Remove 8 | .PARAMETER checkbox 9 | should contain the current instance of the checkbox that triggered the Event. 10 | Most of the time will be the automatic variable $this 11 | .EXAMPLE 12 | $checkbox.Add_Unchecked({Invoke-WPFSelectedAppsUpdate -type "Remove" -checkbox $this}) 13 | OR 14 | Invoke-WPFSelectedAppsUpdate -type "Add" -checkbox $specificCheckbox 15 | #> 16 | param ( 17 | $type, 18 | $checkbox 19 | ) 20 | 21 | $selectedAppsButton = $sync.WPFselectedAppsButton 22 | # Get the actual Name from the selectedAppLabel inside the Checkbox 23 | $appKey = $checkbox.Parent.Tag 24 | if ($type -eq "Add") { 25 | $sync.selectedApps.Add($appKey) 26 | # The List type needs to be specified again, because otherwise Sort-Object will convert the list to a string if there is only a single entry 27 | [System.Collections.Generic.List[pscustomobject]]$sync.selectedApps = $sync.SelectedApps | Sort-Object 28 | 29 | } 30 | elseif ($type -eq "Remove") { 31 | $sync.SelectedApps.Remove($appKey) 32 | } 33 | else{ 34 | Write-Error "Type: $type not implemented" 35 | } 36 | 37 | $count = $sync.SelectedApps.Count 38 | $selectedAppsButton.Content = "Selected Apps: $count" 39 | # On every change, remove all entries inside the Popup Menu. This is done, so we can keep the alphabetical order even if elements are selected in a random way 40 | $sync.selectedAppsstackPanel.Children.Clear() 41 | $sync.SelectedApps | Foreach-Object { Add-SelectedAppsMenuItem -name $($sync.configs.applicationsHashtable.$_.Content) -key $_ } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFTab.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFTab { 2 | 3 | <# 4 | 5 | .SYNOPSIS 6 | Sets the selected tab to the tab that was clicked 7 | 8 | .PARAMETER ClickedTab 9 | The name of the tab that was clicked 10 | 11 | #> 12 | 13 | Param ( 14 | [Parameter(Mandatory,position=0)] 15 | [string]$ClickedTab 16 | ) 17 | 18 | $tabNav = Get-WinUtilVariables | Where-Object {$psitem -like "WPFTabNav"} 19 | $tabNumber = [int]($ClickedTab -replace "WPFTab","" -replace "BT","") - 1 20 | 21 | $filter = Get-WinUtilVariables -Type ToggleButton | Where-Object {$psitem -like "WPFTab?BT"} 22 | ($sync.GetEnumerator()).where{$psitem.Key -in $filter} | ForEach-Object { 23 | if ($ClickedTab -ne $PSItem.name) { 24 | $sync[$PSItem.Name].IsChecked = $false 25 | } else { 26 | $sync["$ClickedTab"].IsChecked = $true 27 | $tabNumber = [int]($ClickedTab-replace "WPFTab","" -replace "BT","") - 1 28 | $sync.$tabNav.Items[$tabNumber].IsSelected = $true 29 | } 30 | } 31 | $sync.currentTab = $sync.$tabNav.Items[$tabNumber].Header 32 | 33 | # Always reset the filter for the current tab 34 | if ($sync.currentTab -eq "Install") { 35 | # Reset Install tab filter 36 | Find-AppsByNameOrDescription -SearchString "" 37 | } elseif ($sync.currentTab -eq "Tweaks") { 38 | # Reset Tweaks tab filter 39 | Find-TweaksByNameOrDescription -SearchString "" 40 | } 41 | 42 | # Show search bar in Install and Tweaks tabs 43 | if ($tabNumber -eq 0 -or $tabNumber -eq 1) { 44 | $sync.SearchBar.Visibility = "Visible" 45 | $searchIcon = ($sync.Form.FindName("SearchBar").Parent.Children | Where-Object { $_ -is [System.Windows.Controls.TextBlock] -and $_.Text -eq [char]0xE721 })[0] 46 | if ($searchIcon) { 47 | $searchIcon.Visibility = "Visible" 48 | } 49 | } else { 50 | $sync.SearchBar.Visibility = "Collapsed" 51 | $searchIcon = ($sync.Form.FindName("SearchBar").Parent.Children | Where-Object { $_ -is [System.Windows.Controls.TextBlock] -and $_.Text -eq [char]0xE721 })[0] 52 | if ($searchIcon) { 53 | $searchIcon.Visibility = "Collapsed" 54 | } 55 | # Hide the clear button if it's visible 56 | $sync.SearchBarClearButton.Visibility = "Collapsed" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFTweakPS7.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFTweakPS7{ 2 | <# 3 | .SYNOPSIS 4 | This will edit the config file of the Windows Terminal Replacing the Powershell 5 to Powershell 7 and install Powershell 7 if necessary 5 | .PARAMETER action 6 | PS7: Configures Powershell 7 to be the default Terminal 7 | PS5: Configures Powershell 5 to be the default Terminal 8 | #> 9 | param ( 10 | [ValidateSet("PS7", "PS5")] 11 | [string]$action 12 | ) 13 | 14 | switch ($action) { 15 | "PS7"{ 16 | if (Test-Path -Path "$env:ProgramFiles\PowerShell\7") { 17 | Write-Host "Powershell 7 is already installed." 18 | } else { 19 | Write-Host "Installing Powershell 7..." 20 | Install-WinUtilProgramWinget -Action Install -Programs @("Microsoft.PowerShell") 21 | } 22 | $targetTerminalName = "PowerShell" 23 | } 24 | "PS5"{ 25 | $targetTerminalName = "Windows PowerShell" 26 | } 27 | } 28 | # Check if the Windows Terminal is installed and return if not (Prerequisite for the following code) 29 | if (-not (Get-Command "wt" -ErrorAction SilentlyContinue)) { 30 | Write-Host "Windows Terminal not installed. Skipping Terminal preference" 31 | return 32 | } 33 | # Check if the Windows Terminal settings.json file exists and return if not (Prereqisite for the following code) 34 | $settingsPath = "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json" 35 | if (-not (Test-Path -Path $settingsPath)) { 36 | Write-Host "Windows Terminal Settings file not found at $settingsPath" 37 | return 38 | } 39 | 40 | Write-Host "Settings file found." 41 | $settingsContent = Get-Content -Path $settingsPath | ConvertFrom-Json 42 | $ps7Profile = $settingsContent.profiles.list | Where-Object { $_.name -eq $targetTerminalName } 43 | if ($ps7Profile) { 44 | $settingsContent.defaultProfile = $ps7Profile.guid 45 | $updatedSettings = $settingsContent | ConvertTo-Json -Depth 100 46 | Set-Content -Path $settingsPath -Value $updatedSettings 47 | Write-Host "Default profile updated to " -NoNewline 48 | Write-Host "$targetTerminalName " -ForegroundColor White -NoNewline 49 | Write-Host "using the name attribute." 50 | } else { 51 | Write-Host "No PowerShell 7 profile found in Windows Terminal settings using the name attribute." 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFUltimatePerformance.ps1: -------------------------------------------------------------------------------- 1 | Function Invoke-WPFUltimatePerformance { 2 | <# 3 | 4 | .SYNOPSIS 5 | Enables or disables the Ultimate Performance power scheme based on its GUID. 6 | 7 | .PARAMETER State 8 | Specifies whether to "Enable" or "Disable" the Ultimate Performance power scheme. 9 | 10 | #> 11 | param( 12 | [Parameter(Mandatory = $true)] 13 | [ValidateSet("Enable", "Disable")] 14 | [string]$State 15 | ) 16 | 17 | try { 18 | # GUID of the Ultimate Performance power plan 19 | $ultimateGUID = "e9a42b02-d5df-448d-aa00-03f14749eb61" 20 | 21 | switch ($State) { 22 | "Enable" { 23 | # Duplicate the Ultimate Performance power plan using its GUID 24 | $duplicateOutput = powercfg /duplicatescheme $ultimateGUID 25 | 26 | $guid = $null 27 | $nameFromFile = "ChrisTitus - Ultimate Power Plan" 28 | $description = "Ultimate Power Plan, added via WinUtils" 29 | 30 | # Extract the new GUID from the duplicateOutput 31 | foreach ($line in $duplicateOutput) { 32 | if ($line -match "\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b") { 33 | $guid = $matches[0] # $matches[0] will contain the first match, which is the GUID 34 | Write-Output "GUID: $guid has been extracted and stored in the variable." 35 | break 36 | } 37 | } 38 | 39 | if (-not $guid) { 40 | Write-Output "No GUID found in the duplicateOutput. Check the output format." 41 | exit 1 42 | } 43 | 44 | # Change the name of the power plan and set its description 45 | $changeNameOutput = powercfg /changename $guid "$nameFromFile" "$description" 46 | Write-Output "The power plan name and description have been changed. Output:" 47 | Write-Output $changeNameOutput 48 | 49 | # Set the duplicated Ultimate Performance plan as active 50 | $setActiveOutput = powercfg /setactive $guid 51 | Write-Output "The power plan has been set as active. Output:" 52 | Write-Output $setActiveOutput 53 | 54 | Write-Host "> Ultimate Performance plan installed and set as active." 55 | } 56 | "Disable" { 57 | # Check if the Ultimate Performance plan is installed by GUID 58 | $installedPlan = powercfg -list | Select-String -Pattern "ChrisTitus - Ultimate Power Plan" 59 | 60 | if ($installedPlan) { 61 | # Extract the GUID of the installed Ultimate Performance plan 62 | $ultimatePlanGUID = $installedPlan.Line.Split()[3] 63 | 64 | # Set a different power plan as active before deleting the Ultimate Performance plan 65 | $balancedPlanGUID = "381b4222-f694-41f0-9685-ff5bb260df2e" 66 | powercfg -setactive $balancedPlanGUID 67 | 68 | # Delete the Ultimate Performance plan by GUID 69 | powercfg -delete $ultimatePlanGUID 70 | 71 | Write-Host "Ultimate Performance plan has been uninstalled." 72 | Write-Host "> Balanced plan is now active." 73 | } else { 74 | Write-Host "Ultimate Performance plan is not installed." 75 | } 76 | } 77 | default { 78 | Write-Host "Invalid state. Please use 'Enable' or 'Disable'." 79 | } 80 | } 81 | } catch { 82 | Write-Error "Error occurred: $_" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFUnInstall.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFUnInstall { 2 | param( 3 | [Parameter(Mandatory=$false)] 4 | [PSObject[]]$PackagesToUninstall = $($sync.selectedApps | Foreach-Object { $sync.configs.applicationsHashtable.$_ }) 5 | ) 6 | <# 7 | 8 | .SYNOPSIS 9 | Uninstalls the selected programs 10 | #> 11 | 12 | if($sync.ProcessRunning) { 13 | $msg = "[Invoke-WPFUnInstall] Install process is currently running" 14 | [System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) 15 | return 16 | } 17 | 18 | if ($PackagesToUninstall.Count -eq 0) { 19 | $WarningMsg = "Please select the program(s) to uninstall" 20 | [System.Windows.MessageBox]::Show($WarningMsg, $AppTitle, [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) 21 | return 22 | } 23 | 24 | $ButtonType = [System.Windows.MessageBoxButton]::YesNo 25 | $MessageboxTitle = "Are you sure?" 26 | $Messageboxbody = ("This will uninstall the following applications: `n $($PackagesToUninstall | Select-Object Name, Description| Out-String)") 27 | $MessageIcon = [System.Windows.MessageBoxImage]::Information 28 | 29 | $confirm = [System.Windows.MessageBox]::Show($Messageboxbody, $MessageboxTitle, $ButtonType, $MessageIcon) 30 | 31 | if($confirm -eq "No") {return} 32 | 33 | $ManagerPreference = $sync["ManagerPreference"] 34 | 35 | Invoke-WPFRunspace -ArgumentList @(("PackagesToUninstall", $PackagesToUninstall),("ManagerPreference", $ManagerPreference)) -DebugPreference $DebugPreference -ScriptBlock { 36 | param($PackagesToUninstall, $ManagerPreference, $DebugPreference) 37 | 38 | $packagesSorted = Get-WinUtilSelectedPackages -PackageList $PackagesToUninstall -Preference $ManagerPreference 39 | $packagesWinget = $packagesSorted[[PackageManagers]::Winget] 40 | $packagesChoco = $packagesSorted[[PackageManagers]::Choco] 41 | 42 | try { 43 | $sync.ProcessRunning = $true 44 | Show-WPFInstallAppBusy -text "Uninstalling apps..." 45 | 46 | # Uninstall all selected programs in new window 47 | if($packagesWinget.Count -gt 0) { 48 | Install-WinUtilProgramWinget -Action Uninstall -Programs $packagesWinget 49 | } 50 | if($packagesChoco.Count -gt 0) { 51 | Install-WinUtilProgramChoco -Action Uninstall -Programs $packagesChoco 52 | } 53 | Hide-WPFInstallAppBusy 54 | Write-Host "===========================================" 55 | Write-Host "-- Uninstalls have finished ---" 56 | Write-Host "===========================================" 57 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }) 58 | } catch { 59 | Write-Host "===========================================" 60 | Write-Host "Error: $_" 61 | Write-Host "===========================================" 62 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Error" -overlay "warning" }) 63 | } 64 | $sync.ProcessRunning = $False 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFUpdatesdefault.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFUpdatesdefault { 2 | <# 3 | 4 | .SYNOPSIS 5 | Resets Windows Update settings to default 6 | 7 | #> 8 | If (!(Test-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU")) { 9 | New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Force | Out-Null 10 | } 11 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "NoAutoUpdate" -Type DWord -Value 0 12 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "AUOptions" -Type DWord -Value 3 13 | If (!(Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config")) { 14 | New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -Force | Out-Null 15 | } 16 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -Name "DODownloadMode" -Type DWord -Value 1 17 | 18 | $services = @( 19 | "BITS" 20 | "wuauserv" 21 | ) 22 | 23 | foreach ($service in $services) { 24 | # -ErrorAction SilentlyContinue is so it doesn't write an error to stdout if a service doesn't exist 25 | 26 | Write-Host "Setting $service StartupType to Automatic" 27 | Get-Service -Name $service -ErrorAction SilentlyContinue | Set-Service -StartupType Automatic 28 | } 29 | Write-Host "Enabling driver offering through Windows Update..." 30 | Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" -Name "PreventDeviceMetadataFromNetwork" -ErrorAction SilentlyContinue 31 | Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Name "DontPromptForWindowsUpdate" -ErrorAction SilentlyContinue 32 | Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Name "DontSearchWindowsUpdate" -ErrorAction SilentlyContinue 33 | Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Name "DriverUpdateWizardWuSearchEnabled" -ErrorAction SilentlyContinue 34 | Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -Name "ExcludeWUDriversInQualityUpdate" -ErrorAction SilentlyContinue 35 | Write-Host "Enabling Windows Update automatic restart..." 36 | Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "NoAutoRebootWithLoggedOnUsers" -ErrorAction SilentlyContinue 37 | Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "AUPowerManagement" -ErrorAction SilentlyContinue 38 | Write-Host "Enabled driver offering through Windows Update" 39 | Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Name "BranchReadinessLevel" -ErrorAction SilentlyContinue 40 | Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Name "DeferFeatureUpdatesPeriodInDays" -ErrorAction SilentlyContinue 41 | Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Name "DeferQualityUpdatesPeriodInDays" -ErrorAction SilentlyContinue 42 | Write-Host "===================================================" 43 | Write-Host "--- Windows Update Settings Reset to Default ---" 44 | Write-Host "===================================================" 45 | 46 | Start-Process -FilePath "secedit" -ArgumentList "/configure /cfg $env:windir\inf\defltbase.inf /db defltbase.sdb /verbose" -Wait 47 | Start-Process -FilePath "cmd.exe" -ArgumentList "/c RD /S /Q $env:WinDir\System32\GroupPolicyUsers" -Wait 48 | Start-Process -FilePath "cmd.exe" -ArgumentList "/c RD /S /Q $env:WinDir\System32\GroupPolicy" -Wait 49 | Start-Process -FilePath "gpupdate" -ArgumentList "/force" -Wait 50 | Remove-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies" -Recurse -Force -ErrorAction SilentlyContinue 51 | Remove-Item -Path "HKCU:\Software\Microsoft\WindowsSelfHost" -Recurse -Force -ErrorAction SilentlyContinue 52 | Remove-Item -Path "HKCU:\Software\Policies" -Recurse -Force -ErrorAction SilentlyContinue 53 | Remove-Item -Path "HKLM:\Software\Microsoft\Policies" -Recurse -Force -ErrorAction SilentlyContinue 54 | Remove-Item -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies" -Recurse -Force -ErrorAction SilentlyContinue 55 | Remove-Item -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\WindowsStore\WindowsUpdate" -Recurse -Force -ErrorAction SilentlyContinue 56 | Remove-Item -Path "HKLM:\Software\Microsoft\WindowsSelfHost" -Recurse -Force -ErrorAction SilentlyContinue 57 | Remove-Item -Path "HKLM:\Software\Policies" -Recurse -Force -ErrorAction SilentlyContinue 58 | Remove-Item -Path "HKLM:\Software\WOW6432Node\Microsoft\Policies" -Recurse -Force -ErrorAction SilentlyContinue 59 | Remove-Item -Path "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Policies" -Recurse -Force -ErrorAction SilentlyContinue 60 | Remove-Item -Path "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\WindowsStore\WindowsUpdate" -Recurse -Force -ErrorAction SilentlyContinue 61 | 62 | Write-Host "===================================================" 63 | Write-Host "--- Windows Local Policies Reset to Default ---" 64 | Write-Host "===================================================" 65 | } 66 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFUpdatesdisable.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFUpdatesdisable { 2 | <# 3 | 4 | .SYNOPSIS 5 | Disables Windows Update 6 | 7 | .NOTES 8 | Disabling Windows Update is not recommended. This is only for advanced users who know what they are doing. 9 | 10 | #> 11 | If (!(Test-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU")) { 12 | New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Force | Out-Null 13 | } 14 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "NoAutoUpdate" -Type DWord -Value 1 15 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "AUOptions" -Type DWord -Value 1 16 | If (!(Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config")) { 17 | New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -Force | Out-Null 18 | } 19 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config" -Name "DODownloadMode" -Type DWord -Value 0 20 | 21 | $services = @( 22 | "BITS" 23 | "wuauserv" 24 | ) 25 | 26 | foreach ($service in $services) { 27 | # -ErrorAction SilentlyContinue is so it doesn't write an error to stdout if a service doesn't exist 28 | 29 | Write-Host "Setting $service StartupType to Disabled" 30 | Get-Service -Name $service -ErrorAction SilentlyContinue | Set-Service -StartupType Disabled 31 | } 32 | Write-Host "=================================" 33 | Write-Host "--- Updates ARE DISABLED ---" 34 | Write-Host "=================================" 35 | } 36 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFUpdatessecurity.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFUpdatessecurity { 2 | <# 3 | 4 | .SYNOPSIS 5 | Sets Windows Update to recommended settings 6 | 7 | .DESCRIPTION 8 | 1. Disables driver offering through Windows Update 9 | 2. Disables Windows Update automatic restart 10 | 3. Sets Windows Update to Semi-Annual Channel (Targeted) 11 | 4. Defers feature updates for 365 days 12 | 5. Defers quality updates for 4 days 13 | 14 | #> 15 | Write-Host "Disabling driver offering through Windows Update..." 16 | If (!(Test-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata")) { 17 | New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" -Force | Out-Null 18 | } 19 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" -Name "PreventDeviceMetadataFromNetwork" -Type DWord -Value 1 20 | If (!(Test-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching")) { 21 | New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Force | Out-Null 22 | } 23 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Name "DontPromptForWindowsUpdate" -Type DWord -Value 1 24 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Name "DontSearchWindowsUpdate" -Type DWord -Value 1 25 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DriverSearching" -Name "DriverUpdateWizardWuSearchEnabled" -Type DWord -Value 0 26 | If (!(Test-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate")) { 27 | New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" | Out-Null 28 | } 29 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -Name "ExcludeWUDriversInQualityUpdate" -Type DWord -Value 1 30 | Write-Host "Disabling Windows Update automatic restart..." 31 | If (!(Test-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU")) { 32 | New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Force | Out-Null 33 | } 34 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "NoAutoRebootWithLoggedOnUsers" -Type DWord -Value 1 35 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "AUPowerManagement" -Type DWord -Value 0 36 | Write-Host "Disabled driver offering through Windows Update" 37 | If (!(Test-Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings")) { 38 | New-Item -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Force | Out-Null 39 | } 40 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Name "BranchReadinessLevel" -Type DWord -Value 20 41 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Name "DeferFeatureUpdatesPeriodInDays" -Type DWord -Value 365 42 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Name "DeferQualityUpdatesPeriodInDays" -Type DWord -Value 4 43 | 44 | $ButtonType = [System.Windows.MessageBoxButton]::OK 45 | $MessageboxTitle = "Set Security Updates" 46 | $Messageboxbody = ("Recommended Update settings loaded") 47 | $MessageIcon = [System.Windows.MessageBoxImage]::Information 48 | 49 | [System.Windows.MessageBox]::Show($Messageboxbody, $MessageboxTitle, $ButtonType, $MessageIcon) 50 | Write-Host "=================================" 51 | Write-Host "-- Updates Set to Recommended ---" 52 | Write-Host "=================================" 53 | } 54 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFtweaksbutton.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFtweaksbutton { 2 | <# 3 | 4 | .SYNOPSIS 5 | Invokes the functions associated with each group of checkboxes 6 | 7 | #> 8 | 9 | if($sync.ProcessRunning) { 10 | $msg = "[Invoke-WPFtweaksbutton] Install process is currently running." 11 | [System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) 12 | return 13 | } 14 | 15 | $Tweaks = (Get-WinUtilCheckBoxes)["WPFTweaks"] 16 | 17 | Set-WinUtilDNS -DNSProvider $sync["WPFchangedns"].text 18 | 19 | if ($tweaks.count -eq 0 -and $sync["WPFchangedns"].text -eq "Default") { 20 | $msg = "Please check the tweaks you wish to perform." 21 | [System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) 22 | return 23 | } 24 | 25 | Write-Debug "Number of tweaks to process: $($Tweaks.Count)" 26 | 27 | # The leading "," in the ParameterList is nessecary because we only provide one argument and powershell cannot be convinced that we want a nested loop with only one argument otherwise 28 | Invoke-WPFRunspace -ParameterList @(,("tweaks",$tweaks)) -DebugPreference $DebugPreference -ScriptBlock { 29 | param( 30 | $tweaks, 31 | $DebugPreference 32 | ) 33 | Write-Debug "Inside Number of tweaks to process: $($Tweaks.Count)" 34 | 35 | $sync.ProcessRunning = $true 36 | 37 | if ($Tweaks.count -eq 1) { 38 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }) 39 | } else { 40 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }) 41 | } 42 | # Execute other selected tweaks 43 | 44 | for ($i = 0; $i -lt $Tweaks.Count; $i++) { 45 | Set-WinUtilProgressBar -Label "Applying $($tweaks[$i])" -Percent ($i / $tweaks.Count * 100) 46 | Invoke-WinUtilTweaks $tweaks[$i] 47 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($i/$Tweaks.Count) }) 48 | } 49 | Set-WinUtilProgressBar -Label "Tweaks finished" -Percent 100 50 | $sync.ProcessRunning = $false 51 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }) 52 | Write-Host "=================================" 53 | Write-Host "-- Tweaks are Finished ---" 54 | Write-Host "=================================" 55 | 56 | # $ButtonType = [System.Windows.MessageBoxButton]::OK 57 | # $MessageboxTitle = "Tweaks are Finished " 58 | # $Messageboxbody = ("Done") 59 | # $MessageIcon = [System.Windows.MessageBoxImage]::Information 60 | # [System.Windows.MessageBox]::Show($Messageboxbody, $MessageboxTitle, $ButtonType, $MessageIcon) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /functions/public/Invoke-WPFundoall.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-WPFundoall { 2 | <# 3 | 4 | .SYNOPSIS 5 | Undoes every selected tweak 6 | 7 | #> 8 | 9 | if($sync.ProcessRunning) { 10 | $msg = "[Invoke-WPFundoall] Install process is currently running." 11 | [System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) 12 | return 13 | } 14 | 15 | $tweaks = (Get-WinUtilCheckBoxes)["WPFtweaks"] 16 | 17 | if ($tweaks.count -eq 0) { 18 | $msg = "Please check the tweaks you wish to undo." 19 | [System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) 20 | return 21 | } 22 | 23 | Invoke-WPFRunspace -ArgumentList $tweaks -DebugPreference $DebugPreference -ScriptBlock { 24 | param($tweaks, $DebugPreference) 25 | 26 | $sync.ProcessRunning = $true 27 | if ($tweaks.count -eq 1) { 28 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Indeterminate" -value 0.01 -overlay "logo" }) 29 | } else { 30 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "Normal" -value 0.01 -overlay "logo" }) 31 | } 32 | 33 | 34 | for ($i = 0; $i -lt $tweaks.Count; $i++) { 35 | Set-WinUtilProgressBar -Label "Undoing $($tweaks[$i])" -Percent ($i / $tweaks.Count * 100) 36 | Invoke-WinUtiltweaks $tweaks[$i] -undo $true 37 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -value ($i/$tweaks.Count) }) 38 | } 39 | 40 | Set-WinUtilProgressBar -Label "Undo Tweaks Finished" -Percent 100 41 | $sync.ProcessRunning = $false 42 | $sync.form.Dispatcher.Invoke([action]{ Set-WinUtilTaskbaritem -state "None" -overlay "checkmark" }) 43 | Write-Host "==================================" 44 | Write-Host "--- Undo Tweaks are Finished ---" 45 | Write-Host "==================================" 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /functions/public/Show-CTTLogo.ps1: -------------------------------------------------------------------------------- 1 | Function Show-CTTLogo { 2 | <# 3 | .SYNOPSIS 4 | Displays the CTT logo in ASCII art. 5 | .DESCRIPTION 6 | This function displays the CTT logo in ASCII art format. 7 | .PARAMETER None 8 | No parameters are required for this function. 9 | .EXAMPLE 10 | Show-CTTLogo 11 | Prints the CTT logo in ASCII art format to the console. 12 | #> 13 | 14 | $asciiArt = @" 15 | CCCCCCCCCCCCCTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT 16 | CCC::::::::::::CT:::::::::::::::::::::TT:::::::::::::::::::::T 17 | CC:::::::::::::::CT:::::::::::::::::::::TT:::::::::::::::::::::T 18 | C:::::CCCCCCCC::::CT:::::TT:::::::TT:::::TT:::::TT:::::::TT:::::T 19 | C:::::C CCCCCCTTTTTT T:::::T TTTTTTTTTTTT T:::::T TTTTTT 20 | C:::::C T:::::T T:::::T 21 | C:::::C T:::::T T:::::T 22 | C:::::C T:::::T T:::::T 23 | C:::::C T:::::T T:::::T 24 | C:::::C T:::::T T:::::T 25 | C:::::C T:::::T T:::::T 26 | C:::::C CCCCCC T:::::T T:::::T 27 | C:::::CCCCCCCC::::C TT:::::::TT TT:::::::TT 28 | CC:::::::::::::::C T:::::::::T T:::::::::T 29 | CCC::::::::::::C T:::::::::T T:::::::::T 30 | CCCCCCCCCCCCC TTTTTTTTTTT TTTTTTTTTTT 31 | 32 | ====Chris Titus Tech===== 33 | =====Windows Toolbox===== 34 | "@ 35 | 36 | Write-Host $asciiArt 37 | } 38 | 39 | -------------------------------------------------------------------------------- /lint/PSScriptAnalyser.ps1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Only diagnostic records of the specified severity will be generated. 3 | # Uncomment the following line if you only want Errors and Warnings but 4 | # not Information diagnostic records. 5 | # Severity = @('Error','Warning') 6 | 7 | # Analyze **only** the following rules. Use IncludeRules when you want 8 | # to invoke only a small subset of the default rules. 9 | <# 10 | IncludeRules = @('PSAvoidDefaultValueSwitchParameter', 11 | 'PSMisleadingBacktick', 12 | 'PSMissingModuleManifestField', 13 | 'PSReservedCmdletChar', 14 | 'PSReservedParams', 15 | 'PSShouldProcess', 16 | 'PSUseApprovedVerbs', 17 | 'PSUseDeclaredVarsMoreThanAssignments') 18 | #> 19 | # Do not analyze the following rules. Use ExcludeRules when you have 20 | # commented out the IncludeRules settings above and want to include all 21 | # the default rules except for those you exclude below. 22 | # Note: if a rule is in both IncludeRules and ExcludeRules, the rule 23 | # will be excluded. 24 | ExcludeRules = @('PSAvoidUsingWriteHost') 25 | } 26 | -------------------------------------------------------------------------------- /overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block header %} 4 | {{ super() }} 5 | <div style="color: red; text-align: center; padding: 10px; font-size: 20px;"> 6 | <strong>Announcement:</strong> We are currently reworking the docs to use Hugo rather then mkdocs. 7 | </div> 8 | {% endblock %} 9 | 10 | {% block footer %} 11 | {# Empty block to override the footer #} 12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /pester/configs.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Import Config Files 2 | $global:importedconfigs = @{} 3 | Get-ChildItem .\config | Where-Object {$_.Extension -eq ".json"} | ForEach-Object { 4 | $global:importedconfigs[$psitem.BaseName] = Get-Content $psitem.FullName | ConvertFrom-Json 5 | } 6 | 7 | 8 | #=========================================================================== 9 | # Tests - Application Installs 10 | #=========================================================================== 11 | 12 | Describe "Config Files" -ForEach @( 13 | @{ 14 | name = "applications" 15 | config = $('{ 16 | "winget": "value", 17 | "choco": "value", 18 | "category": "value", 19 | "content": "value", 20 | "description": "value", 21 | "link": "value" 22 | }' | ConvertFrom-Json) 23 | }, 24 | @{ 25 | name = "tweaks" 26 | undo = $true 27 | } 28 | ) { 29 | Context "$name config file" { 30 | It "Imports with no errors" { 31 | $global:importedconfigs.$name | should -Not -BeNullOrEmpty 32 | } 33 | if ($config) { 34 | It "Imports should be the correct structure" { 35 | $applications = $global:importedconfigs.$name | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name 36 | $template = $config | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name 37 | $result = New-Object System.Collections.Generic.List[System.Object] 38 | Foreach ($application in $applications) { 39 | $compare = $global:importedconfigs.$name.$application | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name 40 | if (-not $compare) { 41 | throw "Comparison object for application '$application' is null." 42 | } 43 | if (-not $template) { 44 | throw "Template object for application '$application' is null." 45 | } 46 | if ($(Compare-Object $compare $template) -ne $null) { 47 | $result.Add($application) 48 | } 49 | } 50 | 51 | $result | Select-String "WPF*" | should -BeNullOrEmpty 52 | } 53 | } 54 | if($undo) { 55 | It "Tweaks should contain original Value" { 56 | $tweaks = $global:importedconfigs.$name | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name 57 | $result = New-Object System.Collections.Generic.List[System.Object] 58 | 59 | foreach ($tweak in $tweaks) { 60 | $Originals = @( 61 | @{ 62 | name = "registry" 63 | value = "OriginalValue" 64 | }, 65 | @{ 66 | name = "service" 67 | value = "OriginalType" 68 | }, 69 | @{ 70 | name = "ScheduledTask" 71 | value = "OriginalState" 72 | } 73 | ) 74 | Foreach ($original in $Originals) { 75 | $TotalCount = ($global:importedconfigs.$name.$tweak.$($original.name)).count 76 | $OriginalCount = ($global:importedconfigs.$name.$tweak.$($original.name).$($original.value) | Where-Object {$_}).count 77 | if($TotalCount -ne $OriginalCount) { 78 | $result.Add("$Tweak,$($original.name)") 79 | } 80 | } 81 | } 82 | $result | Select-String "WPF*" | should -BeNullOrEmpty 83 | } 84 | } 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /pester/functions.Tests.ps1: -------------------------------------------------------------------------------- 1 | #=========================================================================== 2 | # Tests - Functions 3 | #=========================================================================== 4 | Describe "Comprehensive Checks for PS1 Files in Functions Folder" { 5 | BeforeAll { 6 | # Get all .ps1 files in the functions folder 7 | $ps1Files = Get-ChildItem -Path ./functions -Filter *.ps1 -Recurse 8 | } 9 | 10 | foreach ($file in $ps1Files) { 11 | Context "Checking $($file.Name)" { 12 | It "Should import without errors" { 13 | { . $file.FullName } | Should -Not -Throw 14 | } 15 | 16 | It "Should have no syntax errors" { 17 | $syntaxErrors = $null 18 | $null = [System.Management.Automation.PSParser]::Tokenize((Get-Content -Path $file.FullName -Raw), [ref]$syntaxErrors) 19 | $syntaxErrors.Count | Should -Be 0 20 | } 21 | 22 | It "Should not use deprecated cmdlets or aliases" { 23 | $content = Get-Content -Path $file.FullName -Raw 24 | # Example check for a known deprecated cmdlet or alias 25 | $content | Should -Not -Match 'DeprecatedCmdlet' 26 | # Add more checks as needed 27 | } 28 | 29 | It "Should follow naming conventions for functions" { 30 | $functions = (Get-Command -Path $file.FullName).Name 31 | foreach ($function in $functions) { 32 | $function | Should -Match '^[a-z]+(-[a-z]+)*#39; # Enforce lower-kebab-case 33 | } 34 | } 35 | 36 | It "Should define mandatory parameters for all functions" { 37 | . $file.FullName 38 | $functions = (Get-Command -Path $file.FullName).Name 39 | foreach ($function in $functions) { 40 | $parameters = (Get-Command -Name $function).Parameters.Values 41 | $mandatoryParams = $parameters | Where-Object { $_.Attributes.Mandatory -eq $true } 42 | $mandatoryParams.Count | Should -BeGreaterThan 0 43 | } 44 | } 45 | 46 | It "Should have all functions available after import" { 47 | . $file.FullName 48 | $functions = (Get-Command -Path $file.FullName).Name 49 | foreach ($function in $functions) { 50 | { Get-Command -Name $function -CommandType Function } | Should -Not -BeNullOrEmpty 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /releases/oscdimg.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisTitusTech/winutil/c983ec2253b760cbe5ec5bcbfec0ff76827c7549/releases/oscdimg.exe -------------------------------------------------------------------------------- /scripts/start.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | Author : Chris Titus @christitustech 4 | Runspace Author: @DeveloperDurp 5 | GitHub : https://github.com/ChrisTitusTech 6 | Version : #{replaceme} 7 | #> 8 | 9 | param ( 10 | [switch]$Debug, 11 | [string]$Config, 12 | [switch]$Run 13 | ) 14 | 15 | # Set DebugPreference based on the -Debug switch 16 | if ($Debug) { 17 | $DebugPreference = "Continue" 18 | } 19 | 20 | if ($Config) { 21 | $PARAM_CONFIG = $Config 22 | } 23 | 24 | $PARAM_RUN = $false 25 | # Handle the -Run switch 26 | if ($Run) { 27 | Write-Host "Running config file tasks..." 28 | $PARAM_RUN = $true 29 | } 30 | 31 | # Load DLLs 32 | Add-Type -AssemblyName PresentationFramework 33 | Add-Type -AssemblyName System.Windows.Forms 34 | 35 | # Variable to sync between runspaces 36 | $sync = [Hashtable]::Synchronized(@{}) 37 | $sync.PSScriptRoot = $PSScriptRoot 38 | $sync.version = "#{replaceme}" 39 | $sync.configs = @{} 40 | $sync.Buttons = [System.Collections.Generic.List[PSObject]]::new() 41 | $sync.ProcessRunning = $false 42 | $sync.selectedApps = [System.Collections.Generic.List[string]]::new() 43 | $sync.currentTab = "Install" 44 | $sync.selectedAppsStackPanel 45 | $sync.selectedAppsPopup 46 | 47 | 48 | if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { 49 | Write-Output "Winutil needs to be run as Administrator. Attempting to relaunch." 50 | $argList = @() 51 | 52 | $PSBoundParameters.GetEnumerator() | ForEach-Object { 53 | $argList += if ($_.Value -is [switch] -and $_.Value) { 54 | "-$($_.Key)" 55 | } elseif ($_.Value -is [array]) { 56 | "-$($_.Key) $($_.Value -join ',')" 57 | } elseif ($_.Value) { 58 | "-$($_.Key) '$($_.Value)'" 59 | } 60 | } 61 | 62 | $script = if ($PSCommandPath) { 63 | "& { & `'$($PSCommandPath)`' $($argList -join ' ') }" 64 | } else { 65 | "&([ScriptBlock]::Create((irm https://github.com/ChrisTitusTech/winutil/releases/latest/download/winutil.ps1))) $($argList -join ' ')" 66 | } 67 | 68 | $powershellCmd = if (Get-Command pwsh -ErrorAction SilentlyContinue) { "pwsh" } else { "powershell" } 69 | $processCmd = if (Get-Command wt.exe -ErrorAction SilentlyContinue) { "wt.exe" } else { "$powershellCmd" } 70 | 71 | if ($processCmd -eq "wt.exe") { 72 | Start-Process $processCmd -ArgumentList "$powershellCmd -ExecutionPolicy Bypass -NoProfile -Command `"$script`"" -Verb RunAs 73 | } else { 74 | Start-Process $processCmd -ArgumentList "-ExecutionPolicy Bypass -NoProfile -Command `"$script`"" -Verb RunAs 75 | } 76 | 77 | break 78 | } 79 | 80 | $dateTime = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" 81 | 82 | $logdir = "$env:localappdata\winutil\logs" 83 | [System.IO.Directory]::CreateDirectory("$logdir") | Out-Null 84 | Start-Transcript -Path "$logdir\winutil_$dateTime.log" -Append -NoClobber | Out-Null 85 | 86 | # Set PowerShell window title 87 | $Host.UI.RawUI.WindowTitle = "WinUtil (Admin)" 88 | clear-host 89 | -------------------------------------------------------------------------------- /sign.bat: -------------------------------------------------------------------------------- 1 | signtool.exe sign /td sha256 /tr http://timestamp.digicert.com /fd sha256 /n "CT Tech Group LLC" .\winutil.ps1 2 | -------------------------------------------------------------------------------- /windev.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This Script is used as a target for the https://christitus.com/windev alias. 4 | It queries the latest winget release (no matter if Pre-Release, Draft or Full Release) and invokes It 5 | .DESCRIPTION 6 | This Script provides a simple way to always start the bleeding edge release even if it's not yet a full release. 7 | This function should be run with administrative privileges. 8 | Because this way of recursively invoking scripts via Invoke-Expression it might very well happen that AV Programs flag this because it's a common way of mulitstage exploits to run 9 | .EXAMPLE 10 | irm https://christitus.com/windev | iex 11 | OR 12 | Run in Admin Powershell > ./windev.ps1 13 | #> 14 | 15 | # Function to fetch the latest release tag from the GitHub API 16 | function Get-LatestRelease { 17 | try { 18 | $releases = Invoke-RestMethod -Uri 'https://api.github.com/repos/ChrisTitusTech/winutil/releases' 19 | $latestRelease = $releases | Where-Object {$_.prerelease -eq $true} | Select-Object -First 1 20 | return $latestRelease.tag_name 21 | } catch { 22 | Write-Host "Error fetching release data: $_" -ForegroundColor Red 23 | return $latestRelease.tag_name 24 | } 25 | } 26 | 27 | # Function to redirect to the latest pre-release version 28 | function RedirectToLatestPreRelease { 29 | $latestRelease = Get-LatestRelease 30 | if ($latestRelease) { 31 | $url = "https://github.com/ChrisTitusTech/winutil/releases/download/$latestRelease/winutil.ps1" 32 | } else { 33 | Write-Host 'No pre-release version found. This is most likely because the latest release is a full release and no newer pre-release exists.' -ForegroundColor Yellow 34 | Write-Host "Using latest Full Release" 35 | $url = "https://github.com/ChrisTitusTech/winutil/releases/latest/download/winutil.ps1" 36 | } 37 | 38 | $script = Invoke-RestMethod $url 39 | # Elevate Shell if necessary 40 | if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { 41 | Write-Output "Winutil needs to be run as Administrator. Attempting to relaunch." 42 | 43 | $powershellcmd = if (Get-Command pwsh -ErrorAction SilentlyContinue) { "pwsh" } else { "powershell" } 44 | $processCmd = if (Get-Command wt.exe -ErrorAction SilentlyContinue) { "wt.exe" } else { $powershellcmd } 45 | 46 | Start-Process $processCmd -ArgumentList "$powershellcmd -ExecutionPolicy Bypass -NoProfile -Command $(Invoke-Expression $script)" -Verb RunAs 47 | } 48 | else{ 49 | Invoke-Expression $script 50 | } 51 | } 52 | 53 | # Call the redirect function 54 | 55 | RedirectToLatestPreRelease 56 | --------------------------------------------------------------------------------