mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-08-10 01:51:18 +10:00
Compare commits
37
Commits
1c7ee06bbe
...
26.07.28
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f37ef4a80 | ||
|
|
d790118546 | ||
|
|
5ebb24b107 | ||
|
|
fae58d73c4 | ||
|
|
9e2989964e | ||
|
|
50be7390e5 | ||
|
|
8ab02ae756 | ||
|
|
7d1a03dcc9 | ||
|
|
5a4c9c883d | ||
|
|
5c104d02e2 | ||
|
|
06f33e88ba | ||
|
|
b9dee86694 | ||
|
|
3d1904ebbe | ||
|
|
70f1c50bdd | ||
|
|
2e5df82979 | ||
|
|
29595f90e2 | ||
|
|
9ed06e793a | ||
|
|
51cb3811dd | ||
|
|
975616b58f | ||
|
|
11783c75a8 | ||
|
|
13a18eeeb2 | ||
|
|
822003d87f | ||
|
|
c8c3c7869f | ||
|
|
157a66b14d | ||
|
|
9791386f53 | ||
|
|
c8e756dc11 | ||
|
|
9e21c8f4a7 | ||
|
|
8cd8679512 | ||
|
|
ac11c73b64 | ||
|
|
9d67514bf9 | ||
|
|
fea9f4e869 | ||
|
|
1aa0dd90ef | ||
|
|
90a19685f5 | ||
|
|
007995c112 | ||
|
|
0dcd361c51 | ||
|
|
aeaef46e30 | ||
|
|
e0dfeb4f4d |
+88
-114
@@ -1,149 +1,123 @@
|
||||
## Contributing Code
|
||||
# How to Contribute?
|
||||
|
||||
### Before You Start
|
||||
## Testing
|
||||
|
||||
- Keep each pull request focused on a single feature or fix.
|
||||
- Avoid unnecessary formatting changes or large unrelated edits.
|
||||
- Document what changed and why in your PR description.
|
||||
* Test the latest changes to WinUtil by running the pre-release and reporting issues you are encountering to help us continually improve WinUtil!
|
||||
|
||||
---
|
||||
#### **Run the latest pre-release**
|
||||
```ps1
|
||||
irm https://christitus.com/windev | iex
|
||||
```
|
||||
|
||||
## Basic Git Workflow
|
||||
!!! bug "Keep in mind"
|
||||
|
||||
### 1. Fork the Repository
|
||||
This is a pre-release and should be treated as such. It exists for developers to test the utility and report or fix bugs before they get added to the stable release. Don't use it in production!
|
||||
|
||||
Go to the ChrisTitusTech/winutil repository on GitHub and click the Fork button in the top right corner.
|
||||
## Issues
|
||||
|
||||
<img width="171" height="50" alt="{650A4723-F38A-44A4-9820-D232BC87C8A0}" src="https://github.com/user-attachments/assets/a214f27c-2fee-444a-920f-d87b14f5896f" />
|
||||
* If you encounter any challenges or problems with the script, I kindly request that you submit them via the "Issues" tab on the GitHub repository. By filling out the provided template, you can provide specific details about the issue, allowing me (and others in the community) to promptly address any bugs or consider feature requests.
|
||||
|
||||
---
|
||||
## Contribute Code
|
||||
|
||||
### 2. Clone Your Fork
|
||||
* Pull requests are now handled directly on the **MAIN branch**. This was done since we can now select specific releases to launch via releases in GitHub.
|
||||
|
||||
```powershell
|
||||
git clone https://github.com/YOUR_USERNAME/winutil.git
|
||||
cd winutil
|
||||
* If you're doing code changes, then you can submit a PR to the `main` branch.
|
||||
|
||||
!!! warning "Important"
|
||||
|
||||
Do not use a code formatter, make massive amounts of line changes, or make multiple feature changes. EACH FEATURE CHANGE SHOULD BE IT'S OWN PULL REQUEST!
|
||||
|
||||
Do not open a pull request that adds support for other languages to WinUtil for now, until we decide how we want to move forward with language support.
|
||||
|
||||
* When creating pull requests, it is essential to thoroughly document all changes made. This includes, but is not limited to, documenting any additions made to the `tweaks` section and corresponding `undo tweak`, so users are able to remove the newly added tweaks if necessary, and comprehensive documentation is required for all code changes. Document your changes and briefly explain why you made your changes in your Pull Request Description. Failure to adhere to this format may result in the denial of the pull request. Additionally, any code lacking sufficient documentation may also be denied.
|
||||
|
||||
* By following these guidelines, we can maintain a high standard of quality and ensure that the codebase remains organized and well-documented.
|
||||
|
||||
!!! note
|
||||
|
||||
When creating a function, please include "WPF" or "WinUtil" in the file name so it can be loaded into the runspace.
|
||||
|
||||
## Walk through
|
||||
|
||||
* This is a guide for beginners. If you are still having issues, look at the following official GitHub documentation:
|
||||
* [Commit through WEB](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/about-commits)
|
||||
* [Commit through GitHub Desktop](https://docs.github.com/en/desktop/making-changes-in-a-branch/committing-and-reviewing-changes-to-your-project-in-github-desktop#about-commits)
|
||||
* [Create a Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request)
|
||||
|
||||
|
||||
### Overview
|
||||
|
||||
``` mermaid
|
||||
%%{init: {"flowchart": {"curve": "cardinal"}} }%%
|
||||
graph TD
|
||||
A[Fork Project] --> B[Clone Repository];
|
||||
B --> C[Create New Branch];
|
||||
C --> D[Make Changes];
|
||||
D --> G[Test Changes];
|
||||
G --> H{Tests Passed?};
|
||||
H -->|Yes| E[Commit Changes];
|
||||
H -->|No| J[Fix Issues];
|
||||
J --> G;
|
||||
E --> F[Push Branch];
|
||||
F --> K[Create Pull Request];
|
||||
K --> L[Fill out PR template];
|
||||
classDef default stroke:#333,stroke-width:4px,font-size:12pt;
|
||||
```
|
||||
!!! info
|
||||
|
||||
---
|
||||
This is a diagram to guide you through the process. It may vary depending on the type of change you're making.
|
||||
|
||||
### 3. Create a Branch
|
||||
### Fork the Repo
|
||||
* Fork the WinUtil Repository [here](https://github.com/ChrisTitusTech/winutil) to create a copy that will be available in your repository list.
|
||||
|
||||
Never work directly on `main`.
|
||||

|
||||
|
||||
Create a branch related to your change:
|
||||

|
||||
|
||||
```powershell
|
||||
git checkout -b feature-name
|
||||
```
|
||||
### Clone the Fork
|
||||
!!! tip
|
||||
|
||||
Example:
|
||||
While you can make your changes directly through the Web, we recommend cloning the repo to your device using the application GitHub Desktop (available in WinUtil) to test your fork easily.
|
||||
|
||||
```powershell
|
||||
git checkout -b add-firefox-tweak
|
||||
```
|
||||
* Install GitHub Desktop if it is not already installed.
|
||||
* Log in using the same GitHub account you used to fork WinUtil.
|
||||
* Choose the fork under "Your Repositories" and press "clone {repo name}"
|
||||
* Create a new branch and name it something relatable to your changes.
|
||||
|
||||
---
|
||||
* Now you can modify WinUtil to your liking using your preferred text editor.
|
||||
|
||||
### 4. Edit the Code
|
||||
|
||||
Open the project in your preferred text editor and make your changes.
|
||||
### Testing your changes
|
||||
|
||||
Keep changes small and focused.
|
||||
* To test to see if your changes work as intended, run the following commands in a PowerShell terminal as admin:
|
||||
|
||||
---
|
||||
* Change the directory where you are running the commands to the forked project.
|
||||
* `cd {path to the folder with the compile.ps1}`
|
||||
* Run the following command to compile and run WinUtil:
|
||||
* `.\Compile.ps1 -run`
|
||||
|
||||
### 5. Test Your Changes
|
||||

|
||||
|
||||
Open Powershell as Administrator.
|
||||
|
||||
Go to the project folder:
|
||||
Open PowerShell as Administrator.
|
||||
|
||||
```powershell
|
||||
cd path\to\winutil
|
||||
```
|
||||
* After seeing that your changes work properly, feel free to commit the changes to the repository and make a PR. For help on that, follow the documentation below.
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
.\Compile.ps1 -Run
|
||||
```
|
||||
### Committing the changes
|
||||
* Before committing your changes, please discard changes made to the `winutil.ps1` file, like the following:
|
||||
|
||||
Verify:
|
||||

|
||||
|
||||
- WinUtil launches correctly
|
||||
- Your feature works
|
||||
- Nothing else breaks
|
||||
* Now, commit your changes once you are happy with the result.
|
||||
|
||||
If something fails, fix it before committing.
|
||||

|
||||
|
||||
---
|
||||
* Push the changes to upload them to your fork on github.com.
|
||||
|
||||
### 6. Review Your Changes
|
||||

|
||||
|
||||
Check what changed:
|
||||
|
||||
```powershell
|
||||
git status
|
||||
```
|
||||
|
||||
Review the diff:
|
||||
|
||||
```powershell
|
||||
git diff
|
||||
```
|
||||
|
||||
Make sure you did not accidentally modify unrelated files.
|
||||
|
||||
---
|
||||
|
||||
### 7. Commit Your Changes
|
||||
|
||||
Stage files:
|
||||
|
||||
```powershell
|
||||
git add .
|
||||
```
|
||||
|
||||
Commit them:
|
||||
|
||||
```powershell
|
||||
git commit -m "Add feature description"
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```powershell
|
||||
git commit -m "Add Firefox package tweak"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. Push Your Branch
|
||||
|
||||
```powershell
|
||||
git push origin branch-name
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```powershell
|
||||
git push origin add-firefox-tweak
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 9. Open a Pull Request
|
||||
|
||||
Go to your fork on GitHub.
|
||||
|
||||
GitHub will show a button to create a pull request.
|
||||
<img width="1009" height="71" alt="{C8C6A3CC-79D4-44FD-A54C-4C5717F12730}" src="https://github.com/user-attachments/assets/0419d193-d4e7-47c0-87cf-b986742201a0" />
|
||||
|
||||
Before submitting:
|
||||
|
||||
- Explain what changed
|
||||
- Explain why you changed it
|
||||
- Make sure unrelated files are not included
|
||||
|
||||
Once submitted, maintainers will review your PR.
|
||||
### Making a PR
|
||||
* To make a PR on your repo under a new branch linking to the main branch, a button will show and say Preview and Create pull request. Click that button and fill in all the information that is provided on the template. Once all the information is filled in correctly, check your PR to make sure there is no WinUtil.ps1 file attached to the PR. Once everything is good, make the PR and wait for Chris (the maintainer) to accept or deny your PR. Once it is accepted by Chris, you will be able to see your changes in the "/windev" build.
|
||||
* If you do not see your feature in the main "/win" build, that is fine. All new changes go into the /windev build to make sure everything is working OK before going fully public.
|
||||
* Congratulations! You just submitted your first PR. Thank you so much for contributing to WinUtil.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Auto-merge Docs PRs
|
||||
name: Auto-merge Automated Update PRs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
auto-merge:
|
||||
if: github.event.pull_request.head.ref == 'docs-update' && (github.event.pull_request.user.login == 'ChrisTitusTech' || github.event.pull_request.user.login == 'github-actions[bot]')
|
||||
if: (github.event.pull_request.head.ref == 'docs-update' || github.event.pull_request.head.ref == 'sponsors-update') && (github.event.pull_request.user.login == 'ChrisTitusTech' || github.event.pull_request.user.login == 'github-actions[bot]')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
@@ -18,10 +18,11 @@ jobs:
|
||||
- name: Extract Discussion Number & Close If any Were Found
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_BODY: ${{ github.event.pull_request.body }}
|
||||
if: github.event.pull_request.merged == true
|
||||
id: extract-discussion
|
||||
run: |
|
||||
pr_body="${{ github.event.pull_request.body }}"
|
||||
pr_body="$PR_BODY"
|
||||
discussion_ids=$(echo "$pr_body" | grep -oP '(?i)(resolve|fix|close)[s|d]? #\K[0-9]+')
|
||||
|
||||
if [ -z "$discussion_ids" ]; then
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
name: Issue slash commands
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
jobs:
|
||||
issueCommands:
|
||||
# Skip this job if the comment was created/edited on a PR
|
||||
if: ${{ !github.event.issue.pull_request }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: none
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Process slash command
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const allowedUsers = ["ChrisTitusTech", "og-mrk", "Marterich", "MyDrift-user", "Real-MullaC", "CodingWonders", "GabiNun2", "FluffyPunk"];
|
||||
const commenter = context.payload.comment.user.login;
|
||||
|
||||
// Authorization check first — before any parsing of comment content
|
||||
if (!allowedUsers.includes(commenter)) {
|
||||
console.log(`User ${commenter} is not in the allowlist. Skipping.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read comment body as data, never interpolated into shell
|
||||
const body = context.payload.comment.body;
|
||||
const issueNumber = context.issue.number;
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
|
||||
// /label 'name' or /label name
|
||||
const labelMatch = body.match(/\/label\s+'([^']+)'|\/label\s+(\S+?)(?:\s|$)/);
|
||||
if (labelMatch) {
|
||||
const labelName = (labelMatch[1] || labelMatch[2]).trim();
|
||||
console.log(`Adding label: ${labelName}`);
|
||||
await github.rest.issues.addLabels({
|
||||
owner, repo, issue_number: issueNumber,
|
||||
labels: [labelName],
|
||||
});
|
||||
}
|
||||
|
||||
// /unlabel 'name' or /unlabel name
|
||||
const unlabelMatch = body.match(/\/unlabel\s+'([^']+)'|\/unlabel\s+(\S+?)(?:\s|$)/);
|
||||
if (unlabelMatch) {
|
||||
const labelName = (unlabelMatch[1] || unlabelMatch[2]).trim();
|
||||
console.log(`Removing label: ${labelName}`);
|
||||
await github.rest.issues.removeLabel({
|
||||
owner, repo, issue_number: issueNumber,
|
||||
name: labelName,
|
||||
});
|
||||
}
|
||||
|
||||
// /close (optionally with 'not planned')
|
||||
if (body.includes('/close')) {
|
||||
const stateReason = body.includes('not planned') ? 'not_planned' : 'completed';
|
||||
console.log(`Closing issue (reason: ${stateReason})`);
|
||||
await github.rest.issues.update({
|
||||
owner, repo, issue_number: issueNumber,
|
||||
state: 'closed',
|
||||
state_reason: stateReason,
|
||||
});
|
||||
}
|
||||
|
||||
// /open or /reopen
|
||||
if (body.includes('/open') || body.includes('/reopen')) {
|
||||
console.log('Reopening issue');
|
||||
await github.rest.issues.update({
|
||||
owner, repo, issue_number: issueNumber,
|
||||
state: 'open',
|
||||
});
|
||||
}
|
||||
@@ -109,6 +109,21 @@ jobs:
|
||||
config-name: release-drafter.yml
|
||||
version: ${{ env.VERSION }}
|
||||
|
||||
- name: Remove Automated Updates from Release Notes
|
||||
id: filter_notes
|
||||
shell: pwsh
|
||||
env:
|
||||
RELEASE_NOTES: ${{ steps.generate_notes.outputs.body }}
|
||||
run: |
|
||||
$filteredNotes = ($env:RELEASE_NOTES -split '\r?\n' | Where-Object {
|
||||
$_ -notmatch '(?i)Update (Generated Dev Docs|Sponsors)'
|
||||
}) -join "`n"
|
||||
|
||||
$delimiter = [guid]::NewGuid().ToString()
|
||||
"body<<$delimiter" >> $env:GITHUB_OUTPUT
|
||||
$filteredNotes >> $env:GITHUB_OUTPUT
|
||||
$delimiter >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Create and Upload Release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v3
|
||||
@@ -116,7 +131,7 @@ jobs:
|
||||
tag_name: ${{ env.VERSION }}
|
||||
name: Pre-Release ${{ env.VERSION }}
|
||||
body: |
|
||||
${{ steps.generate_notes.outputs.body }}
|
||||
${{ steps.filter_notes.outputs.body }}
|
||||
|
||||

|
||||
append_body: false
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
token: ${{ secrets.AUTO_MERGE }}
|
||||
commit-message: 'Update sponsors in README'
|
||||
title: 'chore: Update Sponsors README'
|
||||
body: 'Automated update of sponsors section'
|
||||
|
||||
@@ -189,5 +189,6 @@ When the user corrects an agent approach, add or tighten one concrete rule here
|
||||
- When the active log file is owned by `Start-Transcript`, do not call `Add-Content` against that file; write to host output so the transcript captures the line in the same log file without recording a terminating-error diagnostic.
|
||||
- Log install/uninstall package names and package-manager IDs before queuing background runspace work; do not rely on runspace host output for the package identity.
|
||||
- For Win11 Creator, start each new ISO modification in a fresh `WinUtil_Win11ISO_*` temp directory; existing-work detection is only for resuming/exporting already modified media.
|
||||
- For Win11 Creator driver injection, keep offline WIM servicing to one mount, one `/Add-Driver`, and one commit; do not export editions or run unrelated WIM cleanup, and reject damaged metadata before ISO export.
|
||||
- For Script Analyzer cleanup, fix actionable source warnings first and do not globally suppress accepted convention warnings such as plural names, `ShouldProcess` on UI helpers, `$global:sync`, or compile-time cross-file false positives.
|
||||
- For DNS DHCP reset, keep the cmdlet reset and explicitly set IPv4 and IPv6 DNS source to DHCP.
|
||||
|
||||
@@ -77,7 +77,7 @@ See https://github.com/ChrisTitusTech/winutil/blob/main/.github/CONTRIBUTING.md
|
||||
|
||||
These are the sponsors that help keep this project alive with monthly contributions.
|
||||
|
||||
<!-- sponsors --><a href="https://github.com/ysaito8015"><img src="https://github.com/ysaito8015.png" width="60px" alt="User avatar: Yusuke Saito" /></a><a href="https://github.com/dwelfusius"><img src="https://github.com/dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https://github.com/mews-se.png" width="60px" alt="User avatar: Martin Stockzell" /></a><a href="https://github.com/jdiegmueller"><img src="https://github.com/jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https://github.com/robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/paulsheets"><img src="https://github.com/paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https://github.com/djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https://github.com/anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https://github.com/FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https://github.com/DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https://github.com/DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https://github.com/KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/andrewpayne68"><img src="https://github.com/andrewpayne68.png" width="60px" alt="User avatar: Andrew P" /></a><a href="https://github.com/johanwildeboer"><img src="https://github.com/johanwildeboer.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/lukas346"><img src="https://github.com/lukas346.png" width="60px" alt="User avatar: Wook" /></a><a href="https://github.com/tsv31"><img src="https://github.com/tsv31.png" width="60px" alt="User avatar: Sorin" /></a><!-- sponsors -->
|
||||
<!-- sponsors --><a href="https://github.com/ysaito8015"><img src="https://github.com/ysaito8015.png" width="60px" alt="User avatar: Yusuke Saito" /></a><a href="https://github.com/dwelfusius"><img src="https://github.com/dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https://github.com/mews-se.png" width="60px" alt="User avatar: Martin Stockzell" /></a><a href="https://github.com/jdiegmueller"><img src="https://github.com/jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https://github.com/robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/paulsheets"><img src="https://github.com/paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https://github.com/djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https://github.com/anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https://github.com/FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https://github.com/DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https://github.com/DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https://github.com/KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/andrewpayne68"><img src="https://github.com/andrewpayne68.png" width="60px" alt="User avatar: Andrew P" /></a><a href="https://github.com/seanh1995"><img src="https://github.com/seanh1995.png" width="60px" alt="User avatar: Sean (ANGRYxScotsman)" /></a><a href="https://github.com/Abs313a"><img src="https://github.com/Abs313a.png" width="60px" alt="User avatar: Abs" /></a><!-- sponsors -->
|
||||
|
||||
---
|
||||
|
||||
|
||||
+12
-12
@@ -530,6 +530,15 @@
|
||||
"winget": "TechPowerUp.GPU-Z",
|
||||
"foss": false
|
||||
},
|
||||
"gsudo": {
|
||||
"category": "Pro Tools",
|
||||
"choco": "gsudo",
|
||||
"content": "gsudo",
|
||||
"description": "gsudo is a sudo equivalent for Windows. It allows you to run commands with elevated administrative privileges directly within the current console window.",
|
||||
"link": "https://github.com/gerardog/gsudo",
|
||||
"winget": "gerardog.gsudo",
|
||||
"foss": true
|
||||
},
|
||||
"helium": {
|
||||
"category": "Browsers",
|
||||
"choco": "helium",
|
||||
@@ -719,15 +728,6 @@
|
||||
"winget": "XBMCFoundation.Kodi",
|
||||
"foss": true
|
||||
},
|
||||
"lightshot": {
|
||||
"category": "Multimedia Tools",
|
||||
"choco": "lightshot",
|
||||
"content": "Lightshot",
|
||||
"description": "The fastest way to take a customizable screenshot.",
|
||||
"link": "https://app.prntscr.com/",
|
||||
"winget": "Skillbrains.Lightshot",
|
||||
"foss": false
|
||||
},
|
||||
"lazygit": {
|
||||
"category": "Development",
|
||||
"choco": "lazygit",
|
||||
@@ -1207,7 +1207,7 @@
|
||||
"processmonitor": {
|
||||
"category": "Microsoft Tools",
|
||||
"choco": "procexp",
|
||||
"content": "SysInternals Process Monitor",
|
||||
"content": "Process Monitor",
|
||||
"description": "SysInternals Process Monitor is an advanced monitoring tool that shows real-time file system, registry, and process/thread activity.",
|
||||
"link": "https://docs.microsoft.com/en-us/sysinternals/downloads/procmon",
|
||||
"winget": "Microsoft.Sysinternals.ProcessMonitor",
|
||||
@@ -1396,7 +1396,7 @@
|
||||
"tcpview": {
|
||||
"category": "Microsoft Tools",
|
||||
"choco": "tcpview",
|
||||
"content": "SysInternals TCPView",
|
||||
"content": "TCPView",
|
||||
"description": "SysInternals TCPView is a network monitoring tool that displays a detailed list of all TCP and UDP endpoints on your system.",
|
||||
"link": "https://docs.microsoft.com/en-us/sysinternals/downloads/tcpview",
|
||||
"winget": "Microsoft.Sysinternals.TCPView",
|
||||
@@ -1513,7 +1513,7 @@
|
||||
"ungoogled": {
|
||||
"category": "Browsers",
|
||||
"choco": "ungoogled-chromium",
|
||||
"content": "Ungoogled",
|
||||
"content": "Ungoogled Chromium",
|
||||
"description": "Ungoogled Chromium is a version of Chromium without Google's integration for enhanced privacy and control.",
|
||||
"link": "https://github.com/Eloston/ungoogled-chromium",
|
||||
"winget": "eloston.ungoogled-chromium",
|
||||
|
||||
+60
-30
@@ -4,203 +4,232 @@
|
||||
"Content": "Feedback Hub",
|
||||
"Description": "Allows users to submit bug reports, feature suggestions, and diagnostic data directly to Microsoft.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsFeedbackHub"
|
||||
"PackageId": "Microsoft.WindowsFeedbackHub",
|
||||
"StoreId": "9NBLGGH4R32N"
|
||||
},
|
||||
"WPFAppxMicrosoft_GetHelp": {
|
||||
"Category": "Microsoft Apps",
|
||||
"Content": "Get Help",
|
||||
"Description": "Provides access to automated troubleshooting guides, support documentation, and direct Microsoft customer assistance.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.GetHelp"
|
||||
"PackageId": "Microsoft.GetHelp",
|
||||
"StoreId": "9PKDZBMV1H3T"
|
||||
},
|
||||
"WPFAppxMicrosoft_OutlookForWindows": {
|
||||
"Category": "Microsoft Apps",
|
||||
"Content": "Outlook for Windows",
|
||||
"Description": "Provides modern email management, calendar scheduling, and contact organization features.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.OutlookForWindows"
|
||||
"PackageId": "Microsoft.OutlookForWindows",
|
||||
"StoreId": "9NRX63209R7B"
|
||||
},
|
||||
"WPFAppxMSTeams": {
|
||||
"Category": "Microsoft Apps",
|
||||
"Content": "Microsoft Teams",
|
||||
"Description": "Facilitates instant messaging, video conferencing, file sharing, and workspace collaboration.",
|
||||
"Panel": "0",
|
||||
"PackageId": "MSTeams"
|
||||
"PackageId": "MSTeams",
|
||||
"StoreId": "XP8BT8DW290MPQ"
|
||||
},
|
||||
"WPFAppxClipchamp_Clipchamp": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Clipchamp",
|
||||
"Description": "Provides a user-friendly video editor with built-in templates, effects, and timeline editing tools.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Clipchamp.Clipchamp"
|
||||
"PackageId": "Clipchamp.Clipchamp",
|
||||
"StoreId": "9P1J8S7CCWWT"
|
||||
},
|
||||
"WPFAppxMicrosoft_MicrosoftOfficeHub": {
|
||||
"Category": "Microsoft Apps",
|
||||
"Content": "Microsoft 365",
|
||||
"Description": "Serves as a centralized launcher and dashboard for accessing cloud-based Microsoft 365 apps and recent documents.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.MicrosoftOfficeHub"
|
||||
"PackageId": "Microsoft.MicrosoftOfficeHub",
|
||||
"StoreId": "9WZDNCRD29V9"
|
||||
},
|
||||
"WPFAppxMicrosoft_ZuneMusic": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Media Player",
|
||||
"Description": "Plays local audio and video files with modern playlist management and casting capabilities.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.ZuneMusic"
|
||||
"PackageId": "Microsoft.ZuneMusic",
|
||||
"StoreId": "9WZDNCRFJ3PT"
|
||||
},
|
||||
"WPFAppxMicrosoft_BingSearch": {
|
||||
"Category": "Bing & Web Services",
|
||||
"Content": "Bing Search",
|
||||
"Description": "Integrates Microsoft Bing search capabilities and web services directly into the operating system.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.BingSearch"
|
||||
"PackageId": "Microsoft.BingSearch",
|
||||
"StoreId": "9NZBF4GT040C"
|
||||
},
|
||||
"WPFAppxMicrosoftCorporationII_QuickAssist": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Quick Assist",
|
||||
"Description": "Enables secure remote technical support and screen sharing over an internet connection.",
|
||||
"Panel": "0",
|
||||
"PackageId": "MicrosoftCorporationII.QuickAssist"
|
||||
"PackageId": "MicrosoftCorporationII.QuickAssist",
|
||||
"StoreId": "9P7BP5VNWKX5"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsDevHome": {
|
||||
"Category": "Developer Tools",
|
||||
"Content": "Dev Home",
|
||||
"Description": "Provides a specialized dashboard for software developer environment setups, repository syncing, and hardware widgets.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.Windows.DevHome"
|
||||
"PackageId": "Microsoft.Windows.DevHome",
|
||||
"StoreId": "9N8MHTPHNGVV"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsCrossDevice": {
|
||||
"Category": "Microsoft Ecosystem",
|
||||
"Content": "Mobile Devices",
|
||||
"Description": "Manages system-level background connectivity with paired mobile devices. Removing this may disable cross-device features such as phone screen mirroring, file transfer, and mobile hotspot handoff integrated into Windows Settings.",
|
||||
"Panel": "0",
|
||||
"PackageId": "MicrosoftWindows.CrossDevice"
|
||||
"PackageId": "MicrosoftWindows.CrossDevice",
|
||||
"StoreId": "9NTXGKQ8P7N0"
|
||||
},
|
||||
"WPFAppxMicrosoft_Todos": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "To Do",
|
||||
"Description": "Creates, tracks, and synchronizes personal tasks, smart lists, and daily reminders.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.Todos"
|
||||
"PackageId": "Microsoft.Todos",
|
||||
"StoreId": "9NBLGGH5R558"
|
||||
},
|
||||
"WPFAppxMicrosoft_PowerAutomateDesktop": {
|
||||
"Category": "Developer Tools",
|
||||
"Content": "Power Automate",
|
||||
"Description": "Automates repetitive workflows and desktop tasks using low-code visual scripting.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.PowerAutomateDesktop"
|
||||
"PackageId": "Microsoft.PowerAutomateDesktop",
|
||||
"StoreId": "9NFTCH6J7FHV"
|
||||
},
|
||||
"WPFAppxMicrosoft_YourPhone": {
|
||||
"Category": "Microsoft Ecosystem",
|
||||
"Content": "Phone Link",
|
||||
"Description": "Synchronizes text messages, phone notifications, photos, and calls from a mobile device to the desktop.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.YourPhone"
|
||||
"PackageId": "Microsoft.YourPhone",
|
||||
"StoreId": "9NMPJ99VJBWV"
|
||||
},
|
||||
"WPFAppxMicrosoft_MicrosoftStickyNotes": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Sticky Notes",
|
||||
"Description": "Creates quick, floating text notes on the desktop that automatically sync across devices.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.MicrosoftStickyNotes"
|
||||
"PackageId": "Microsoft.MicrosoftStickyNotes",
|
||||
"StoreId": "9NBLGGH4QGHW"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsSoundRecorder": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Sound Recorder",
|
||||
"Description": "Records and trims live audio inputs with simple microphone adjustment controls.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsSoundRecorder"
|
||||
"PackageId": "Microsoft.WindowsSoundRecorder",
|
||||
"StoreId": "9WZDNCRFHWKN"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsAlarms": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Clock",
|
||||
"Description": "Features world clocks, alarms, countdown timers, stopwatches, and dedicated focus session tracking.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsAlarms"
|
||||
"PackageId": "Microsoft.WindowsAlarms",
|
||||
"StoreId": "9WZDNCRFJ3PR"
|
||||
},
|
||||
"WPFAppxMicrosoft_Paint": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Paint",
|
||||
"Description": "Provides built-in digital sketching, basic image editing, and pixel-level graphic manipulation tools.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.Paint"
|
||||
"PackageId": "Microsoft.Paint",
|
||||
"StoreId": "9PCFS5B6T72H"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsNotepad": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Notepad",
|
||||
"Description": "Provides a lightweight text editor with multi-tab support for plain text files and code snippets.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsNotepad"
|
||||
"PackageId": "Microsoft.WindowsNotepad",
|
||||
"StoreId": "9MSMLRH6LZF3"
|
||||
},
|
||||
"WPFAppxMicrosoft_ScreenSketch": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Snipping Tool",
|
||||
"Description": "Captures screenshots or screen recordings with built-in markup, image cropping, and optical character recognition (OCR).",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.ScreenSketch"
|
||||
"PackageId": "Microsoft.ScreenSketch",
|
||||
"StoreId": "9MZ95KL8MR0L"
|
||||
},
|
||||
"WPFAppxMicrosoft_Copilot": {
|
||||
"Category": "Bing & Web Services",
|
||||
"Content": "Copilot",
|
||||
"Description": "Launches the Microsoft AI companion for contextual answers, creative writing assistance, and intelligent web search.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.Copilot"
|
||||
"PackageId": "Microsoft.Copilot",
|
||||
"StoreId": "9NHT9RB2F4HD"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsCalculator": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Calculator",
|
||||
"Description": "Performs standard arithmetic, scientific operations, programming calculations, and unit conversions.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsCalculator"
|
||||
"PackageId": "Microsoft.WindowsCalculator",
|
||||
"StoreId": "9WZDNCRFHVN5"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsCamera": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Camera",
|
||||
"Description": "Captures photographs and records video files via connected webcams or imaging hardware.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.WindowsCamera"
|
||||
"PackageId": "Microsoft.WindowsCamera",
|
||||
"StoreId": "9WZDNCRFJBBG"
|
||||
},
|
||||
"WPFAppxMicrosoft_WindowsPhotos": {
|
||||
"Category": "Utilities & Productivity",
|
||||
"Content": "Photos",
|
||||
"Description": "Organizes, views, and crops local images with basic color adjustment and album creation tools.",
|
||||
"Panel": "0",
|
||||
"PackageId": "Microsoft.Windows.Photos"
|
||||
"PackageId": "Microsoft.Windows.Photos",
|
||||
"StoreId": "9WZDNCRFJBH4"
|
||||
},
|
||||
"WPFAppxMicrosoft_BingNews": {
|
||||
"Category": "Bing & Web Services",
|
||||
"Content": "News",
|
||||
"Description": "Aggregates breaking news headlines, personalized article feeds, and world current events.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.BingNews"
|
||||
"PackageId": "Microsoft.BingNews",
|
||||
"StoreId": "9WZDNCRFHVFW"
|
||||
},
|
||||
"WPFAppxMicrosoft_BingWeather": {
|
||||
"Category": "Bing & Web Services",
|
||||
"Content": "Weather",
|
||||
"Description": "Displays local real-time weather tracking, radar maps, and historical meteorological forecasts.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.BingWeather"
|
||||
"PackageId": "Microsoft.BingWeather",
|
||||
"StoreId": "9WZDNCRFJ3Q2"
|
||||
},
|
||||
"WPFAppxMicrosoft_GamingApp": {
|
||||
"Category": "Xbox & Gaming",
|
||||
"Content": "Xbox App",
|
||||
"Description": "Serves as the primary gaming library manager, social community interface, and PC Game Pass dashboard.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.GamingApp"
|
||||
"PackageId": "Microsoft.GamingApp",
|
||||
"StoreId": "9MV0B5HZVK9Z"
|
||||
},
|
||||
"WPFAppxMicrosoft_XboxGamingOverlay": {
|
||||
"Category": "Xbox & Gaming",
|
||||
"Content": "Xbox Game Bar",
|
||||
"Description": "Provides customizable in-game status widgets, audio balancing sliders, system monitoring tools, and gameplay recording.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.XboxGamingOverlay"
|
||||
"PackageId": "Microsoft.XboxGamingOverlay",
|
||||
"StoreId": "9NZKPSTSNW4P"
|
||||
},
|
||||
"WPFAppxMicrosoft_XboxIdentityProvider": {
|
||||
"Category": "Xbox & Gaming",
|
||||
"Content": "Xbox Identity Provider",
|
||||
"Description": "Manages Xbox network user authentication and background account validation for connected titles. Warning: removing this may break Microsoft account sign-in for non-Xbox games and apps that rely on this authentication pipeline.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.XboxIdentityProvider"
|
||||
"PackageId": "Microsoft.XboxIdentityProvider",
|
||||
"StoreId": "9WZDNCRD1HKW"
|
||||
},
|
||||
"WPFAppxMicrosoft_XboxSpeechToTextOverlay": {
|
||||
"Category": "Xbox & Gaming",
|
||||
@@ -221,7 +250,8 @@
|
||||
"Content": "Start Experiences App",
|
||||
"Description": "Powers the Windows Widgets board, delivering a personalized feed of news, weather, sports, and finance content.",
|
||||
"Panel": "1",
|
||||
"PackageId": "Microsoft.StartExperiencesApp"
|
||||
"PackageId": "Microsoft.StartExperiencesApp",
|
||||
"StoreId": "9PC1H9VN18CM"
|
||||
},
|
||||
"WPFAppxMicrosoft_MicrosoftSolitaireCollection": {
|
||||
"Category": "Xbox & Gaming",
|
||||
|
||||
+16
-8
@@ -3,48 +3,56 @@
|
||||
"Primary": "8.8.8.8",
|
||||
"Secondary": "8.8.4.4",
|
||||
"Primary6": "2001:4860:4860::8888",
|
||||
"Secondary6": "2001:4860:4860::8844"
|
||||
"Secondary6": "2001:4860:4860::8844",
|
||||
"DohTemplate": "https://dns.google/dns-query"
|
||||
},
|
||||
"Cloudflare":{
|
||||
"Primary": "1.1.1.1",
|
||||
"Secondary": "1.0.0.1",
|
||||
"Primary6": "2606:4700:4700::1111",
|
||||
"Secondary6": "2606:4700:4700::1001"
|
||||
"Secondary6": "2606:4700:4700::1001",
|
||||
"DohTemplate": "https://cloudflare-dns.com/dns-query"
|
||||
},
|
||||
"Cloudflare_Malware":{
|
||||
"Primary": "1.1.1.2",
|
||||
"Secondary": "1.0.0.2",
|
||||
"Primary6": "2606:4700:4700::1112",
|
||||
"Secondary6": "2606:4700:4700::1002"
|
||||
"Secondary6": "2606:4700:4700::1002",
|
||||
"DohTemplate": "https://security.cloudflare-dns.com/dns-query"
|
||||
},
|
||||
"Cloudflare_Malware_Adult":{
|
||||
"Primary": "1.1.1.3",
|
||||
"Secondary": "1.0.0.3",
|
||||
"Primary6": "2606:4700:4700::1113",
|
||||
"Secondary6": "2606:4700:4700::1003"
|
||||
"Secondary6": "2606:4700:4700::1003",
|
||||
"DohTemplate": "https://family.cloudflare-dns.com/dns-query"
|
||||
},
|
||||
"Open_DNS":{
|
||||
"Primary": "208.67.222.222",
|
||||
"Secondary": "208.67.220.220",
|
||||
"Primary6": "2620:119:35::35",
|
||||
"Secondary6": "2620:119:53::53"
|
||||
"Secondary6": "2620:119:53::53",
|
||||
"DohTemplate": "https://doh.opendns.com/dns-query"
|
||||
},
|
||||
"Quad9":{
|
||||
"Primary": "9.9.9.9",
|
||||
"Secondary": "149.112.112.112",
|
||||
"Primary6": "2620:fe::fe",
|
||||
"Secondary6": "2620:fe::9"
|
||||
"Secondary6": "2620:fe::9",
|
||||
"DohTemplate": "https://dns.quad9.net/dns-query"
|
||||
},
|
||||
"AdGuard_Ads_Trackers":{
|
||||
"Primary": "94.140.14.14",
|
||||
"Secondary": "94.140.15.15",
|
||||
"Primary6": "2a10:50c0::ad1:ff",
|
||||
"Secondary6": "2a10:50c0::ad2:ff"
|
||||
"Secondary6": "2a10:50c0::ad2:ff",
|
||||
"DohTemplate": "https://dns.adguard-dns.com/dns-query"
|
||||
},
|
||||
"AdGuard_Ads_Trackers_Malware_Adult":{
|
||||
"Primary": "94.140.14.15",
|
||||
"Secondary": "94.140.15.16",
|
||||
"Primary6": "2a10:50c0::bad1:ff",
|
||||
"Secondary6": "2a10:50c0::bad2:ff"
|
||||
"Secondary6": "2a10:50c0::bad2:ff",
|
||||
"DohTemplate": "https://family.adguard-dns.com/dns-query"
|
||||
}
|
||||
}
|
||||
|
||||
+52
-8
@@ -179,6 +179,17 @@
|
||||
"function": "Invoke-WPFFixesWinget",
|
||||
"link": "https://winutil.christitus.com/dev/features/fixes/winget"
|
||||
},
|
||||
"WPFPanelComputer": {
|
||||
"Content": "Computer Management",
|
||||
"category": "Legacy Windows Panels",
|
||||
"panel": "2",
|
||||
"Type": "Button",
|
||||
"ButtonWidth": "300",
|
||||
"InvokeScript": [
|
||||
"compmgmt.msc"
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/computer"
|
||||
},
|
||||
"WPFPanelControl": {
|
||||
"Content": "Control Panel",
|
||||
"category": "Legacy Windows Panels",
|
||||
@@ -190,16 +201,16 @@
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/control"
|
||||
},
|
||||
"WPFPanelComputer": {
|
||||
"Content": "Computer Management",
|
||||
"WPFPanelMouse": {
|
||||
"Content": "Mouse Properties",
|
||||
"category": "Legacy Windows Panels",
|
||||
"panel": "2",
|
||||
"Type": "Button",
|
||||
"ButtonWidth": "300",
|
||||
"InvokeScript": [
|
||||
"compmgmt.msc"
|
||||
"main.cpl"
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/computer"
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/mouse"
|
||||
},
|
||||
"WPFPanelNetwork": {
|
||||
"Content": "Network Connections",
|
||||
@@ -234,6 +245,17 @@
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/printer"
|
||||
},
|
||||
"WPFPanelPrograms": {
|
||||
"Content": "Programs and Features",
|
||||
"category": "Legacy Windows Panels",
|
||||
"panel": "2",
|
||||
"Type": "Button",
|
||||
"ButtonWidth": "300",
|
||||
"InvokeScript": [
|
||||
"appwiz.cpl"
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/programs"
|
||||
},
|
||||
"WPFPanelRegion": {
|
||||
"Content": "Region",
|
||||
"category": "Legacy Windows Panels",
|
||||
@@ -245,16 +267,16 @@
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/region"
|
||||
},
|
||||
"WPFPanelRestore": {
|
||||
"Content": "Windows Restore",
|
||||
"WPFPanelSecurity": {
|
||||
"Content": "Security and Maintenance",
|
||||
"category": "Legacy Windows Panels",
|
||||
"panel": "2",
|
||||
"Type": "Button",
|
||||
"ButtonWidth": "300",
|
||||
"InvokeScript": [
|
||||
"rstrui.exe"
|
||||
"wscui.cpl"
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/restore"
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/security"
|
||||
},
|
||||
"WPFPanelSound": {
|
||||
"Content": "Sound Settings",
|
||||
@@ -289,6 +311,28 @@
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/timedate"
|
||||
},
|
||||
"WPFPanelFirewall": {
|
||||
"Content": "Windows Defender Firewall",
|
||||
"category": "Legacy Windows Panels",
|
||||
"panel": "2",
|
||||
"Type": "Button",
|
||||
"ButtonWidth": "300",
|
||||
"InvokeScript": [
|
||||
"firewall.cpl"
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/firewall"
|
||||
},
|
||||
"WPFPanelRestore": {
|
||||
"Content": "Windows Restore",
|
||||
"category": "Legacy Windows Panels",
|
||||
"panel": "2",
|
||||
"Type": "Button",
|
||||
"ButtonWidth": "300",
|
||||
"InvokeScript": [
|
||||
"rstrui.exe"
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/features/legacy-windows-panels/restore"
|
||||
},
|
||||
"WPFWinUtilInstallPSProfile": {
|
||||
"Content": "CTT PowerShell Profile - Install",
|
||||
"category": "Powershell Profile Powershell 7+ Only",
|
||||
|
||||
+7
-10
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"shared": {
|
||||
"AppEntryWidth": "200",
|
||||
"AppEntryFontSize": "11",
|
||||
"AppEntryMargin": "1,0,1,0",
|
||||
"AppEntryBorderThickness": "0",
|
||||
"AppEntryWidth": "220",
|
||||
"AppEntryFontSize": "13.2",
|
||||
"AppEntryIconSize": "28",
|
||||
"AppEntryMargin": "3",
|
||||
"AppEntryBorderThickness": "1",
|
||||
"CustomDialogFontSize": "12",
|
||||
"CustomDialogFontSizeHeader": "14",
|
||||
"CustomDialogLogoSize": "25",
|
||||
@@ -24,7 +25,7 @@
|
||||
"IconFontSize": "14",
|
||||
"IconButtonSize": "35",
|
||||
"SettingsIconFontSize": "18",
|
||||
"CloseIconFontSize": "18",
|
||||
"CloseIconFontSize": "12",
|
||||
"GroupBorderBackgroundColor": "#232629",
|
||||
"ButtonFontSize": "12",
|
||||
"ButtonFontFamily": "Arial",
|
||||
@@ -45,7 +46,6 @@
|
||||
"AppInstallUnselectedColor": "#F7F7F7",
|
||||
"AppInstallHighlightedColor": "#CFCFCF",
|
||||
"AppInstallSelectedColor": "#C2C2C2",
|
||||
"AppInstallOverlayBackgroundColor": "#6A6D72",
|
||||
"ComboBoxForegroundColor": "#232629",
|
||||
"ComboBoxBackgroundColor": "#F7F7F7",
|
||||
"LabelboxForegroundColor": "#232629",
|
||||
@@ -59,7 +59,6 @@
|
||||
"ScrollBarDraggingColor": "#6A6D72",
|
||||
"ProgressBarForegroundColor": "#2E77FF",
|
||||
"ProgressBarBackgroundColor": "Transparent",
|
||||
"ProgressBarTextColor": "#232629",
|
||||
"ButtonInstallBackgroundColor": "#F7F7F7",
|
||||
"ButtonTweaksBackgroundColor": "#F7F7F7",
|
||||
"ButtonConfigBackgroundColor": "#F7F7F7",
|
||||
@@ -87,7 +86,6 @@
|
||||
"AppInstallUnselectedColor": "#232629",
|
||||
"AppInstallHighlightedColor": "#3C3C3C",
|
||||
"AppInstallSelectedColor": "#4C4C4C",
|
||||
"AppInstallOverlayBackgroundColor": "#2E3135",
|
||||
"ComboBoxForegroundColor": "#F7F7F7",
|
||||
"ComboBoxBackgroundColor": "#1E3747",
|
||||
"LabelboxForegroundColor": "#5BDCFF",
|
||||
@@ -99,9 +97,8 @@
|
||||
"ScrollBarBackgroundColor": "#2E3135",
|
||||
"ScrollBarHoverColor": "#3B4252",
|
||||
"ScrollBarDraggingColor": "#5E81AC",
|
||||
"ProgressBarForegroundColor": "#222222",
|
||||
"ProgressBarForegroundColor": "#6EFF72",
|
||||
"ProgressBarBackgroundColor": "Transparent",
|
||||
"ProgressBarTextColor": "#232629",
|
||||
"ButtonInstallBackgroundColor": "#222222",
|
||||
"ButtonTweaksBackgroundColor": "#333333",
|
||||
"ButtonConfigBackgroundColor": "#444444",
|
||||
|
||||
+18
-2
@@ -429,7 +429,7 @@
|
||||
},
|
||||
"WPFTweaksConsumerFeatures": {
|
||||
"Content": "ConsumerFeatures - Disable",
|
||||
"Description": "Windows will not automatically install any games, third-party apps, or application links from the Windows Store for the signed-in user. Some default Apps will be inaccessible (e.g. Phone Link).",
|
||||
"Description": "Stops promoted app installs and reduces app suggestions from Microsoft Store content.",
|
||||
"category": "Essential Tweaks",
|
||||
"panel": "1",
|
||||
"registry": [
|
||||
@@ -650,7 +650,7 @@
|
||||
icacls $Env:OneDrive /deny \"Administrators:(D,DC)\"
|
||||
|
||||
Write-Host \"Uninstalling OneDrive...\"
|
||||
Start-Process '$Env:SystemRoot\\System32\\OneDriveSetup.exe' -ArgumentList '/uninstall' -Wait
|
||||
Start-Process -FilePath (Join-Path $Env:SystemRoot \"System32\\OneDriveSetup.exe\") -ArgumentList '/uninstall' -Wait
|
||||
|
||||
# Some of OneDrive files use explorer, and OneDrive uses FileCoAuth
|
||||
Write-Host \"Removing leftover OneDrive Files...\"
|
||||
@@ -944,6 +944,22 @@
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/tweaks/essential-tweaks/wpbt"
|
||||
},
|
||||
"WPFTweaksPreventDeviceMetadataFromNetwork": {
|
||||
"Content": "Prevent Device Companion Apps",
|
||||
"Description": "Prevents additional software from being installed when plugging in devices (e.g. Ads when plugging in a monitor). Poses potential security risk.",
|
||||
"category": "Essential Tweaks",
|
||||
"panel": "1",
|
||||
"registry": [
|
||||
{
|
||||
"Path": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Device Metadata",
|
||||
"Name": "PreventDeviceMetadataFromNetwork",
|
||||
"Value": "1",
|
||||
"Type": "DWord",
|
||||
"OriginalValue": "<RemoveEntry>"
|
||||
}
|
||||
],
|
||||
"link": "https://winutil.christitus.com/dev/tweaks/essential-tweaks/preventdevicemetadatafromnetwork"
|
||||
},
|
||||
"WPFTweaksRazerBlock": {
|
||||
"Content": "Razer Software Auto-Install - Disable",
|
||||
"Description": "Blocks ALL Razer Software installations. The hardware works fine without any software.",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 194 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
+88
-119
@@ -2,152 +2,121 @@
|
||||
toc: true
|
||||
---
|
||||
|
||||
## Contributing Code
|
||||
# How to Contribute?
|
||||
|
||||
### Before You Start
|
||||
## Testing
|
||||
|
||||
- Keep each pull request focused on a single feature or fix.
|
||||
- Avoid unnecessary formatting changes or large unrelated edits.
|
||||
- Document what changed and why in your PR description.
|
||||
* Test the latest changes to WinUtil by running the pre-release and reporting issues you are encountering to help us continually improve WinUtil!
|
||||
|
||||
---
|
||||
#### Run the latest pre-release
|
||||
|
||||
## Basic Git Workflow
|
||||
|
||||
### 1. Fork the Repository
|
||||
|
||||
Go to the ChrisTitusTech/winutil repository on GitHub and click the Fork button in the top right corner.
|
||||
|
||||
<img width="171" height="50" alt="{650A4723-F38A-44A4-9820-D232BC87C8A0}" src="https://github.com/user-attachments/assets/a214f27c-2fee-444a-920f-d87b14f5896f" />
|
||||
|
||||
---
|
||||
|
||||
### 2. Clone Your Fork
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/winutil.git
|
||||
cd winutil
|
||||
```ps1
|
||||
irm https://christitus.com/windev | iex
|
||||
```
|
||||
|
||||
---
|
||||
> [!IMPORTANT]
|
||||
> **Keep in mind** That this is a pre-release and should be treated as such. It exists for developers to test the utility and report or fix bugs before they get added to the stable release. Don't use it in production!
|
||||
|
||||
### 3. Create a Branch
|
||||
## Issues
|
||||
|
||||
Never work directly on `main`.
|
||||
* If you encounter any challenges or problems with the script, I kindly request that you submit them via the "Issues" tab on the GitHub repository. By filling out the provided template, you can provide specific details about the issue, allowing me (and others in the community) to promptly address any bugs or consider feature requests.
|
||||
|
||||
Create a branch related to your change:
|
||||
## Contribute Code
|
||||
|
||||
```bash
|
||||
git checkout -b feature-name
|
||||
* Pull requests are now handled directly on the **MAIN branch**. This was done since we can now select specific releases to launch via releases in GitHub.
|
||||
|
||||
* If you're doing code changes, then you can submit a PR to the `main` branch.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Do not use a code formatter, make massive amounts of line changes, or make multiple feature changes. EACH FEATURE CHANGE SHOULD BE IT'S OWN PULL REQUEST!
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Do not open a pull request that adds support for other languages to WinUtil for now, until we decide how we want to move forward with language support.
|
||||
|
||||
* When creating pull requests, it is essential to thoroughly document all changes made. This includes, but is not limited to, documenting any additions made to the `tweaks` section and corresponding `undo tweak`, so users are able to remove the newly added tweaks if necessary, and comprehensive documentation is required for all code changes. Document your changes and briefly explain why you made your changes in your Pull Request Description. Failure to adhere to this format may result in the denial of the pull request. Additionally, any code lacking sufficient documentation may also be denied.
|
||||
|
||||
* By following these guidelines, we can maintain a high standard of quality and ensure that the codebase remains organized and well-documented.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> When creating a function, please include "WPF" or "WinUtil" in the file name so it can be loaded into the runspace.
|
||||
|
||||
## Walk through
|
||||
|
||||
* This is a guide for beginners. If you are still having issues, look at the following official GitHub documentation:
|
||||
* [Commit through WEB](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/about-commits)
|
||||
* [Commit through GitHub Desktop](https://docs.github.com/en/desktop/making-changes-in-a-branch/committing-and-reviewing-changes-to-your-project-in-github-desktop#about-commits)
|
||||
* [Create a Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request)
|
||||
|
||||
|
||||
### Overview
|
||||
|
||||
```mermaid
|
||||
%%{init: {"flowchart": {"curve": "cardinal"}} }%%
|
||||
graph TD
|
||||
A[Fork Project] --> B[Clone Repository];
|
||||
B --> C[Create New Branch];
|
||||
C --> D[Make Changes];
|
||||
D --> G[Test Changes];
|
||||
G --> H{Tests Passed?};
|
||||
H -->|Yes| E[Commit Changes];
|
||||
H -->|No| J[Fix Issues];
|
||||
J --> G;
|
||||
E --> F[Push Branch];
|
||||
F --> K[Create Pull Request];
|
||||
K --> L[Fill out PR template];
|
||||
classDef default stroke:#333,stroke-width:4px,font-size:12pt;
|
||||
```
|
||||
|
||||
Example:
|
||||
> [!NOTE]
|
||||
>
|
||||
> This is a diagram to guide you through the process. It may vary depending on the type of change you're making.
|
||||
|
||||
```bash
|
||||
git checkout -b add-firefox-tweak
|
||||
```
|
||||
### Fork the Repo
|
||||
* Fork the WinUtil Repository [here](https://github.com/ChrisTitusTech/winutil) to create a copy that will be available in your repository list.
|
||||
|
||||
---
|
||||
{{< image src="images/Fork-Button" alt="Fork Image" >}}
|
||||
|
||||
### 4. Edit the Code
|
||||
### Clone the Fork
|
||||
> [!TIP]
|
||||
>
|
||||
> While you can make your changes directly through the Web, we recommend cloning the repo to your device using the application GitHub Desktop (available in WinUtil) to test your fork easily.
|
||||
|
||||
Open the project in your preferred text editor and make your changes.
|
||||
* Install GitHub Desktop if it is not already installed.
|
||||
* Log in using the same GitHub account you used to fork WinUtil.
|
||||
* Choose the fork under "Your Repositories" and press "clone {repo name}"
|
||||
* Create a new branch and name it something relatable to your changes.
|
||||
|
||||
Keep changes small and focused.
|
||||
* Now you can modify WinUtil to your liking using your preferred text editor.
|
||||
|
||||
---
|
||||
|
||||
### 5. Test Your Changes
|
||||
### Testing your changes
|
||||
|
||||
Open PowerShell as Administrator.
|
||||
* To test to see if your changes work as intended, run the following commands in a PowerShell terminal as admin:
|
||||
|
||||
Go to the project folder:
|
||||
* Change the directory where you are running the commands to the forked project.
|
||||
* `cd {path to the folder with the compile.ps1}`
|
||||
* Run the following command to compile and run WinUtil:
|
||||
* `.\Compile.ps1 -run`
|
||||
|
||||
```powershell
|
||||
cd path\to\winutil
|
||||
```
|
||||
{{< image src="images/Complie" alt="Compile" >}}
|
||||
|
||||
Run:
|
||||
* After seeing that your changes work properly, feel free to commit the changes to the repository and make a PR. For help on that, follow the documentation below.
|
||||
|
||||
```powershell
|
||||
.\Compile.ps1 -Run
|
||||
```
|
||||
### Committing the changes
|
||||
* Before committing your changes, please discard changes made to the `winutil.ps1` file, like the following:
|
||||
|
||||
Verify:
|
||||
{{< image src="images/Discard-GHD" alt="Push Commit Image" >}}
|
||||
|
||||
- WinUtil launches correctly
|
||||
- Your feature works
|
||||
- Nothing else breaks
|
||||
* Now, commit your changes once you are happy with the result.
|
||||
|
||||
If something fails, fix it before committing.
|
||||
{{< image src="images/Commit-GHD" alt="Commit Image" >}}
|
||||
|
||||
---
|
||||
* Push the changes to upload them to your fork on github.com.
|
||||
|
||||
### 6. Review Your Changes
|
||||
{{< image src="images/Push-Commit" alt="Push Commit Image" >}}
|
||||
|
||||
Check what changed:
|
||||
|
||||
```bash
|
||||
git status
|
||||
```
|
||||
|
||||
Review the diff:
|
||||
|
||||
```bash
|
||||
git diff
|
||||
```
|
||||
|
||||
Make sure you did not accidentally modify unrelated files.
|
||||
|
||||
---
|
||||
|
||||
### 7. Commit Your Changes
|
||||
|
||||
Stage files:
|
||||
|
||||
```bash
|
||||
git add .
|
||||
```
|
||||
|
||||
Commit them:
|
||||
|
||||
```bash
|
||||
git commit -m "Add feature description"
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
git commit -m "Add Firefox package tweak"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. Push Your Branch
|
||||
|
||||
```bash
|
||||
git push origin branch-name
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
git push origin add-firefox-tweak
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 9. Open a Pull Request
|
||||
|
||||
Go to your fork on GitHub.
|
||||
|
||||
GitHub will show a button to create a pull request.
|
||||
<img width="1009" height="71" alt="{C8C6A3CC-79D4-44FD-A54C-4C5717F12730}" src="https://github.com/user-attachments/assets/0419d193-d4e7-47c0-87cf-b986742201a0" />
|
||||
|
||||
Before submitting:
|
||||
|
||||
- Explain what changed
|
||||
- Explain why you changed it
|
||||
- Make sure unrelated files are not included
|
||||
|
||||
Once submitted, maintainers will review your PR.
|
||||
### Making a PR
|
||||
* To make a PR on your repo under a new branch linking to the main branch, a button will show and say Preview and Create pull request. Click that button and fill in all the information that is provided on the template. Once all the information is filled in correctly, check your PR to make sure there is no WinUtil.ps1 file attached to the PR. Once everything is good, make the PR and wait for Chris (the maintainer) to accept or deny your PR. Once it is accepted by Chris, you will be able to see your changes in the "/windev" build.
|
||||
* If you do not see your feature in the main "/win" build, that is fine. All new changes go into the /windev build to make sure everything is working OK before going fully public.
|
||||
* Congratulations! You just submitted your first PR. Thank you so much for contributing to WinUtil.
|
||||
|
||||
@@ -5,7 +5,7 @@ toc: false
|
||||
---
|
||||
|
||||
> [!NOTE]
|
||||
> This section contains technical documentation for developers. For end-user documentation, see the [User Guide](../userguide/).
|
||||
> This section contains code-only references. For end-user documentation, see the [User Guide](../userguide/).
|
||||
|
||||
## Architecture
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Computer Management"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=193}
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=182}
|
||||
"WPFPanelComputer": {
|
||||
"Content": "Computer Management",
|
||||
"category": "Legacy Windows Panels",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Control Panel"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=182}
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=193}
|
||||
"WPFPanelControl": {
|
||||
"Content": "Control Panel",
|
||||
"category": "Legacy Windows Panels",
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "Windows Defender Firewall"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=314}
|
||||
"WPFPanelFirewall": {
|
||||
"Content": "Windows Defender Firewall",
|
||||
"category": "Legacy Windows Panels",
|
||||
"panel": "2",
|
||||
"Type": "Button",
|
||||
"ButtonWidth": "300",
|
||||
"InvokeScript": [
|
||||
"firewall.cpl"
|
||||
],
|
||||
```
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "Mouse Properties"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=204}
|
||||
"WPFPanelMouse": {
|
||||
"Content": "Mouse Properties",
|
||||
"category": "Legacy Windows Panels",
|
||||
"panel": "2",
|
||||
"Type": "Button",
|
||||
"ButtonWidth": "300",
|
||||
"InvokeScript": [
|
||||
"main.cpl"
|
||||
],
|
||||
```
|
||||
@@ -3,7 +3,7 @@ title: "Network Connections"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=204}
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=215}
|
||||
"WPFPanelNetwork": {
|
||||
"Content": "Network Connections",
|
||||
"category": "Legacy Windows Panels",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Power Panel"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=215}
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=226}
|
||||
"WPFPanelPower": {
|
||||
"Content": "Power Panel",
|
||||
"category": "Legacy Windows Panels",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Printer Panel"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=226}
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=237}
|
||||
"WPFPanelPrinter": {
|
||||
"Content": "Printer Panel",
|
||||
"category": "Legacy Windows Panels",
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "Programs and Features"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=248}
|
||||
"WPFPanelPrograms": {
|
||||
"Content": "Programs and Features",
|
||||
"category": "Legacy Windows Panels",
|
||||
"panel": "2",
|
||||
"Type": "Button",
|
||||
"ButtonWidth": "300",
|
||||
"InvokeScript": [
|
||||
"appwiz.cpl"
|
||||
],
|
||||
```
|
||||
@@ -3,7 +3,7 @@ title: "Region"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=237}
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=259}
|
||||
"WPFPanelRegion": {
|
||||
"Content": "Region",
|
||||
"category": "Legacy Windows Panels",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Windows Restore"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=248}
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=325}
|
||||
"WPFPanelRestore": {
|
||||
"Content": "Windows Restore",
|
||||
"category": "Legacy Windows Panels",
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "Security and Maintenance"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=270}
|
||||
"WPFPanelSecurity": {
|
||||
"Content": "Security and Maintenance",
|
||||
"category": "Legacy Windows Panels",
|
||||
"panel": "2",
|
||||
"Type": "Button",
|
||||
"ButtonWidth": "300",
|
||||
"InvokeScript": [
|
||||
"wscui.cpl"
|
||||
],
|
||||
```
|
||||
@@ -3,7 +3,7 @@ title: "Sound Settings"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=259}
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=281}
|
||||
"WPFPanelSound": {
|
||||
"Content": "Sound Settings",
|
||||
"category": "Legacy Windows Panels",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "System Properties"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=270}
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=292}
|
||||
"WPFPanelSystem": {
|
||||
"Content": "System Properties",
|
||||
"category": "Legacy Windows Panels",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Time and Date"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=281}
|
||||
```json {filename="config/feature.json",linenos=inline,linenostart=303}
|
||||
"WPFPanelTimedate": {
|
||||
"Content": "Time and Date",
|
||||
"category": "Legacy Windows Panels",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "System Tray Battery Percentage"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1252}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1268}
|
||||
"WPFToggleBatteryPercentage": {
|
||||
"Content": "System Tray Battery Percentage",
|
||||
"Description": "Shows numeric battery percentage next to the battery icon in the system tray.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Start Menu Bing Search"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1605}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1621}
|
||||
"WPFToggleBingSearch": {
|
||||
"Content": "Start Menu Bing Search",
|
||||
"Description": "Toggles Bing web search results in Windows Search.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Dark Theme for Windows"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1270}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1286}
|
||||
"WPFToggleDarkMode": {
|
||||
"Content": "Dark Theme for Windows",
|
||||
"Description": "Dark Mode for the system and applications.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "BSoD Verbose Mode"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1226}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1242}
|
||||
"WPFToggleDetailedBSoD": {
|
||||
"Content": "BSoD Verbose Mode",
|
||||
"Description": "Gives more information when you blue screen.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Lock Screen - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1641}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1657}
|
||||
"WPFTweaksDisableLockscreen": {
|
||||
"Content": "Lock Screen - Disable",
|
||||
"Description": "Skips the lock screen entirely and goes directly to the sign-in screen on boot and wake.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Game Mode"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1784}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1800}
|
||||
"WPFToggleGameMode": {
|
||||
"Content": "Game Mode",
|
||||
"Description": "Toggles Windows prioritizes gaming performance by allocating system resources to games.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "File Explorer Hidden Files"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1340}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1356}
|
||||
"WPFToggleHiddenFiles": {
|
||||
"Content": "File Explorer Hidden Files",
|
||||
"Description": "Reveals hidden files in Explorer.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Settings Home Page"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1587}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1603}
|
||||
"WPFToggleHideSettingsHome": {
|
||||
"Content": "Settings Home Page",
|
||||
"Description": "Toggles the Home Page in the Windows Settings app.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Logon Screen Acrylic Blur"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1623}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1639}
|
||||
"WPFToggleLoginBlur": {
|
||||
"Content": "Logon Screen Acrylic Blur",
|
||||
"Description": "Toggles the acrylic blur effect on login screen background.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Enable Long Paths"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1810}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1826}
|
||||
"WPFToggleLongPaths": {
|
||||
"Content": "Enable Long Paths",
|
||||
"Description": "Toggles support for file paths longer than 260 characters in Explorer.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Mouse Acceleration"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1473}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1489}
|
||||
"WPFToggleMouseAcceleration": {
|
||||
"Content": "Mouse Acceleration",
|
||||
"Description": "Makes it so Cursor movement is affected by the speed of your physical mouse movements.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Multiplane Overlay"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1447}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1463}
|
||||
"WPFToggleMultiplaneOverlay": {
|
||||
"Content": "Multiplane Overlay",
|
||||
"Description": "Multiplane Overlay compose multiple image layers, which can sometimes cause issues with graphics cards.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Microsoft Outlook New Version"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1386}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1402}
|
||||
"WPFToggleNewOutlook": {
|
||||
"Content": "Microsoft Outlook New Version",
|
||||
"Description": "This will ensures the classic Outlook application is used.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Num Lock on Startup"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1507}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1523}
|
||||
"WPFToggleNumLock": {
|
||||
"Content": "Num Lock on Startup",
|
||||
"Description": "Toggle the Num Lock key state when your computer starts.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "S3 Sleep"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1569}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1585}
|
||||
"WPFToggleS3Sleep": {
|
||||
"Content": "S3 Sleep",
|
||||
"Description": "Toggles between Modern Standby and S3 Sleep, which cuts off power to the CPU while continuing to refresh the memory.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Scrollbars Always Visible"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1428}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1444}
|
||||
"WPFToggleScrollbars": {
|
||||
"Content": "Scrollbars Always Visible",
|
||||
"Description": "If enabled, scrollbars will always be visible. If disabled, Windows will automatically hide scrollbars when not in use.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "File Explorer File Extensions"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1312}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1328}
|
||||
"WPFToggleShowExt": {
|
||||
"Content": "File Explorer File Extensions",
|
||||
"Description": "Shows .file extensions in Explorer (.exe, .png, etc.)",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "S0 Sleep Network Connectivity"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1551}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1567}
|
||||
"WPFToggleStandbyFix": {
|
||||
"Content": "S0 Sleep Network Connectivity",
|
||||
"Description": "Toggles network connectivity during S0 Sleep which is low power idle in modern laptops.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Start Menu Recommendations"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1658}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1674}
|
||||
"WPFToggleStartMenuRecommendations": {
|
||||
"Content": "Start Menu Recommendations",
|
||||
"Description": "Toggles the recommendations section in the Start Menu. WARNING: This will also disable Windows Spotlight on your Lock Screen as a side effect.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Sticky Keys"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1702}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1718}
|
||||
"WPFToggleStickyKeys": {
|
||||
"Content": "Sticky Keys",
|
||||
"Description": "Toggles the Sticky Keys, which activate when clicking shift rapidly.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Taskbar Task View Icon"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1766}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1782}
|
||||
"WPFToggleTaskView": {
|
||||
"Content": "Taskbar Task View Icon",
|
||||
"Description": "Toggles the Task View Button in the Taskbar.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Taskbar Centered Icons"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1720}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1736}
|
||||
"WPFToggleTaskbarAlignment": {
|
||||
"Content": "Taskbar Centered Icons",
|
||||
"Description": "Toggles the Taskbar alignment either to the left or center.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Taskbar Search Icon"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1748}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1764}
|
||||
"WPFToggleTaskbarSearch": {
|
||||
"Content": "Taskbar Search Icon",
|
||||
"Description": "Toggles the Search Button on the Taskbar.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Logon Verbose Mode"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1368}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1384}
|
||||
"WPFToggleVerboseLogon": {
|
||||
"Content": "Logon Verbose Mode",
|
||||
"Description": "Show detailed messages during startup/shutdown.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Window Snapping"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1533}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1549}
|
||||
"WPFToggleWindowSnapping": {
|
||||
"Content": "Window Snapping",
|
||||
"Description": "Toggles the window snapping feature when dragging windows.",
|
||||
|
||||
@@ -6,7 +6,7 @@ description: ""
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=430}
|
||||
"WPFTweaksConsumerFeatures": {
|
||||
"Content": "ConsumerFeatures - Disable",
|
||||
"Description": "Windows will not automatically install any games, third-party apps, or application links from the Windows Store for the signed-in user. Some default Apps will be inaccessible (e.g. Phone Link).",
|
||||
"Description": "Stops promoted app installs and reduces app suggestions from Microsoft Store content.",
|
||||
"category": "Essential Tweaks",
|
||||
"panel": "1",
|
||||
"registry": [
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Temporary Files - Remove"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1066}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1082}
|
||||
"WPFTweaksDeleteTempFiles": {
|
||||
"Content": "Temporary Files - Remove",
|
||||
"Description": "Erases TEMP Folders.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "File Explorer Automatic Folder Discovery - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1171}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1187}
|
||||
"WPFTweaksDisableExplorerAutoDiscovery": {
|
||||
"Content": "File Explorer Automatic Folder Discovery - Disable",
|
||||
"Description": "Windows Explorer automatically tries to guess the type of the folder based on its contents, slowing down the browsing experience. WARNING! Will disable File Explorer grouping.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Disk Cleanup - Run"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1053}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1069}
|
||||
"WPFTweaksDiskCleanup": {
|
||||
"Content": "Disk Cleanup - Run",
|
||||
"Description": "Runs Disk Cleanup on Drive C: and removes old Windows Updates.",
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: "Prevent Device Companion Apps"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=947}
|
||||
"WPFTweaksPreventDeviceMetadataFromNetwork": {
|
||||
"Content": "Prevent Device Companion Apps",
|
||||
"Description": "Prevents additional software from being installed when plugging in devices (e.g. Ads when plugging in a monitor). Poses potential security risk.",
|
||||
"category": "Essential Tweaks",
|
||||
"panel": "1",
|
||||
"registry": [
|
||||
{
|
||||
"Path": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Device Metadata",
|
||||
"Name": "PreventDeviceMetadataFromNetwork",
|
||||
"Value": "1",
|
||||
"Type": "DWord",
|
||||
"OriginalValue": "<RemoveEntry>"
|
||||
}
|
||||
],
|
||||
```
|
||||
|
||||
## Registry Changes
|
||||
|
||||
Applications and System Components store and retrieve configuration data to modify Windows settings, so we can use the registry to change many settings in one place.
|
||||
|
||||
You can find information about the registry on [Wikipedia](https://en.wikipedia.org/wiki/Windows_Registry) and [Microsoft's Website](https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry).
|
||||
@@ -3,7 +3,7 @@ title: "Adobe URL Block List - Enable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1011}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1027}
|
||||
"WPFTweaksBlockAdobeNet": {
|
||||
"Content": "Adobe URL Block List - Enable",
|
||||
"Description": "Reduces user interruptions by selectively blocking connections to Adobe's activation and telemetry servers. Credit: Ruddernation-Designs",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Background Apps - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1139}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1155}
|
||||
"WPFTweaksDisableBGapps": {
|
||||
"Content": "Background Apps - Disable",
|
||||
"Description": "Disables all Microsoft Store apps from running in the background, which has to be done individually since Windows 11.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Fullscreen Optimizations - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1155}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1171}
|
||||
"WPFTweaksDisableFSO": {
|
||||
"Content": "Fullscreen Optimizations - Disable",
|
||||
"Description": "Disables FSO in all applications. NOTE: This will disable Color Management in Exclusive Fullscreen.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "IPv6 - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1117}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1133}
|
||||
"WPFTweaksDisableIPv6": {
|
||||
"Content": "IPv6 - Disable",
|
||||
"Description": "Disables IPv6.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "System Tray Notifications & Calendar - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=988}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1004}
|
||||
"WPFTweaksDisableNotifications": {
|
||||
"Content": "System Tray Notifications & Calendar - Disable",
|
||||
"Description": "Disables all Notifications INCLUDING Calendar.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "IPv6 - Set IPv4 as Preferred"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1079}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1095}
|
||||
"WPFTweaksIPv46": {
|
||||
"Content": "IPv6 - Set IPv4 as Preferred",
|
||||
"Description": "Setting the IPv4 preference can have latency and security benefits on private networks where IPv6 is not configured.",
|
||||
|
||||
@@ -5,15 +5,53 @@ description: ""
|
||||
|
||||
```powershell {filename="functions/public/Invoke-WPFOOSU.ps1",linenos=inline,linenostart=1}
|
||||
function Invoke-WPFOOSU {
|
||||
try {
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
if ($sync.ProcessRunning) {
|
||||
Show-WinUtilMessage -Message "Another process is currently running." -Title "WinUtil" -Button "OK" -Icon "Warning"
|
||||
return
|
||||
}
|
||||
|
||||
Invoke-WebRequest -Uri https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe -OutFile "$winutildir\ooshutup10.exe"
|
||||
Start-Process -FilePath "$winutildir\ooshutup10.exe"
|
||||
$downloadPath = Join-Path $sync.winutildir "ooshutup10.exe"
|
||||
$sync.ProcessRunning = $true
|
||||
|
||||
$ProgressPreference = 'Continue'
|
||||
} catch {
|
||||
Write-Error "Couldn't download O&O ShutUp10. Please make sure you have an active Internet connection."
|
||||
Invoke-WPFRunspace -ParameterList @(,("downloadPath", $downloadPath)) -ScriptBlock {
|
||||
param($downloadPath)
|
||||
|
||||
$hasUI = $null -ne $sync.Form -and $null -ne $sync.Form.Dispatcher
|
||||
|
||||
try {
|
||||
Write-WinUtilLog -Component "OOSU" -Message "Downloading O&O ShutUp10++."
|
||||
if ($hasUI) {
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Downloading O&O ShutUp10++ (0%)" -Percent 0
|
||||
}
|
||||
|
||||
Save-WinUtilFile -Uri "https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe" -DestinationPath $downloadPath -ProgressCallback {
|
||||
param($percent)
|
||||
|
||||
if ($hasUI) {
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Downloading O&O ShutUp10++ ($percent%)" -Percent $percent
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasUI) {
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Launching O&O ShutUp10++" -Percent 100
|
||||
}
|
||||
Start-Process -FilePath $downloadPath
|
||||
|
||||
Write-WinUtilLog -Component "OOSU" -Message "O&O ShutUp10++ launched."
|
||||
if ($hasUI) {
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "O&O ShutUp10++ launched" -Percent 100
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-WinUtilLog -Level "ERROR" -Component "OOSU" -Message "O&O ShutUp10++ download failed: $($_.Exception.Message)"
|
||||
if ($hasUI) {
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "O&O ShutUp10++ download failed" -Percent 100
|
||||
}
|
||||
Write-Error "Couldn't download O&O ShutUp10. Please make sure you have an active Internet connection."
|
||||
}
|
||||
finally {
|
||||
$sync.ProcessRunning = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Razer Software Auto-Install - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=947}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=963}
|
||||
"WPFTweaksRazerBlock": {
|
||||
"Content": "Razer Software Auto-Install - Disable",
|
||||
"Description": "Blocks ALL Razer Software installations. The hardware works fine without any software.",
|
||||
|
||||
@@ -15,7 +15,7 @@ description: ""
|
||||
icacls $Env:OneDrive /deny \"Administrators:(D,DC)\"
|
||||
|
||||
Write-Host \"Uninstalling OneDrive...\"
|
||||
Start-Process '$Env:SystemRoot\\System32\\OneDriveSetup.exe' -ArgumentList '/uninstall' -Wait
|
||||
Start-Process -FilePath (Join-Path $Env:SystemRoot \"System32\\OneDriveSetup.exe\") -ArgumentList '/uninstall' -Wait
|
||||
|
||||
# Some of OneDrive files use explorer, and OneDrive uses FileCoAuth
|
||||
Write-Host \"Removing leftover OneDrive Files...\"
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Right-Click Menu Previous Layout - Enable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1037}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1053}
|
||||
"WPFTweaksRightClickMenu": {
|
||||
"Content": "Right-Click Menu Previous Layout - Enable",
|
||||
"Description": "Restores the classic context menu when right-clicking in File Explorer, replacing the simplified Windows 11 version.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Teredo - Disable"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1095}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1111}
|
||||
"WPFTweaksTeredo": {
|
||||
"Content": "Teredo - Disable",
|
||||
"Description": "Teredo network tunneling is an IPv6 feature that can cause additional latency, but may cause problems with some games.",
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "DNS - Set to:"
|
||||
description: ""
|
||||
---
|
||||
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1835}
|
||||
```json {filename="config/tweaks.json",linenos=inline,linenostart=1851}
|
||||
"WPFchangedns": {
|
||||
"Content": "DNS - Set to:",
|
||||
"category": "z__Advanced Tweaks - CAUTION",
|
||||
|
||||
@@ -138,7 +138,7 @@ For a better Windows experience with minimal risk:
|
||||
|
||||
1. Check multiple application boxes
|
||||
2. All checked apps will install in sequence
|
||||
3. Progress is shown in the bottom panel
|
||||
3. Install and uninstall progress is shown in the window-level bottom panel, including the current package or package-manager batch and overall completion
|
||||
|
||||
### Applying Tweaks
|
||||
|
||||
|
||||
@@ -34,6 +34,14 @@ Use the quick-selection buttons at the top of the Tweaks tab to speed up setup:
|
||||
* **Select Tweaks to Remove**: Choose the tweaks you want to disable or remove.
|
||||
* **Undo Tweaks**: Click **Undo Selected Tweaks** at the bottom of the screen to apply the changes.
|
||||
|
||||
### AppX Packages
|
||||
|
||||
Open **AppX Removal** from the Tweaks tab to manage the listed Windows apps. Select one or more packages, then choose **Install Selected** or **Remove Selected**.
|
||||
|
||||
When installing a selected package, WinUtil first registers an existing local `AppxManifest.xml`. If no usable local manifest remains, WinUtil installs the package from the Microsoft Store through WinGet when a Store product ID is available.
|
||||
|
||||
During AppX installation or removal, the window-level progress bar shows the current package, completed package count, and overall progress. The Windows taskbar also reflects progress and the final success or failure state.
|
||||
|
||||
### Essential Tweaks
|
||||
Essential Tweaks are the safest starting point for most systems. They focus on lower-risk changes that improve usability, reduce noise, and avoid the more invasive changes found in advanced options.
|
||||
|
||||
|
||||
@@ -9,28 +9,30 @@ WinUtil provides three update modes so you can choose how aggressively Windows U
|
||||
|
||||
Changing modes adjusts system-wide Windows Update behavior. After switching modes, give Windows a moment to apply the policy and plan for a restart if the new state does not appear immediately.
|
||||
|
||||
{{< image src="images/updates-tab-new" alt="Updates tab in WinUtil" >}}
|
||||
- **Recommended**: Prioritizes stability while still receiving security updates
|
||||
- **Windows Default**: Restores standard Windows Update behavior
|
||||
- **Disable Updates**: Blocks Windows Update and should only be used with extreme caution
|
||||
|
||||
- **Default (Out of the Box) Settings**: Restores standard Windows Update behavior
|
||||
- **Security (Recommended) Settings**: Prioritizes stability while still receiving security updates
|
||||
- **Disable ALL Updates**: Turns off Windows Update entirely and should only be used with extreme caution
|
||||
### Windows Default
|
||||
|
||||
### Default (Out of Box) Settings
|
||||
|
||||
- **What it does**: Restores the default Windows Update configuration.
|
||||
- **What it does**: Removes Windows Update policies managed by WinUtil, restores update service startup settings, and re-enables update scheduled tasks.
|
||||
- **Best for**: Systems where you want Windows to manage updates normally.
|
||||
- **Notes**: This removes custom update settings previously applied by WinUtil. If update errors continue, use the reset option in the **Config** tab to restore Microsoft Update services to their default state.
|
||||
- **Notes**: Only values managed by WinUtil are removed; other Windows Update policies are left in place. If update errors continue, use the reset option in the **Config** tab to repair Microsoft Update components.
|
||||
|
||||
### Security (Recommended) Settings
|
||||
### Recommended
|
||||
|
||||
- **What it does**: Applies a more conservative update strategy designed for most users.
|
||||
- **Feature updates**: Delayed by **365 days** to reduce the chance of disruption from major Windows changes.
|
||||
- **Security updates**: Delayed by **4 days** to allow time for early issues to surface while still keeping the system protected.
|
||||
- **Quality updates**: Delayed by **4 days** to allow time for early issues to surface while still keeping the system protected.
|
||||
- **Drivers**: Excluded from Windows quality updates.
|
||||
- **Restarts**: Scheduled updates do not automatically restart Windows while a user is signed in. A restart explicitly scheduled by a user still takes precedence.
|
||||
- **Availability**: Update deferral policies apply to Windows Pro, Enterprise, and Education editions.
|
||||
- **Why use it**: This mode offers the best balance between security and stability, which is why it is the recommended option for most PCs.
|
||||
|
||||
### Disable ALL Updates (NOT RECOMMENDED!)
|
||||
### Disable Updates (NOT RECOMMENDED!)
|
||||
|
||||
- **What it does**: Disables all Windows updates.
|
||||
- **What it does**: Disables automatic update policy, stops and disables update services, disables update scheduled tasks, and clears downloaded update files.
|
||||
- **Best for**: Highly controlled or special-purpose systems where updates must remain off temporarily.
|
||||
- **Warning**: This leaves the system without security patches and significantly increases security risk.
|
||||
- **Notes**: Windows servicing can restore update components in some circumstances. Use **Restore Defaults** when you are ready to receive updates again.
|
||||
- **Recommendation**: Avoid this mode unless you fully understand the tradeoffs and have a specific reason to use it.
|
||||
|
||||
@@ -10,6 +10,9 @@ function Find-AppsByNameOrDescription {
|
||||
.PARAMETER SearchString
|
||||
The string to be searched for. Wildcards are treated as literal characters.
|
||||
|
||||
.PARAMETER Category
|
||||
When provided, only applications in this exact category are shown.
|
||||
|
||||
.NOTES
|
||||
- Uses module-scope $sync (no parameter needed; inherits from caller's scope)
|
||||
- Performs literal matching (no wildcard expansion)
|
||||
@@ -18,7 +21,10 @@ function Find-AppsByNameOrDescription {
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$SearchString = ""
|
||||
[string]$SearchString = "",
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Category = ""
|
||||
)
|
||||
|
||||
# Validate that $sync exists and has required structure
|
||||
@@ -39,7 +45,7 @@ function Find-AppsByNameOrDescription {
|
||||
|
||||
try {
|
||||
# Reset the visibility if the search string is empty or the search is cleared
|
||||
if ([string]::IsNullOrWhiteSpace($SearchString)) {
|
||||
if ([string]::IsNullOrWhiteSpace($SearchString) -and [string]::IsNullOrWhiteSpace($Category)) {
|
||||
$sync.ItemsControl.Items | ForEach-Object {
|
||||
# Each item is a StackPanel container
|
||||
$_.Visibility = [Windows.Visibility]::Visible
|
||||
@@ -94,10 +100,11 @@ function Find-AppsByNameOrDescription {
|
||||
|
||||
# Check if app matches search criteria
|
||||
if ($null -ne $appEntry) {
|
||||
$contentMatch = $appEntry.Content -like "*$escapedSearchString*"
|
||||
$descriptionMatch = $appEntry.Description -like "*$escapedSearchString*"
|
||||
$categoryMatch = -not [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Category -eq $Category
|
||||
$contentMatch = [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Content -like "*$escapedSearchString*"
|
||||
$descriptionMatch = [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Description -like "*$escapedSearchString*"
|
||||
|
||||
if ($contentMatch -or $descriptionMatch) {
|
||||
if ($categoryMatch -or $contentMatch -or $descriptionMatch) {
|
||||
# Show the App and mark that this category has a match
|
||||
$appControl.Visibility = [Windows.Visibility]::Visible
|
||||
$categoryHasMatch = $true
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
function Get-WinUtilInstalledAPPX {
|
||||
<#
|
||||
|
||||
.SYNOPSIS
|
||||
Gets the names of AppX packages installed for all users
|
||||
|
||||
#>
|
||||
|
||||
# AppX module auto-loading can leave PowerShell 7 dependent on a temporary Windows PowerShell
|
||||
# compatibility proxy. Run the query in Windows PowerShell 5.1 so it remains available after
|
||||
# those temporary proxy files are removed.
|
||||
$ps5Command = {
|
||||
Get-AppxPackage -AllUsers -ErrorAction Stop | Select-Object -ExpandProperty Name
|
||||
}
|
||||
|
||||
$packageOutput = powershell.exe -NoProfile -NonInteractive -Command $ps5Command 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
$failureDetails = ($packageOutput | Out-String).Trim()
|
||||
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message "Failed to get installed AppX packages: $failureDetails"
|
||||
return @()
|
||||
}
|
||||
|
||||
return @($packageOutput)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
function Hide-WPFInstallAppBusy {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Hides the busy overlay in the install app area of the WPF form.
|
||||
This is used to indicate that an install or uninstall has finished.
|
||||
#>
|
||||
Invoke-WPFUIThread -ScriptBlock {
|
||||
$sync.InstallAppAreaOverlay.Visibility = [Windows.Visibility]::Collapsed
|
||||
$sync.InstallAppAreaBorder.IsEnabled = $true
|
||||
$sync.InstallAppAreaScrollViewer.Effect.Radius = 0
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,6 @@
|
||||
This is used as the parent object for all category and app entries on the install tab
|
||||
Used to as part of the Install Tab UI generation
|
||||
|
||||
Also creates an overlay with a progress bar and text to indicate that an install or uninstall is in progress
|
||||
|
||||
.PARAMETER TargetElement
|
||||
The element to which the AppArea should be added
|
||||
|
||||
@@ -19,22 +17,14 @@
|
||||
$Border = New-Object Windows.Controls.Border
|
||||
$Border.VerticalAlignment = "Stretch"
|
||||
$Border.SetResourceReference([Windows.Controls.Control]::StyleProperty, "BorderStyle")
|
||||
$sync.InstallAppAreaBorder = $Border
|
||||
|
||||
# Add a ScrollViewer, because the ItemsControl does not support scrolling by itself
|
||||
$scrollViewer = New-Object Windows.Controls.ScrollViewer
|
||||
$scrollViewer.VerticalScrollBarVisibility = 'Auto'
|
||||
$scrollViewer.HorizontalAlignment = 'Stretch'
|
||||
$scrollViewer.VerticalAlignment = 'Stretch'
|
||||
$scrollViewer.CanContentScroll = $true
|
||||
$sync.InstallAppAreaScrollViewer = $scrollViewer
|
||||
$Border.Child = $scrollViewer
|
||||
|
||||
# Initialize the Blur Effect for the ScrollViewer, which will be used to indicate that an install/uninstall is in progress
|
||||
$blurEffect = New-Object Windows.Media.Effects.BlurEffect
|
||||
$blurEffect.Radius = 0
|
||||
$scrollViewer.Effect = $blurEffect
|
||||
|
||||
## Create the ItemsControl, which will be the parent of all the app entries
|
||||
$itemsControl = New-Object Windows.Controls.ItemsControl
|
||||
$itemsControl.HorizontalAlignment = 'Stretch'
|
||||
@@ -52,61 +42,5 @@
|
||||
# Add the Border containing the App Area to the target Grid
|
||||
$targetGrid.Children.Add($Border) | Out-Null
|
||||
|
||||
$overlay = New-Object Windows.Controls.Border
|
||||
$overlay.CornerRadius = New-Object Windows.CornerRadius(10)
|
||||
$overlay.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallOverlayBackgroundColor")
|
||||
$overlay.Visibility = [Windows.Visibility]::Collapsed
|
||||
|
||||
# Also add the overlay to the target Grid on top of the App Area
|
||||
$targetGrid.Children.Add($overlay) | Out-Null
|
||||
$sync.InstallAppAreaOverlay = $overlay
|
||||
|
||||
$overlayText = New-Object Windows.Controls.TextBlock
|
||||
$overlayText.Text = "Installing apps..."
|
||||
$overlayText.HorizontalAlignment = 'Center'
|
||||
$overlayText.VerticalAlignment = 'Center'
|
||||
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::ForegroundProperty, "MainForegroundColor")
|
||||
$overlayText.Background = "Transparent"
|
||||
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::FontSizeProperty, "HeaderFontSize")
|
||||
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::FontFamilyProperty, "MainFontFamily")
|
||||
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::FontWeightProperty, "MainFontWeight")
|
||||
$overlayText.SetResourceReference([Windows.Controls.TextBlock]::MarginProperty, "MainMargin")
|
||||
$sync.InstallAppAreaOverlayText = $overlayText
|
||||
|
||||
$progressbar = New-Object Windows.Controls.ProgressBar
|
||||
$progressbar.Name = "ProgressBar"
|
||||
$progressbar.Width = 250
|
||||
$progressbar.Height = 50
|
||||
$sync.ProgressBar = $progressbar
|
||||
|
||||
# Add a TextBlock overlay for the progress bar text
|
||||
$progressBarTextBlock = New-Object Windows.Controls.TextBlock
|
||||
$progressBarTextBlock.Name = "progressBarTextBlock"
|
||||
$progressBarTextBlock.FontWeight = [Windows.FontWeights]::Bold
|
||||
$progressBarTextBlock.FontSize = 16
|
||||
$progressBarTextBlock.Width = $progressbar.Width
|
||||
$progressBarTextBlock.Height = $progressbar.Height
|
||||
$progressBarTextBlock.SetResourceReference([Windows.Controls.TextBlock]::ForegroundProperty, "ProgressBarTextColor")
|
||||
$progressBarTextBlock.TextTrimming = "CharacterEllipsis"
|
||||
$progressBarTextBlock.Background = "Transparent"
|
||||
$sync.progressBarTextBlock = $progressBarTextBlock
|
||||
|
||||
# Create a Grid to overlay the text on the progress bar
|
||||
$progressGrid = New-Object Windows.Controls.Grid
|
||||
$progressGrid.Width = $progressbar.Width
|
||||
$progressGrid.Height = $progressbar.Height
|
||||
$progressGrid.Margin = "0,10,0,10"
|
||||
$progressGrid.Children.Add($progressbar) | Out-Null
|
||||
$progressGrid.Children.Add($progressBarTextBlock) | Out-Null
|
||||
|
||||
$overlayStackPanel = New-Object Windows.Controls.StackPanel
|
||||
$overlayStackPanel.Orientation = "Vertical"
|
||||
$overlayStackPanel.HorizontalAlignment = 'Center'
|
||||
$overlayStackPanel.VerticalAlignment = 'Center'
|
||||
$overlayStackPanel.Children.Add($overlayText) | Out-Null
|
||||
$overlayStackPanel.Children.Add($progressGrid) | Out-Null
|
||||
|
||||
$overlay.Child = $overlayStackPanel
|
||||
|
||||
return $itemsControl
|
||||
}
|
||||
|
||||
@@ -60,6 +60,30 @@ function Initialize-InstallAppEntry {
|
||||
$borderElement.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallUnselectedColor")
|
||||
})
|
||||
|
||||
$contentPanel = New-Object Windows.Controls.StackPanel
|
||||
$contentPanel.Orientation = "Horizontal"
|
||||
$contentPanel.VerticalAlignment = [Windows.VerticalAlignment]::Center
|
||||
|
||||
$icon = New-Object Windows.Controls.Grid
|
||||
$icon.SetResourceReference([Windows.FrameworkElement]::WidthProperty, "AppEntryIconSize")
|
||||
$icon.SetResourceReference([Windows.FrameworkElement]::HeightProperty, "AppEntryIconSize")
|
||||
$icon.Margin = New-Object Windows.Thickness(0, 0, 8, 0)
|
||||
$fallback = New-Object Windows.Controls.TextBlock
|
||||
$fallback.Text = $app.content.TrimStart(".").Substring(0, 1).ToUpper()
|
||||
$fallback.FontWeight = "Bold"; $fallback.HorizontalAlignment = "Center"; $fallback.VerticalAlignment = "Center"
|
||||
if ($app.link) { $fallback.Visibility = "Collapsed" }
|
||||
$fallback.SetResourceReference([Windows.Controls.TextBlock]::FontSizeProperty, "AppEntryFontSize")
|
||||
$fallback.SetResourceReference([Windows.Controls.TextBlock]::ForegroundProperty, "ToggleButtonOnColor")
|
||||
[void]$icon.Children.Add($fallback)
|
||||
if ($app.link) {
|
||||
$logo = New-Object Windows.Controls.Image
|
||||
$logo.Stretch = [Windows.Media.Stretch]::Uniform
|
||||
$logo.Source = "https://www.google.com/s2/favicons?sz=64&domain_url=$([uri]::EscapeDataString($app.link))"
|
||||
$logo.Add_ImageFailed({ $this.Visibility = "Collapsed"; $this.Parent.Children[0].Visibility = "Visible" })
|
||||
[void]$icon.Children.Add($logo)
|
||||
}
|
||||
[void]$contentPanel.Children.Add($icon)
|
||||
|
||||
# Create the TextBlock for the application name
|
||||
$appName = New-Object Windows.Controls.TextBlock
|
||||
$appName.Style = $sync.Form.Resources.AppEntryNameStyle
|
||||
@@ -73,7 +97,8 @@ function Initialize-InstallAppEntry {
|
||||
|
||||
[void]$appName.Inlines.Add($fossRun)
|
||||
}
|
||||
$checkBox.Content = $appName
|
||||
[void]$contentPanel.Children.Add($appName)
|
||||
$checkBox.Content = $contentPanel
|
||||
|
||||
# Add accessibility properties to make the elements screen reader friendly
|
||||
$checkBox.SetValue([Windows.Automation.AutomationProperties]::NameProperty, $app.content)
|
||||
|
||||
@@ -28,7 +28,7 @@ function Initialize-WinUtilTabContent {
|
||||
"AppX" {
|
||||
Invoke-WPFUIElements -configVariable $sync.configs.appx -targetGridName "appxpanel" -columncount 2
|
||||
}
|
||||
"Win11 Creator" {
|
||||
"Win11ISO" {
|
||||
if ($sync.Form -and $sync.Form.Dispatcher) {
|
||||
$sync.Form.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ Invoke-WinUtilISOCheckExistingWork }) | Out-Null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
function Install-WinUtilAPPX {
|
||||
<#
|
||||
|
||||
.SYNOPSIS
|
||||
Registers a local AppX package or installs it from the Microsoft Store
|
||||
|
||||
.PARAMETER Name
|
||||
The AppX package name to install
|
||||
|
||||
.PARAMETER StoreId
|
||||
The optional Microsoft Store product ID used when no local manifest is available
|
||||
|
||||
#>
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name,
|
||||
|
||||
[string]$StoreId
|
||||
)
|
||||
|
||||
Write-WinUtilLog -Component "AppX" -Message "Installing AppX package: $Name"
|
||||
|
||||
# AppX and DISM cmdlets are more reliable in Windows PowerShell 5.1. Query both installed and
|
||||
# provisioned package metadata because either can expose a local manifest that can be registered.
|
||||
$ps5Command = {
|
||||
$packageName = $args[0]
|
||||
$manifestPaths = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
Get-AppxPackage -AllUsers -Name $packageName -ErrorAction SilentlyContinue |
|
||||
Sort-Object -Property Version -Descending |
|
||||
ForEach-Object {
|
||||
if (-not [string]::IsNullOrWhiteSpace($_.InstallLocation)) {
|
||||
$manifestPaths.Add((Join-Path $_.InstallLocation "AppxManifest.xml"))
|
||||
}
|
||||
}
|
||||
|
||||
Get-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue |
|
||||
Where-Object DisplayName -EQ $packageName |
|
||||
ForEach-Object {
|
||||
if (-not [string]::IsNullOrWhiteSpace($_.InstallLocation)) {
|
||||
$manifestPaths.Add((Join-Path $_.InstallLocation "AppxManifest.xml"))
|
||||
}
|
||||
}
|
||||
|
||||
$manifestPath = $manifestPaths |
|
||||
Select-Object -Unique |
|
||||
Where-Object { Test-Path -LiteralPath $_ } |
|
||||
Select-Object -First 1
|
||||
|
||||
if ($null -ne $manifestPath) {
|
||||
Add-AppxPackage -Register $manifestPath -DisableDevelopmentMode -ErrorAction Stop
|
||||
Write-Output $manifestPath
|
||||
}
|
||||
}
|
||||
|
||||
$manifestOutput = powershell.exe -NoProfile -NonInteractive -Command $ps5Command -args $Name 2>&1
|
||||
if ($LASTEXITCODE -eq 0 -and $null -ne $manifestOutput) {
|
||||
$manifestPath = ($manifestOutput | Select-Object -Last 1).ToString().Trim()
|
||||
if (-not [string]::IsNullOrWhiteSpace($manifestPath)) {
|
||||
Write-WinUtilLog -Component "AppX" -Message "Registered local AppX manifest for $Name`: $manifestPath"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
$failureDetails = ($manifestOutput | Out-String).Trim()
|
||||
Write-WinUtilLog -Level "WARN" -Component "AppX" -Message "Local AppX registration failed for $Name`: $failureDetails"
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($StoreId)) {
|
||||
$errorMessage = "Unable to install $Name because no local manifest or Microsoft Store ID is available."
|
||||
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message $errorMessage
|
||||
throw $errorMessage
|
||||
}
|
||||
|
||||
Write-WinUtilLog -Component "AppX" -Message "No usable local manifest found for $Name. Installing Microsoft Store product $StoreId."
|
||||
Install-WinUtilWinget
|
||||
Install-WinUtilProgramWinget -Action Install -Programs @("msstore:$StoreId")
|
||||
}
|
||||
@@ -15,32 +15,36 @@ Function Invoke-WinUtilCurrentSystem {
|
||||
)
|
||||
if ($CheckBox -eq "choco") {
|
||||
$apps = (choco list | Select-String -Pattern "^\S+").Matches.Value
|
||||
$filter = Get-WinUtilVariables -Type Checkbox | Where-Object {$psitem -like "WPFInstall*"}
|
||||
$sync.GetEnumerator() | Where-Object {$psitem.Key -in $filter} | ForEach-Object {
|
||||
$dependencies = @($sync.configs.applications.$($psitem.Key).choco -split ";")
|
||||
if ($dependencies -in $apps) {
|
||||
Write-Output $psitem.name
|
||||
$sync.configs.applicationsHashtable.GetEnumerator() | ForEach-Object {
|
||||
$packageId = ($_.Value.choco -split ";")[-1].Trim()
|
||||
if ($packageId -ne "na" -and $packageId -in $apps) {
|
||||
Write-Output $_.Key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($checkbox -eq "winget") {
|
||||
|
||||
$originalEncoding = [Console]::OutputEncoding
|
||||
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
|
||||
$Sync.InstalledPrograms = @("winget", "msstore") | ForEach-Object {
|
||||
winget list -s $psitem | Select-Object -skip 3 | ConvertFrom-String -PropertyNames "Name", "Id", "Version", "Available" -Delimiter '\s{2,}'
|
||||
try {
|
||||
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
|
||||
$installedProgramOutput = @(winget list --accept-source-agreements --disable-interactivity 2>&1)
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "winget list failed with exit code $LASTEXITCODE."
|
||||
}
|
||||
} finally {
|
||||
[Console]::OutputEncoding = $originalEncoding
|
||||
}
|
||||
[Console]::OutputEncoding = $originalEncoding
|
||||
$installedProgramText = $installedProgramOutput -join "`n"
|
||||
|
||||
$filter = Get-WinUtilVariables -Type Checkbox | Where-Object {$psitem -like "WPFInstall*"}
|
||||
$sync.GetEnumerator() | Where-Object {$psitem.Key -in $filter} | ForEach-Object {
|
||||
$dependencies = @($sync.configs.applications.$($psitem.Key).winget -split ";") | ForEach-Object {
|
||||
$psitem -replace "^msstore:", ""
|
||||
$sync.configs.applicationsHashtable.GetEnumerator() | ForEach-Object {
|
||||
$packageId = (($_.Value.winget -split ";")[-1] -replace "^msstore:", "").Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($packageId) -or $packageId -eq "na") {
|
||||
return
|
||||
}
|
||||
|
||||
if ($dependencies[-1] -in $sync.InstalledPrograms.Id) {
|
||||
Write-Output $psitem.name
|
||||
$packagePattern = "(?im)[^\S\r\n]{2,}$([regex]::Escape($packageId))(?=[^\S\r\n]{2,}|$)"
|
||||
if ($installedProgramText -match $packagePattern) {
|
||||
Write-Output $_.Key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function Write-Win11ISOLog {
|
||||
function Write-WinUtilISOLog {
|
||||
param([string]$Message)
|
||||
$ts = (Get-Date).ToString("HH:mm:ss")
|
||||
$logLine = "[$ts] $Message"
|
||||
@@ -35,7 +35,7 @@ function Invoke-WinUtilISOBrowse {
|
||||
$sync["WPFWin11ISOModifySection"].Visibility = "Collapsed"
|
||||
$sync["WPFWin11ISOOutputSection"].Visibility = "Collapsed"
|
||||
|
||||
Write-Win11ISOLog "ISO selected: $isoPath ($fileSizeGB GB)"
|
||||
Write-WinUtilISOLog "ISO selected: $isoPath ($fileSizeGB GB)"
|
||||
}
|
||||
|
||||
function Invoke-WinUtilISOMountAndVerify {
|
||||
@@ -46,84 +46,102 @@ function Invoke-WinUtilISOMountAndVerify {
|
||||
return
|
||||
}
|
||||
|
||||
Write-Win11ISOLog "Mounting ISO: $isoPath"
|
||||
Set-WinUtilProgressBar -Label "Mounting ISO..." -Percent 10
|
||||
Write-WinUtilISOLog "Mounting ISO: $isoPath"
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Mounting ISO..." -Percent 10
|
||||
$sync["WPFWin11ISOBrowseButton"].IsEnabled = $false
|
||||
$sync["WPFWin11ISOMountButton"].IsEnabled = $false
|
||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $false
|
||||
$sync["Win11ISOProcessRunning"] = $true
|
||||
|
||||
try {
|
||||
Mount-DiskImage -ImagePath $isoPath
|
||||
Invoke-WPFRunspace -ParameterList @(,('isoPath', $isoPath)) -ScriptBlock {
|
||||
param($isoPath)
|
||||
|
||||
do {
|
||||
Start-Sleep -Milliseconds 500
|
||||
} until ((Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter)
|
||||
try {
|
||||
Mount-DiskImage -ImagePath $isoPath
|
||||
|
||||
$driveLetter = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter + ":"
|
||||
Write-Win11ISOLog "Mounted at drive $driveLetter"
|
||||
do {
|
||||
Start-Sleep -Milliseconds 500
|
||||
} until ((Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter)
|
||||
|
||||
Set-WinUtilProgressBar -Label "Verifying ISO contents..." -Percent 30
|
||||
$driveLetter = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter + ":"
|
||||
Write-WinUtilISOLog "Mounted at drive $driveLetter"
|
||||
|
||||
$wimPath = Join-Path $driveLetter "sources\install.wim"
|
||||
$esdPath = Join-Path $driveLetter "sources\install.esd"
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Verifying ISO contents..." -Percent 30
|
||||
|
||||
if (-not (Test-Path $wimPath) -and -not (Test-Path $esdPath)) {
|
||||
Dismount-DiskImage -ImagePath $isoPath
|
||||
Write-Win11ISOLog "ERROR: install.wim/install.esd not found - not a valid Windows ISO."
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"This does not appear to be a valid Windows ISO.`n`ninstall.wim / install.esd was not found.",
|
||||
"Invalid ISO", "OK", "Error")
|
||||
Set-WinUtilProgressBar -Label "" -Percent 0
|
||||
return
|
||||
}
|
||||
$wimPath = Join-Path $driveLetter "sources\install.wim"
|
||||
$esdPath = Join-Path $driveLetter "sources\install.esd"
|
||||
|
||||
$activeWim = if (Test-Path $wimPath) { $wimPath } else { $esdPath }
|
||||
|
||||
Set-WinUtilProgressBar -Label "Reading image metadata..." -Percent 55
|
||||
$imageInfo = Get-WindowsImage -ImagePath $activeWim | Select-Object ImageIndex, ImageName
|
||||
|
||||
if (-not ($imageInfo | Where-Object { $_.ImageName -match "Windows 11" })) {
|
||||
Dismount-DiskImage -ImagePath $isoPath
|
||||
Write-Win11ISOLog "ERROR: No 'Windows 11' edition found in the image."
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"No Windows 11 edition was found in this ISO.`n`nOnly official Windows 11 ISOs are supported.",
|
||||
"Not a Windows 11 ISO", "OK", "Error")
|
||||
Set-WinUtilProgressBar -Label "" -Percent 0
|
||||
return
|
||||
}
|
||||
|
||||
$sync["Win11ISOImageInfo"] = $imageInfo
|
||||
|
||||
$sync["WPFWin11ISOMountDriveLetter"].Text = "Mounted at: $driveLetter | Image file: $(Split-Path $activeWim -Leaf)"
|
||||
$sync["WPFWin11ISOEditionComboBox"].Dispatcher.Invoke([action]{
|
||||
$sync["WPFWin11ISOEditionComboBox"].Items.Clear()
|
||||
foreach ($img in $imageInfo) {
|
||||
[void]$sync["WPFWin11ISOEditionComboBox"].Items.Add("$($img.ImageIndex): $($img.ImageName)")
|
||||
}
|
||||
if ($sync["WPFWin11ISOEditionComboBox"].Items.Count -gt 0) {
|
||||
$proIndex = -1
|
||||
for ($i = 0; $i -lt $sync["WPFWin11ISOEditionComboBox"].Items.Count; $i++) {
|
||||
if ($sync["WPFWin11ISOEditionComboBox"].Items[$i] -match "Windows 11 Pro(?![\w ])") {
|
||||
$proIndex = $i; break
|
||||
}
|
||||
if (-not (Test-Path $wimPath) -and -not (Test-Path $esdPath)) {
|
||||
Dismount-DiskImage -ImagePath $isoPath
|
||||
Write-WinUtilISOLog "ERROR: install.wim/install.esd not found - not a valid Windows ISO."
|
||||
Invoke-WPFUIThread {
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"This does not appear to be a valid Windows ISO.`n`ninstall.wim / install.esd was not found.",
|
||||
"Invalid ISO", "OK", "Error")
|
||||
}
|
||||
$sync["WPFWin11ISOEditionComboBox"].SelectedIndex = if ($proIndex -ge 0) { $proIndex } else { 0 }
|
||||
return
|
||||
}
|
||||
})
|
||||
$sync["WPFWin11ISOVerifyResultPanel"].Visibility = "Visible"
|
||||
|
||||
$sync["Win11ISODriveLetter"] = $driveLetter
|
||||
$sync["Win11ISOWimPath"] = $activeWim
|
||||
$sync["Win11ISOImagePath"] = $isoPath
|
||||
$sync["WPFWin11ISOModifySection"].Visibility = "Visible"
|
||||
$activeWim = if (Test-Path $wimPath) { $wimPath } else { $esdPath }
|
||||
|
||||
Set-WinUtilProgressBar -Label "ISO verified" -Percent 100
|
||||
Write-Win11ISOLog "ISO verified OK. Editions found: $($imageInfo.Count)"
|
||||
} catch {
|
||||
Write-Win11ISOLog "ERROR during mount/verify: $_"
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"An error occurred while mounting or verifying the ISO:`n`n$_",
|
||||
"Error", "OK", "Error")
|
||||
} finally {
|
||||
Start-Sleep -Milliseconds 800
|
||||
Set-WinUtilProgressBar -Label "" -Percent 0
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "Reading image metadata..." -Percent 55
|
||||
$imageInfo = Get-WindowsImage -ImagePath $activeWim | Select-Object ImageIndex, ImageName
|
||||
|
||||
if (-not ($imageInfo | Where-Object { $_.ImageName -match "Windows 11" })) {
|
||||
Dismount-DiskImage -ImagePath $isoPath
|
||||
Write-WinUtilISOLog "ERROR: No 'Windows 11' edition found in the image."
|
||||
Invoke-WPFUIThread {
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"No Windows 11 edition was found in this ISO.`n`nOnly official Windows 11 ISOs are supported.",
|
||||
"Not a Windows 11 ISO", "OK", "Error")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
$sync["Win11ISOImageInfo"] = $imageInfo
|
||||
$sync["Win11ISODriveLetter"] = $driveLetter
|
||||
$sync["Win11ISOWimPath"] = $activeWim
|
||||
$sync["Win11ISOImagePath"] = $isoPath
|
||||
|
||||
Invoke-WPFUIThread {
|
||||
$sync["WPFWin11ISOMountDriveLetter"].Text = "Mounted at: $driveLetter | Image file: $(Split-Path $activeWim -Leaf)"
|
||||
$sync["WPFWin11ISOEditionComboBox"].Items.Clear()
|
||||
foreach ($img in $imageInfo) {
|
||||
[void]$sync["WPFWin11ISOEditionComboBox"].Items.Add("$($img.ImageIndex): $($img.ImageName)")
|
||||
}
|
||||
if ($sync["WPFWin11ISOEditionComboBox"].Items.Count -gt 0) {
|
||||
$proIndex = -1
|
||||
for ($i = 0; $i -lt $sync["WPFWin11ISOEditionComboBox"].Items.Count; $i++) {
|
||||
if ($sync["WPFWin11ISOEditionComboBox"].Items[$i] -match "Windows 11 Pro(?![\w ])") {
|
||||
$proIndex = $i; break
|
||||
}
|
||||
}
|
||||
$sync["WPFWin11ISOEditionComboBox"].SelectedIndex = if ($proIndex -ge 0) { $proIndex } else { 0 }
|
||||
}
|
||||
$sync["WPFWin11ISOVerifyResultPanel"].Visibility = "Visible"
|
||||
$sync["WPFWin11ISOModifySection"].Visibility = "Visible"
|
||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $true
|
||||
}
|
||||
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $true -Label "ISO verified" -Percent 100
|
||||
Write-WinUtilISOLog "ISO verified OK. Editions found: $($imageInfo.Count)"
|
||||
} catch {
|
||||
$errorMessage = $_
|
||||
Write-WinUtilISOLog "ERROR during mount/verify: $errorMessage"
|
||||
Invoke-WPFUIThread {
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"An error occurred while mounting or verifying the ISO:`n`n$errorMessage",
|
||||
"Error", "OK", "Error")
|
||||
}
|
||||
} finally {
|
||||
Start-Sleep -Milliseconds 800
|
||||
Set-WinUtilTweaksProgressIndicator -Visible $false
|
||||
Invoke-WPFUIThread {
|
||||
$sync["WPFWin11ISOBrowseButton"].IsEnabled = $true
|
||||
$sync["WPFWin11ISOMountButton"].IsEnabled = $true
|
||||
$sync["Win11ISOProcessRunning"] = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,10 +165,11 @@ function Invoke-WinUtilISOModify {
|
||||
$selectedWimIndex = $sync["Win11ISOImageInfo"][0].ImageIndex
|
||||
}
|
||||
$selectedEditionName = if ($selectedItem) { ($selectedItem -replace '^\d+:\s*', '') } else { "Unknown" }
|
||||
Write-Win11ISOLog "Selected edition: $selectedEditionName (Index $selectedWimIndex)"
|
||||
Write-WinUtilISOLog "Selected edition: $selectedEditionName (Index $selectedWimIndex)"
|
||||
|
||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $false
|
||||
$sync["Win11ISOModifying"] = $true
|
||||
$sync["Win11ISOProcessRunning"] = $true
|
||||
|
||||
$workDir = Join-Path $env:TEMP "WinUtil_Win11ISO_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||||
if (Test-Path $workDir) {
|
||||
@@ -169,7 +188,6 @@ function Invoke-WinUtilISOModify {
|
||||
$runspace.ThreadOptions = "ReuseThread"
|
||||
$runspace.Open()
|
||||
$injectDrivers = $sync["WPFWin11ISOInjectDrivers"].IsChecked -eq $true
|
||||
|
||||
$runspace.SessionStateProxy.SetVariable("sync", $sync)
|
||||
$runspace.SessionStateProxy.SetVariable("isoPath", $isoPath)
|
||||
$runspace.SessionStateProxy.SetVariable("driveLetter", $driveLetter)
|
||||
@@ -181,7 +199,7 @@ function Invoke-WinUtilISOModify {
|
||||
$runspace.SessionStateProxy.SetVariable("injectDrivers", $injectDrivers)
|
||||
|
||||
$isoScriptFuncDef = "function Invoke-WinUtilISOScript {`n" + ${function:Invoke-WinUtilISOScript}.ToString() + "`n}"
|
||||
$win11ISOLogFuncDef = "function Write-Win11ISOLog {`n" + ${function:Write-Win11ISOLog}.ToString() + "`n}"
|
||||
$win11ISOLogFuncDef = "function Write-WinUtilISOLog {`n" + ${function:Write-WinUtilISOLog}.ToString() + "`n}"
|
||||
$runspace.SessionStateProxy.SetVariable("isoScriptFuncDef", $isoScriptFuncDef)
|
||||
$runspace.SessionStateProxy.SetVariable("win11ISOLogFuncDef", $win11ISOLogFuncDef)
|
||||
|
||||
@@ -203,9 +221,10 @@ function Invoke-WinUtilISOModify {
|
||||
|
||||
function SetProgress($label, $pct) {
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
$sync.progressBarTextBlock.Text = $label
|
||||
$sync.progressBarTextBlock.ToolTip = $label
|
||||
$sync.ProgressBar.Value = [Math]::Max($pct, 5)
|
||||
$sync["WPFTweaksProgressBar"].Visibility = "Visible"
|
||||
$sync["WPFTweaksProgressLabel"].Text = $label
|
||||
$sync["WPFTweaksProgressLabel"].ToolTip = $label
|
||||
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -233,121 +252,6 @@ function Invoke-WinUtilISOModify {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-WinUtilMountedImageEditionId {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$MountDir,
|
||||
[string]$EditionName,
|
||||
[scriptblock]$Logger
|
||||
)
|
||||
|
||||
try {
|
||||
$dismOutput = & dism /English "/Image:$MountDir" /Get-CurrentEdition 2>&1
|
||||
foreach ($line in $dismOutput) {
|
||||
if ($line -match '^\s*Current Edition\s*:\s*(.+?)\s*$') {
|
||||
$editionId = $Matches[1].Trim()
|
||||
if ($editionId) {
|
||||
if ($Logger) { $null = $Logger.Invoke("Detected mounted image EditionID: $editionId") }
|
||||
return $editionId
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
if ($Logger) { $null = $Logger.Invoke("Warning: could not detect mounted image EditionID with DISM: $_") }
|
||||
}
|
||||
|
||||
$fallbackEditionId = Get-WinUtilEditionIdFromName -EditionName $EditionName
|
||||
if ($fallbackEditionId -and $Logger) {
|
||||
$null = $Logger.Invoke("Using fallback EditionID '$fallbackEditionId' from selected edition name.")
|
||||
}
|
||||
return $fallbackEditionId
|
||||
}
|
||||
|
||||
function Get-DismImageInfoMap {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$ImagePath,
|
||||
[int]$Index = 1
|
||||
)
|
||||
|
||||
$map = @{}
|
||||
$lines = & dism /English "/Get-ImageInfo" "/ImageFile:$ImagePath" "/Index:$Index"
|
||||
foreach ($line in $lines) {
|
||||
if ($line -match '^\s*([^:]+?)\s*:\s*(.*)$') {
|
||||
$key = $Matches[1].Trim()
|
||||
$val = $Matches[2].Trim()
|
||||
if (-not $map.ContainsKey($key)) {
|
||||
$map[$key] = $val
|
||||
}
|
||||
}
|
||||
}
|
||||
return $map
|
||||
}
|
||||
|
||||
function Invoke-WinUtilWimMetadataHydration {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$ImagePath,
|
||||
[Parameter(Mandatory)][string]$EditionName,
|
||||
[scriptblock]$Logger
|
||||
)
|
||||
|
||||
$metadataLogger = $Logger
|
||||
|
||||
function LogMeta([string]$Message) {
|
||||
if ($metadataLogger) {
|
||||
$null = $metadataLogger.Invoke($Message)
|
||||
}
|
||||
}
|
||||
|
||||
$before = Get-DismImageInfoMap -ImagePath $ImagePath -Index 1
|
||||
$undefinedBefore = @($before.GetEnumerator() | Where-Object { $_.Value -eq '<undefined>' } | ForEach-Object { $_.Key })
|
||||
|
||||
if ($undefinedBefore.Count -eq 0) {
|
||||
LogMeta "Metadata check: no undefined DISM fields detected."
|
||||
return
|
||||
}
|
||||
|
||||
LogMeta "Metadata check: undefined DISM fields detected: $($undefinedBefore -join ', ')"
|
||||
LogMeta "Attempting best-effort metadata hydration for install.wim..."
|
||||
|
||||
$setImage = Get-Command Set-WindowsImage -ErrorAction SilentlyContinue
|
||||
if (-not $setImage) {
|
||||
LogMeta "Set-WindowsImage is unavailable on this host; cannot write additional WIM metadata fields."
|
||||
return
|
||||
}
|
||||
|
||||
$targetName = if ($EditionName -and $EditionName -ne 'Unknown') { $EditionName } else { $before['Name'] }
|
||||
if (-not $targetName) { $targetName = 'Windows 11' }
|
||||
|
||||
$targetDescription = if ($before['Description'] -and $before['Description'] -ne '<undefined>') {
|
||||
$before['Description']
|
||||
} else {
|
||||
$targetName
|
||||
}
|
||||
|
||||
$setArgs = @{
|
||||
ImagePath = $ImagePath
|
||||
Index = 1
|
||||
Name = $targetName
|
||||
Description = $targetDescription
|
||||
ErrorAction = 'Stop'
|
||||
}
|
||||
|
||||
try {
|
||||
Set-WindowsImage @setArgs | Out-Null
|
||||
LogMeta "Applied Set-WindowsImage metadata updates (Name/Description)."
|
||||
} catch {
|
||||
LogMeta "Warning: Set-WindowsImage metadata update failed: $_"
|
||||
}
|
||||
|
||||
$after = Get-DismImageInfoMap -ImagePath $ImagePath -Index 1
|
||||
$undefinedAfter = @($after.GetEnumerator() | Where-Object { $_.Value -eq '<undefined>' } | ForEach-Object { $_.Key })
|
||||
if ($undefinedAfter.Count -eq 0) {
|
||||
LogMeta "Metadata hydration complete: no undefined DISM fields remain."
|
||||
} else {
|
||||
LogMeta "Metadata hydration complete. Remaining undefined DISM fields: $($undefinedAfter -join ', ')"
|
||||
LogMeta "Note: some DISM metadata fields are read-only and come from Microsoft image internals."
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
$sync["WPFWin11ISOSelectSection"].Visibility = "Collapsed"
|
||||
@@ -357,51 +261,30 @@ function Invoke-WinUtilISOModify {
|
||||
|
||||
Log "Creating working directory: $workDir"
|
||||
$isoContents = Join-Path $workDir "iso_contents"
|
||||
$mountDir = Join-Path $workDir "wim_mount"
|
||||
New-Item -ItemType Directory -Path $isoContents, $mountDir -Force
|
||||
New-Item -ItemType Directory -Path $isoContents -Force
|
||||
SetProgress "Copying ISO contents..." 10
|
||||
|
||||
Log "Copying ISO contents from $driveLetter to $isoContents..."
|
||||
& robocopy $driveLetter $isoContents /E /NFL /NDL /NJH /NJS
|
||||
Log "ISO contents copied."
|
||||
SetProgress "Mounting install.wim..." 25
|
||||
SetProgress "Preparing setup media..." 25
|
||||
|
||||
$sourceImageFileName = Split-Path $wimPath -Leaf
|
||||
$localWim = Join-Path $isoContents "sources\$sourceImageFileName"
|
||||
if (-not (Test-Path $localWim)) {
|
||||
throw "Copied ISO image file not found: sources\$sourceImageFileName"
|
||||
}
|
||||
Set-ItemProperty -Path $localWim -Name IsReadOnly -Value $false
|
||||
$selectedEditionId = Get-WinUtilEditionIdFromName -EditionName $selectedEditionName
|
||||
|
||||
Log "Mounting install.wim (Index ${selectedWimIndex}: $selectedEditionName) at $mountDir..."
|
||||
Mount-WindowsImage -ImagePath $localWim -Index $selectedWimIndex -Path $mountDir
|
||||
SetProgress "Modifying install.wim..." 45
|
||||
$selectedEditionId = Get-WinUtilMountedImageEditionId -MountDir $mountDir -EditionName $selectedEditionName -Logger ${function:Log}
|
||||
Log "Writing autounattend.xml and edition selection..."
|
||||
Invoke-WinUtilISOScript -ISOContentsDir $isoContents -AutoUnattendXml $autounattendContent -InjectCurrentSystemDrivers $injectDrivers -InstallImagePath $localWim -InstallImageIndex $selectedWimIndex -InstallEditionId $selectedEditionId -Log { param($m) Log $m }
|
||||
|
||||
Log "Applying WinUtil modifications to install.wim..."
|
||||
Invoke-WinUtilISOScript -ScratchDir $mountDir -ISOContentsDir $isoContents -AutoUnattendXml $autounattendContent -InjectCurrentSystemDrivers $injectDrivers -InstallEditionId $selectedEditionId -InstallImageIndex 1 -Log { param($m) Log $m }
|
||||
|
||||
SetProgress "Cleaning up component store (WinSxS)..." 56
|
||||
Log "Running DISM component store cleanup (/ResetBase)..."
|
||||
& dism /English "/image:$mountDir" /Cleanup-Image /StartComponentCleanup /ResetBase | ForEach-Object { Log $_ }
|
||||
Log "Component store cleanup complete."
|
||||
|
||||
SetProgress "Saving modified install.wim..." 65
|
||||
Log "Dismounting and saving install.wim. This will take several minutes..."
|
||||
Dismount-WindowsImage -Path $mountDir -Save
|
||||
Log "install.wim saved."
|
||||
|
||||
SetProgress "Removing unused editions from install.wim..." 70
|
||||
Log "Exporting edition '$selectedEditionName' (Index $selectedWimIndex) to a single-edition install.wim..."
|
||||
$exportWim = Join-Path $isoContents "sources\install_export.wim"
|
||||
Export-WindowsImage -SourceImagePath $localWim -SourceIndex $selectedWimIndex -DestinationImagePath $exportWim
|
||||
Remove-Item -Path $localWim -Force
|
||||
Rename-Item -Path $exportWim -NewName "install.wim" -Force
|
||||
$localWim = Join-Path $isoContents "sources\install.wim"
|
||||
Log "Unused editions removed. install.wim now contains only '$selectedEditionName'."
|
||||
|
||||
SetProgress "Hydrating WIM metadata..." 76
|
||||
Invoke-WinUtilWimMetadataHydration -ImagePath $localWim -EditionName $selectedEditionName -Logger ${function:Log}
|
||||
SetProgress "Preserving install image..." 70
|
||||
if ($injectDrivers) {
|
||||
Log "Added current-system drivers to $sourceImageFileName index $selectedWimIndex with one mount and commit."
|
||||
} else {
|
||||
Log "Preserved the original $sourceImageFileName without mounting, exporting, or modifying it."
|
||||
}
|
||||
|
||||
SetProgress "Dismounting source ISO..." 80
|
||||
Log "Dismounting original ISO..."
|
||||
@@ -419,16 +302,6 @@ function Invoke-WinUtilISOModify {
|
||||
} catch {
|
||||
Log "ERROR during modification: $_"
|
||||
|
||||
try {
|
||||
if (Test-Path $mountDir) {
|
||||
$mountedImages = Get-WindowsImage -Mounted | Where-Object { $_.Path -eq $mountDir }
|
||||
if ($mountedImages) {
|
||||
Log "Cleaning up: dismounting install.wim (discarding changes)..."
|
||||
Dismount-WindowsImage -Path $mountDir -Discard
|
||||
}
|
||||
}
|
||||
} catch { Log "Warning: could not dismount install.wim during cleanup: $_" }
|
||||
|
||||
try {
|
||||
$mountedISO = Get-DiskImage -ImagePath $isoPath
|
||||
if ($mountedISO -and $mountedISO.Attached) {
|
||||
@@ -452,10 +325,12 @@ function Invoke-WinUtilISOModify {
|
||||
} finally {
|
||||
Start-Sleep -Milliseconds 800
|
||||
$sync["Win11ISOModifying"] = $false
|
||||
$sync["Win11ISOProcessRunning"] = $false
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
$sync.progressBarTextBlock.Text = ""
|
||||
$sync.progressBarTextBlock.ToolTip = ""
|
||||
$sync.ProgressBar.Value = 0
|
||||
$sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
|
||||
$sync["WPFTweaksProgressLabel"].Text = ""
|
||||
$sync["WPFTweaksProgressLabel"].ToolTip = ""
|
||||
$sync["WPFTweaksProgressValue"].Value = 0
|
||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $true
|
||||
if ($sync["WPFWin11ISOOutputSection"].Visibility -ne "Visible") {
|
||||
$sync["WPFWin11ISOSelectSection"].Visibility = "Visible"
|
||||
@@ -494,9 +369,9 @@ function Invoke-WinUtilISOCheckExistingWork {
|
||||
$sync["WPFWin11ISOOutputSection"].Visibility = "Visible"
|
||||
|
||||
$modified = $existingWorkDir.LastWriteTime.ToString("yyyy-MM-dd HH:mm")
|
||||
Write-Win11ISOLog "Existing working directory found: $($existingWorkDir.FullName)"
|
||||
Write-Win11ISOLog "Last modified: $modified - Skipping Steps 1-3 and resuming at Step 4."
|
||||
Write-Win11ISOLog "Click 'Clean & Reset' if you want to start over with a new ISO."
|
||||
Write-WinUtilISOLog "Existing working directory found: $($existingWorkDir.FullName)"
|
||||
Write-WinUtilISOLog "Last modified: $modified - Skipping Steps 1-3 and resuming at Step 4."
|
||||
Write-WinUtilISOLog "Click 'Clean & Reset' if you want to start over with a new ISO."
|
||||
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"A previous WinUtil ISO working directory was found:`n`n$($existingWorkDir.FullName)`n`n(Last modified: $modified)`n`nStep 4 (output options) has been restored so you can save the already-modified image.`n`nClick 'Clean & Reset' in Step 4 if you want to start over.",
|
||||
@@ -514,6 +389,7 @@ function Invoke-WinUtilISOCleanAndReset {
|
||||
}
|
||||
|
||||
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $false
|
||||
$sync["Win11ISOProcessRunning"] = $true
|
||||
|
||||
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
|
||||
$runspace.ApartmentState = "STA"
|
||||
@@ -538,9 +414,10 @@ function Invoke-WinUtilISOCleanAndReset {
|
||||
|
||||
function SetProgress($label, $pct) {
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
$sync.progressBarTextBlock.Text = $label
|
||||
$sync.progressBarTextBlock.ToolTip = $label
|
||||
$sync.ProgressBar.Value = [Math]::Max($pct, 5)
|
||||
$sync["WPFTweaksProgressBar"].Visibility = "Visible"
|
||||
$sync["WPFTweaksProgressLabel"].Text = $label
|
||||
$sync["WPFTweaksProgressLabel"].ToolTip = $label
|
||||
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -628,20 +505,24 @@ function Invoke-WinUtilISOCleanAndReset {
|
||||
$sync["WPFWin11ISOModifyButton"].IsEnabled = $true
|
||||
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true
|
||||
|
||||
$sync.progressBarTextBlock.Text = ""
|
||||
$sync.progressBarTextBlock.ToolTip = ""
|
||||
$sync.ProgressBar.Value = 0
|
||||
$sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
|
||||
$sync["WPFTweaksProgressLabel"].Text = ""
|
||||
$sync["WPFTweaksProgressLabel"].ToolTip = ""
|
||||
$sync["WPFTweaksProgressValue"].Value = 0
|
||||
|
||||
$sync["WPFWin11ISOStatusLog"].Text = "Ready. Please select a Windows 11 ISO to begin."
|
||||
})
|
||||
} catch {
|
||||
Log "ERROR during Clean & Reset: $_"
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
$sync.progressBarTextBlock.Text = ""
|
||||
$sync.progressBarTextBlock.ToolTip = ""
|
||||
$sync.ProgressBar.Value = 0
|
||||
$sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
|
||||
$sync["WPFTweaksProgressLabel"].Text = ""
|
||||
$sync["WPFTweaksProgressLabel"].ToolTip = ""
|
||||
$sync["WPFTweaksProgressValue"].Value = 0
|
||||
$sync["WPFWin11ISOCleanResetButton"].IsEnabled = $true
|
||||
})
|
||||
} finally {
|
||||
$sync["Win11ISOProcessRunning"] = $false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -680,32 +561,33 @@ function Invoke-WinUtilISOExport {
|
||||
}
|
||||
|
||||
if (-not $oscdimg) {
|
||||
Write-Win11ISOLog "oscdimg.exe not found. Attempting to install via winget..."
|
||||
Write-WinUtilISOLog "oscdimg.exe not found. Attempting to install via winget..."
|
||||
try {
|
||||
# First ensure winget is installed and operational
|
||||
Install-WinUtilWinget
|
||||
|
||||
$winget = Get-Command winget
|
||||
$result = & $winget install -e --id Microsoft.OSCDIMG --accept-package-agreements --accept-source-agreements
|
||||
Write-Win11ISOLog "winget output: $result"
|
||||
Write-WinUtilISOLog "winget output: $result"
|
||||
$oscdimg = Get-ChildItem "$env:LOCALAPPDATA\Microsoft\WinGet\Packages" -Recurse -Filter "oscdimg.exe" |
|
||||
Where-Object { $_.FullName -match 'Microsoft\.OSCDIMG' } |
|
||||
Select-Object -First 1 -ExpandProperty FullName
|
||||
} catch {
|
||||
Write-Win11ISOLog "winget not available or install failed: $_"
|
||||
Write-WinUtilISOLog "winget not available or install failed: $_"
|
||||
}
|
||||
|
||||
if (-not $oscdimg) {
|
||||
Write-Win11ISOLog "oscdimg.exe still not found after install attempt."
|
||||
Write-WinUtilISOLog "oscdimg.exe still not found after install attempt."
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"oscdimg.exe could not be found or installed automatically.`n`nPlease install it manually:`n winget install -e --id Microsoft.OSCDIMG`n`nOr install the Windows ADK from:`nhttps://learn.microsoft.com/windows-hardware/get-started/adk-install",
|
||||
"oscdimg Not Found", "OK", "Warning")
|
||||
return
|
||||
}
|
||||
Write-Win11ISOLog "oscdimg.exe installed successfully."
|
||||
Write-WinUtilISOLog "oscdimg.exe installed successfully."
|
||||
}
|
||||
|
||||
$sync["WPFWin11ISOChooseISOButton"].IsEnabled = $false
|
||||
$sync["Win11ISOProcessRunning"] = $true
|
||||
|
||||
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
|
||||
$runspace.ApartmentState = "STA"
|
||||
@@ -716,7 +598,7 @@ function Invoke-WinUtilISOExport {
|
||||
$runspace.SessionStateProxy.SetVariable("outputISO", $outputISO)
|
||||
$runspace.SessionStateProxy.SetVariable("oscdimg", $oscdimg)
|
||||
|
||||
$win11ISOLogFuncDef = "function Write-Win11ISOLog {`n" + ${function:Write-Win11ISOLog}.ToString() + "`n}"
|
||||
$win11ISOLogFuncDef = "function Write-WinUtilISOLog {`n" + ${function:Write-WinUtilISOLog}.ToString() + "`n}"
|
||||
$runspace.SessionStateProxy.SetVariable("win11ISOLogFuncDef", $win11ISOLogFuncDef)
|
||||
|
||||
$script = [Management.Automation.PowerShell]::Create()
|
||||
@@ -726,20 +608,21 @@ function Invoke-WinUtilISOExport {
|
||||
|
||||
function SetProgress($label, $pct) {
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
$sync.progressBarTextBlock.Text = $label
|
||||
$sync.progressBarTextBlock.ToolTip = $label
|
||||
$sync.ProgressBar.Value = [Math]::Max($pct, 5)
|
||||
$sync["WPFTweaksProgressBar"].Visibility = "Visible"
|
||||
$sync["WPFTweaksProgressLabel"].Text = $label
|
||||
$sync["WPFTweaksProgressLabel"].ToolTip = $label
|
||||
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
Write-Win11ISOLog "Exporting to ISO: $outputISO"
|
||||
Write-WinUtilISOLog "Exporting to ISO: $outputISO"
|
||||
SetProgress "Building ISO..." 10
|
||||
|
||||
$bootData = "2#p0,e,b`"$contentsDir\boot\etfsboot.com`"#pEF,e,b`"$contentsDir\efi\microsoft\boot\efisys.bin`""
|
||||
$oscdimgArgs = @("-m", "-o", "-u2", "-udfver102", "-bootdata:$bootData", "-l`"CTOS_MODIFIED`"", "`"$contentsDir`"", "`"$outputISO`"")
|
||||
|
||||
Write-Win11ISOLog "Running oscdimg..."
|
||||
Write-WinUtilISOLog "Running oscdimg..."
|
||||
|
||||
$psi = [System.Diagnostics.ProcessStartInfo]::new()
|
||||
$psi.FileName = $oscdimg
|
||||
@@ -756,7 +639,7 @@ function Invoke-WinUtilISOExport {
|
||||
# Stream stdout line-by-line as oscdimg runs
|
||||
while (-not $proc.StandardOutput.EndOfStream) {
|
||||
$line = $proc.StandardOutput.ReadLine()
|
||||
if ($line.Trim()) { Write-Win11ISOLog $line }
|
||||
if ($line.Trim()) { Write-WinUtilISOLog $line }
|
||||
}
|
||||
|
||||
$proc.WaitForExit()
|
||||
@@ -764,17 +647,17 @@ function Invoke-WinUtilISOExport {
|
||||
# Flush any stderr after process exits
|
||||
$stderr = $proc.StandardError.ReadToEnd()
|
||||
foreach ($line in ($stderr -split "`r?`n")) {
|
||||
if ($line.Trim()) { Write-Win11ISOLog "[stderr]$line" }
|
||||
if ($line.Trim()) { Write-WinUtilISOLog "[stderr]$line" }
|
||||
}
|
||||
|
||||
if ($proc.ExitCode -eq 0) {
|
||||
SetProgress "ISO exported" 100
|
||||
Write-Win11ISOLog "ISO exported successfully: $outputISO"
|
||||
Write-WinUtilISOLog "ISO exported successfully: $outputISO"
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
[System.Windows.MessageBox]::Show("ISO exported successfully!`n`n$outputISO", "Export Complete", "OK", "Info")
|
||||
})
|
||||
} else {
|
||||
Write-Win11ISOLog "oscdimg exited with code $($proc.ExitCode)."
|
||||
Write-WinUtilISOLog "oscdimg exited with code $($proc.ExitCode)."
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"oscdimg exited with code $($proc.ExitCode).`nCheck the status log for details.",
|
||||
@@ -782,16 +665,18 @@ function Invoke-WinUtilISOExport {
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
Write-Win11ISOLog "ERROR during ISO export: $_"
|
||||
Write-WinUtilISOLog "ERROR during ISO export: $_"
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
[System.Windows.MessageBox]::Show("ISO export failed:`n`n$_", "Error", "OK", "Error")
|
||||
})
|
||||
} finally {
|
||||
Start-Sleep -Milliseconds 800
|
||||
$sync["Win11ISOProcessRunning"] = $false
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
$sync.progressBarTextBlock.Text = ""
|
||||
$sync.progressBarTextBlock.ToolTip = ""
|
||||
$sync.ProgressBar.Value = 0
|
||||
$sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
|
||||
$sync["WPFTweaksProgressLabel"].Text = ""
|
||||
$sync["WPFTweaksProgressLabel"].ToolTip = ""
|
||||
$sync["WPFTweaksProgressValue"].Value = 0
|
||||
$sync["WPFWin11ISOChooseISOButton"].IsEnabled = $true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,220 +1,228 @@
|
||||
function Invoke-WinUtilISOScript {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Applies WinUtil modifications to a mounted Windows 11 install.wim image.
|
||||
Prepares copied Windows setup media without modifying its install image.
|
||||
|
||||
.DESCRIPTION
|
||||
Removes AppX bloatware and OneDrive, optionally injects all drivers exported from
|
||||
the running system into install.wim and boot.wim (controlled by the
|
||||
-InjectCurrentSystemDrivers switch), applies offline registry tweaks (hardware
|
||||
bypass, privacy, OOBE, telemetry, update suppression), deletes CEIP/WU
|
||||
scheduled-task definition files, and optionally writes autounattend.xml to the ISO
|
||||
root and removes the support\ folder from the ISO contents directory.
|
||||
|
||||
All setup scripts embedded in the autounattend.xml <Extensions><File> nodes are
|
||||
written directly into the WIM at their target paths under C:\Windows\Setup\Scripts\
|
||||
to ensure they survive Windows Setup stripping unrecognised-namespace XML elements
|
||||
from the Panther copy of the answer file.
|
||||
|
||||
Mounting/dismounting the WIM is the caller's responsibility (e.g. Invoke-WinUtilISO).
|
||||
|
||||
.PARAMETER ScratchDir
|
||||
Mandatory. Full path to the directory where the Windows image is currently mounted.
|
||||
Stages WinUtil's AppX removal, registry tweaks, and scheduled-task cleanup
|
||||
in the answer file for first logon, writes sources\ei.cfg for the selected
|
||||
edition, and optionally adds current-system drivers to one install.wim index.
|
||||
|
||||
.PARAMETER ISOContentsDir
|
||||
Optional. Root directory of the extracted ISO contents. When supplied,
|
||||
autounattend.xml is written here and the support\ folder is removed.
|
||||
Root directory of the copied ISO contents.
|
||||
|
||||
.PARAMETER AutoUnattendXml
|
||||
Optional. Full XML content for autounattend.xml. If empty, the OOBE bypass
|
||||
file is skipped and a warning is logged.
|
||||
|
||||
.PARAMETER InjectCurrentSystemDrivers
|
||||
Optional. When $true, exports all drivers from the running system and injects
|
||||
them into install.wim and boot.wim index 2 (Windows Setup PE).
|
||||
Defaults to $false.
|
||||
Full XML content for autounattend.xml.
|
||||
|
||||
.PARAMETER InstallEditionId
|
||||
Optional. Windows edition ID for the selected image, for example Professional
|
||||
or Core. Used to write sources\ei.cfg so setup does not fall back to an
|
||||
embedded firmware product key for a different edition.
|
||||
Windows setup EditionID for sources\ei.cfg, for example Professional or Core.
|
||||
|
||||
.PARAMETER InstallImagePath
|
||||
Copied install.wim to service when current-system driver injection is enabled.
|
||||
|
||||
.PARAMETER InstallImageIndex
|
||||
Optional. Image index that setup should install from the final install.wim.
|
||||
Win11 Creator exports the selected edition to a single-image WIM, so this
|
||||
defaults to 1.
|
||||
Selected edition index in install.wim.
|
||||
|
||||
.PARAMETER Log
|
||||
Optional ScriptBlock for progress/status logging. Receives a single [string] argument.
|
||||
|
||||
.EXAMPLE
|
||||
Invoke-WinUtilISOScript -ScratchDir "C:\Temp\wim_mount"
|
||||
|
||||
.EXAMPLE
|
||||
Invoke-WinUtilISOScript `
|
||||
-ScratchDir $mountDir `
|
||||
-ISOContentsDir $isoRoot `
|
||||
-AutoUnattendXml (Get-Content .\tools\autounattend.xml -Raw) `
|
||||
-Log { param($m) Write-Host $m }
|
||||
|
||||
.NOTES
|
||||
Author : Chris Titus @christitustech
|
||||
GitHub : https://github.com/ChrisTitusTech
|
||||
#>
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$ScratchDir,
|
||||
[string]$ISOContentsDir = "",
|
||||
[Parameter(Mandatory)][string]$ISOContentsDir,
|
||||
[string]$AutoUnattendXml = "",
|
||||
[bool]$InjectCurrentSystemDrivers = $false,
|
||||
[string]$InstallEditionId = "",
|
||||
[string]$InstallImagePath = "",
|
||||
[int]$InstallImageIndex = 1,
|
||||
[scriptblock]$Log = { param($m) Write-Output $m }
|
||||
)
|
||||
function Set-ISOScriptReg {
|
||||
param ([string]$Path, [string]$Name, [string]$Type, [string]$Value)
|
||||
try {
|
||||
& reg add $Path /v $Name /t $Type /d $Value /f
|
||||
& $Log "Set registry value: $Path\$Name"
|
||||
} catch {
|
||||
& $Log "Error setting registry value: $_"
|
||||
|
||||
function Add-WinUtilISOStagedDrivers {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$ContentRoot,
|
||||
[Parameter(Mandatory)][string]$InstallImagePath,
|
||||
[Parameter(Mandatory)][int]$InstallImageIndex,
|
||||
[scriptblock]$Logger
|
||||
)
|
||||
|
||||
function Copy-WinUtilISODriverFolder {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$Source,
|
||||
[Parameter(Mandatory)][string]$Destination
|
||||
)
|
||||
|
||||
$folderName = Split-Path $Source -Leaf
|
||||
$targetPath = Join-Path $Destination $folderName
|
||||
$suffix = 1
|
||||
while (Test-Path -LiteralPath $targetPath) {
|
||||
$targetPath = Join-Path $Destination "${folderName}_$suffix"
|
||||
$suffix++
|
||||
}
|
||||
|
||||
Copy-Item -LiteralPath $Source -Destination $targetPath -Recurse -Force -ErrorAction Stop
|
||||
return $targetPath
|
||||
}
|
||||
}
|
||||
|
||||
function Remove-ISOScriptReg {
|
||||
param ([string]$path)
|
||||
try {
|
||||
& reg delete $path /f
|
||||
& $Log "Removed registry key: $path"
|
||||
} catch {
|
||||
& $Log "Error removing registry key: $_"
|
||||
function Test-WinUtilISOStorageDriver {
|
||||
param ([Parameter(Mandatory)][System.IO.FileInfo]$InfFile)
|
||||
|
||||
if ($InfFile.BaseName -match '(?i)(iaahci|iastor|vmd|irst|rst)') {
|
||||
return $true
|
||||
}
|
||||
|
||||
try {
|
||||
return (Get-Content -LiteralPath $InfFile.FullName -Raw -ErrorAction Stop) -match '(?im)^\s*Class\s*=\s*(SCSIAdapter|HDC)\s*(?:;.*)?$'
|
||||
} catch {
|
||||
& $Logger "Warning: could not classify storage driver '$($InfFile.FullName)': $_"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Add-DriversToImage {
|
||||
param ([string]$MountPath, [string]$DriverDir, [string]$Label = "image", [scriptblock]$Logger)
|
||||
& dism /English "/image:$MountPath" /Add-Driver "/Driver:$DriverDir" /Recurse |
|
||||
ForEach-Object { & $Logger " dism[$Label]: $_" }
|
||||
}
|
||||
function Invoke-WinUtilISODism {
|
||||
param (
|
||||
[Parameter(Mandatory)][string[]]$Arguments,
|
||||
[Parameter(Mandatory)][string]$Operation
|
||||
)
|
||||
|
||||
$output = @(& dism.exe @Arguments 2>&1)
|
||||
$exitCode = $LASTEXITCODE
|
||||
if ($exitCode -ne 0) {
|
||||
foreach ($line in @($output | Select-Object -Last 20)) {
|
||||
if (-not [string]::IsNullOrWhiteSpace([string]$line)) {
|
||||
& $Logger " dism[$Operation]: $line"
|
||||
}
|
||||
}
|
||||
throw "DISM $Operation failed with exit code $exitCode."
|
||||
}
|
||||
if ($Operation -ne 'metadata') {
|
||||
& $Logger "DISM $Operation completed."
|
||||
}
|
||||
return $output
|
||||
}
|
||||
|
||||
function Get-WinUtilISOWimMetadata {
|
||||
param ([Parameter(Mandatory)][string]$ImagePath, [Parameter(Mandatory)][int]$Index)
|
||||
|
||||
$metadata = @{}
|
||||
$output = Invoke-WinUtilISODism -Arguments @('/English', '/Get-WimInfo', "/WimFile:$ImagePath", "/Index:$Index") -Operation 'metadata'
|
||||
foreach ($line in $output) {
|
||||
if ([string]$line -match '^\s*([^:]+?)\s*:\s*(.*?)\s*$') {
|
||||
$metadata[$Matches[1].Trim()] = $Matches[2].Trim()
|
||||
}
|
||||
}
|
||||
return $metadata
|
||||
}
|
||||
|
||||
function Assert-WinUtilISOWimMetadata {
|
||||
param (
|
||||
[Parameter(Mandatory)][hashtable]$Before,
|
||||
[hashtable]$After
|
||||
)
|
||||
|
||||
foreach ($key in 'Languages', 'Installation', 'Edition', 'ProductSuite', 'ProductType') {
|
||||
$beforeValue = [string]$Before[$key]
|
||||
if ($beforeValue -eq '<undefined>' -or ($key -in 'Installation', 'Edition', 'ProductType' -and [string]::IsNullOrWhiteSpace($beforeValue))) {
|
||||
throw "install.wim metadata is already invalid: $key is undefined. Driver injection was not attempted."
|
||||
}
|
||||
if ($After) {
|
||||
$afterValue = [string]$After[$key]
|
||||
if ($afterValue -eq '<undefined>' -or ($beforeValue -and $afterValue -ne $beforeValue)) {
|
||||
throw "install.wim metadata validation failed after driver injection: $key changed from '$beforeValue' to '$afterValue'."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Test-WinUtilISOMountedImage {
|
||||
param ([Parameter(Mandatory)][string]$Path)
|
||||
|
||||
return @(& dism.exe /English /Get-MountedImageInfo 2>$null) -match [regex]::Escape($Path)
|
||||
}
|
||||
|
||||
if ([IO.Path]::GetExtension($InstallImagePath) -ne '.wim') {
|
||||
throw 'Current-system driver injection requires install.wim; install.esd cannot be serviced in place.'
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $InstallImagePath)) {
|
||||
throw "install.wim was not found: $InstallImagePath"
|
||||
}
|
||||
if ($InstallImageIndex -lt 1) {
|
||||
throw 'Current-system driver injection requires a valid install.wim image index.'
|
||||
}
|
||||
|
||||
$driverExportRoot = Join-Path $env:TEMP "WinUtil_DriverExport_$(Get-Date -Format 'yyyyMMdd_HHmmss')_$(([guid]::NewGuid()).ToString('N').Substring(0, 8))"
|
||||
$mountDir = Join-Path (Split-Path -Path $ContentRoot -Parent) 'wim_mount'
|
||||
New-Item -Path $driverExportRoot -ItemType Directory -Force | Out-Null
|
||||
$imageMounted = $false
|
||||
|
||||
function Invoke-BootWimInject {
|
||||
param ([string]$BootWimPath, [string]$DriverDir, [scriptblock]$Logger)
|
||||
Set-ItemProperty -Path $BootWimPath -Name IsReadOnly -Value $false
|
||||
$mountDir = Join-Path $env:TEMP "WinUtil_BootMount_$(Get-Random)"
|
||||
New-Item -Path $mountDir -ItemType Directory -Force
|
||||
try {
|
||||
& $Logger "Mounting boot.wim (index 2) for driver injection..."
|
||||
Mount-WindowsImage -ImagePath $BootWimPath -Index 2 -Path $mountDir
|
||||
Add-DriversToImage -MountPath $mountDir -DriverDir $DriverDir -Label "boot" -Logger $Logger
|
||||
& $Logger "Saving boot.wim..."
|
||||
Dismount-WindowsImage -Path $mountDir -Save
|
||||
& $Logger "boot.wim driver injection complete."
|
||||
} catch {
|
||||
& $Logger "Warning: boot.wim driver injection failed: $_"
|
||||
try { Dismount-WindowsImage -Path $mountDir -Discard } catch { & $Logger "Warning: could not discard boot.wim mount: $_" }
|
||||
& $Logger "Exporting current system drivers before modifying install.wim..."
|
||||
$dismLog = Join-Path $env:TEMP "WinUtil_DismDriverExport_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
|
||||
$dismProcess = Start-Process -FilePath "dism.exe" -ArgumentList "/online /export-driver /destination:`"$driverExportRoot`" /LogPath:`"$dismLog`"" -Wait -NoNewWindow -PassThru
|
||||
if ($dismProcess.ExitCode -ne 0) {
|
||||
throw "dism.exe driver export failed with exit code $($dismProcess.ExitCode)."
|
||||
}
|
||||
|
||||
$driverInfs = @(Get-ChildItem -Path $driverExportRoot -Filter '*.inf' -Recurse -File)
|
||||
if ($driverInfs.Count -eq 0) {
|
||||
throw 'DISM exported no driver INF files.'
|
||||
}
|
||||
$driverFolders = @($driverInfs | Group-Object { $_.Directory.FullName })
|
||||
$winpeDriverDir = Join-Path $ContentRoot '$WinpeDriver$'
|
||||
$storageCount = 0
|
||||
$copyFailures = 0
|
||||
|
||||
foreach ($driverFolderGroup in $driverFolders) {
|
||||
$driverFolder = [string]$driverFolderGroup.Name
|
||||
$storageInfs = @($driverFolderGroup.Group | Where-Object { Test-WinUtilISOStorageDriver -InfFile $_ })
|
||||
if ($storageInfs.Count -eq 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
New-Item -Path $winpeDriverDir -ItemType Directory -Force | Out-Null
|
||||
$winpeTarget = Copy-WinUtilISODriverFolder -Source $driverFolder -Destination $winpeDriverDir
|
||||
$storageCount++
|
||||
& $Logger "Staged boot-storage package '$driverFolder' for WinPE as '$winpeTarget'."
|
||||
} catch {
|
||||
$copyFailures++
|
||||
& $Logger "Warning: failed to stage boot-storage package '$driverFolder': $_"
|
||||
}
|
||||
}
|
||||
|
||||
if ($copyFailures -gt 0) {
|
||||
throw "Failed to stage $copyFailures boot-storage driver package folders."
|
||||
}
|
||||
|
||||
& $Logger "Exported $($driverInfs.Count) driver INF files across $($driverFolders.Count) package folders; staged $storageCount boot-storage packages for WinPE."
|
||||
$metadataBefore = Get-WinUtilISOWimMetadata -ImagePath $InstallImagePath -Index $InstallImageIndex
|
||||
Assert-WinUtilISOWimMetadata -Before $metadataBefore
|
||||
|
||||
Set-ItemProperty -LiteralPath $InstallImagePath -Name IsReadOnly -Value $false
|
||||
New-Item -Path $mountDir -ItemType Directory -Force | Out-Null
|
||||
& $Logger "Mounting install.wim index $InstallImageIndex once for driver injection..."
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Mount-Image', "/ImageFile:$InstallImagePath", "/Index:$InstallImageIndex", "/MountDir:$mountDir") -Operation 'mount' | Out-Null
|
||||
$imageMounted = $true
|
||||
|
||||
& $Logger "Adding all exported drivers to the selected Windows image in one DISM operation..."
|
||||
Invoke-WinUtilISODism -Arguments @('/English', "/Image:$mountDir", '/Add-Driver', "/Driver:$driverExportRoot", '/Recurse') -Operation 'add-driver' | Out-Null
|
||||
|
||||
& $Logger 'Committing the driver-only install.wim change...'
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Unmount-Image', "/MountDir:$mountDir", '/Commit') -Operation 'commit' | Out-Null
|
||||
$imageMounted = $false
|
||||
|
||||
$metadataAfter = Get-WinUtilISOWimMetadata -ImagePath $InstallImagePath -Index $InstallImageIndex
|
||||
Assert-WinUtilISOWimMetadata -Before $metadataBefore -After $metadataAfter
|
||||
& $Logger 'Driver injection complete; install.wim metadata validation passed.'
|
||||
} finally {
|
||||
Remove-Item -Path $mountDir -Recurse -Force
|
||||
}
|
||||
}
|
||||
|
||||
function Get-WinUtilISOScriptChildElement {
|
||||
param (
|
||||
[Parameter(Mandatory)][System.Xml.XmlElement]$Parent,
|
||||
[Parameter(Mandatory)][string]$Name,
|
||||
[Parameter(Mandatory)][string]$NamespaceUri
|
||||
)
|
||||
|
||||
foreach ($childNode in $Parent.ChildNodes) {
|
||||
if ($childNode.NodeType -eq [System.Xml.XmlNodeType]::Element -and
|
||||
$childNode.LocalName -eq $Name -and
|
||||
$childNode.NamespaceURI -eq $NamespaceUri) {
|
||||
return [System.Xml.XmlElement]$childNode
|
||||
if ($imageMounted -or (Test-WinUtilISOMountedImage -Path $mountDir)) {
|
||||
try {
|
||||
Invoke-WinUtilISODism -Arguments @('/English', '/Unmount-Image', "/MountDir:$mountDir", '/Discard') -Operation 'discard' | Out-Null
|
||||
} catch {
|
||||
& $Logger "Warning: could not discard the failed install.wim mount: $_"
|
||||
}
|
||||
}
|
||||
Remove-Item -Path $mountDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $driverExportRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
$childElement = $Parent.OwnerDocument.CreateElement($Name, $NamespaceUri)
|
||||
[void]$Parent.AppendChild($childElement)
|
||||
return $childElement
|
||||
}
|
||||
|
||||
function ConvertTo-WinUtilISOAnswerFile {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$XmlContent,
|
||||
[int]$ImageIndex = 1
|
||||
)
|
||||
|
||||
if ($ImageIndex -lt 1) { $ImageIndex = 1 }
|
||||
|
||||
$unattendNs = "urn:schemas-microsoft-com:unattend"
|
||||
$wcmNs = "http://schemas.microsoft.com/WMIConfig/2002/State"
|
||||
|
||||
$xmlDoc = [xml]::new()
|
||||
$xmlDoc.PreserveWhitespace = $true
|
||||
$xmlDoc.LoadXml($XmlContent)
|
||||
|
||||
if ($xmlDoc.DocumentElement.NamespaceURI -ne $unattendNs) {
|
||||
throw "Unexpected autounattend.xml namespace: $($xmlDoc.DocumentElement.NamespaceURI)"
|
||||
}
|
||||
|
||||
if (-not $xmlDoc.DocumentElement.HasAttribute("xmlns:wcm")) {
|
||||
$xmlDoc.DocumentElement.SetAttribute("wcm", "http://www.w3.org/2000/xmlns/", $wcmNs)
|
||||
}
|
||||
|
||||
$nsMgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||
$nsMgr.AddNamespace("u", $unattendNs)
|
||||
|
||||
$windowsPESettings = $xmlDoc.SelectSingleNode('/u:unattend/u:settings[@pass="windowsPE"]', $nsMgr)
|
||||
if (-not $windowsPESettings) {
|
||||
$windowsPESettings = $xmlDoc.CreateElement("settings", $unattendNs)
|
||||
$windowsPESettings.SetAttribute("pass", "windowsPE")
|
||||
[void]$xmlDoc.DocumentElement.PrependChild($windowsPESettings)
|
||||
}
|
||||
|
||||
$setupComponent = $windowsPESettings.SelectSingleNode('u:component[@name="Microsoft-Windows-Setup"]', $nsMgr)
|
||||
if (-not $setupComponent) {
|
||||
$setupComponent = $xmlDoc.CreateElement("component", $unattendNs)
|
||||
$setupComponent.SetAttribute("name", "Microsoft-Windows-Setup")
|
||||
$setupComponent.SetAttribute("processorArchitecture", "amd64")
|
||||
$setupComponent.SetAttribute("publicKeyToken", "31bf3856ad364e35")
|
||||
$setupComponent.SetAttribute("language", "neutral")
|
||||
$setupComponent.SetAttribute("versionScope", "nonSxS")
|
||||
[void]$windowsPESettings.AppendChild($setupComponent)
|
||||
}
|
||||
|
||||
$productKeyNodes = @($setupComponent.SelectNodes("u:UserData/u:ProductKey", $nsMgr))
|
||||
foreach ($productKeyNode in $productKeyNodes) {
|
||||
$keyNode = $productKeyNode.SelectSingleNode("u:Key", $nsMgr)
|
||||
$keyValue = if ($keyNode) { $keyNode.InnerText.Trim() } else { "" }
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($keyValue) -or $keyValue -eq "00000-00000-00000-00000-00000") {
|
||||
[void]$productKeyNode.ParentNode.RemoveChild($productKeyNode)
|
||||
}
|
||||
}
|
||||
|
||||
$imageInstall = Get-WinUtilISOScriptChildElement -Parent $setupComponent -Name "ImageInstall" -NamespaceUri $unattendNs
|
||||
$osImage = Get-WinUtilISOScriptChildElement -Parent $imageInstall -Name "OSImage" -NamespaceUri $unattendNs
|
||||
$installFrom = Get-WinUtilISOScriptChildElement -Parent $osImage -Name "InstallFrom" -NamespaceUri $unattendNs
|
||||
|
||||
$existingMetadataNodes = @($installFrom.SelectNodes("u:MetaData", $nsMgr))
|
||||
foreach ($metadataNode in $existingMetadataNodes) {
|
||||
[void]$installFrom.RemoveChild($metadataNode)
|
||||
}
|
||||
|
||||
$metadata = $xmlDoc.CreateElement("MetaData", $unattendNs)
|
||||
$actionAttribute = $xmlDoc.CreateAttribute("wcm", "action", $wcmNs)
|
||||
$actionAttribute.Value = "add"
|
||||
[void]$metadata.Attributes.Append($actionAttribute)
|
||||
|
||||
$keyElement = $xmlDoc.CreateElement("Key", $unattendNs)
|
||||
$keyElement.InnerText = "/IMAGE/INDEX"
|
||||
[void]$metadata.AppendChild($keyElement)
|
||||
|
||||
$valueElement = $xmlDoc.CreateElement("Value", $unattendNs)
|
||||
$valueElement.InnerText = [string]$ImageIndex
|
||||
[void]$metadata.AppendChild($valueElement)
|
||||
|
||||
[void]$installFrom.AppendChild($metadata)
|
||||
|
||||
return $xmlDoc.OuterXml
|
||||
}
|
||||
|
||||
function Write-WinUtilISOEditionConfig {
|
||||
@@ -224,10 +232,6 @@ function Invoke-WinUtilISOScript {
|
||||
[scriptblock]$Logger
|
||||
)
|
||||
|
||||
if (-not (Test-Path $ContentRoot)) {
|
||||
return
|
||||
}
|
||||
|
||||
$sourcesDir = Join-Path $ContentRoot "sources"
|
||||
New-Item -Path $sourcesDir -ItemType Directory -Force | Out-Null
|
||||
|
||||
@@ -256,253 +260,281 @@ Retail
|
||||
& $Logger "Written sources\ei.cfg for EditionID '$EditionId'."
|
||||
}
|
||||
|
||||
# -- 1. Remove provisioned AppX packages ----------------------------------
|
||||
& $Log "Removing provisioned AppX packages..."
|
||||
function Add-WinUtilISOSetupCustomizations {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$XmlContent,
|
||||
[Parameter(Mandatory)][int]$InstallImageIndex,
|
||||
[scriptblock]$Logger
|
||||
)
|
||||
|
||||
$packages = & dism /English "/image:$ScratchDir" /Get-ProvisionedAppxPackages |
|
||||
ForEach-Object { if ($_ -match 'PackageName : (.*)') { $matches[1] } }
|
||||
$appxPackages = @(
|
||||
'Clipchamp.Clipchamp', 'Microsoft.BingNews', 'Microsoft.BingSearch',
|
||||
'Microsoft.BingWeather', 'Microsoft.GetHelp', 'Microsoft.MicrosoftOfficeHub',
|
||||
'Microsoft.MicrosoftSolitaireCollection', 'Microsoft.MicrosoftStickyNotes',
|
||||
'Microsoft.OutlookForWindows', 'Microsoft.Paint', 'Microsoft.PowerAutomateDesktop',
|
||||
'Microsoft.StartExperiencesApp', 'Microsoft.Todos', 'Microsoft.Windows.DevHome',
|
||||
'Microsoft.WindowsFeedbackHub', 'Microsoft.WindowsSoundRecorder',
|
||||
'Microsoft.ZuneMusic', 'MicrosoftCorporationII.QuickAssist', 'MSTeams'
|
||||
)
|
||||
|
||||
$packagePrefixes = @(
|
||||
'Clipchamp.Clipchamp',
|
||||
'Microsoft.BingNews',
|
||||
'Microsoft.BingSearch',
|
||||
'Microsoft.BingWeather',
|
||||
'Microsoft.GetHelp',
|
||||
'Microsoft.MicrosoftOfficeHub',
|
||||
'Microsoft.MicrosoftSolitaireCollection',
|
||||
'Microsoft.MicrosoftStickyNotes',
|
||||
'Microsoft.OutlookForWindows',
|
||||
'Microsoft.Paint',
|
||||
'Microsoft.PowerAutomateDesktop',
|
||||
'Microsoft.StartExperiencesApp',
|
||||
'Microsoft.Todos',
|
||||
'Microsoft.Windows.DevHome',
|
||||
'Microsoft.WindowsFeedbackHub',
|
||||
'Microsoft.WindowsSoundRecorder',
|
||||
'Microsoft.ZuneMusic',
|
||||
'MicrosoftCorporationII.QuickAssist',
|
||||
'MSTeams'
|
||||
$appxList = ($appxPackages | ForEach-Object { " '$_'" }) -join "`r`n"
|
||||
$postInstallScript = @"
|
||||
`$ErrorActionPreference = 'Continue'
|
||||
`$logPath = 'C:\Windows\Setup\Scripts\WinUtil-PostInstall.log'
|
||||
Start-Transcript -Path `$logPath -Append -ErrorAction SilentlyContinue
|
||||
|
||||
try {
|
||||
Write-Host 'WinUtil: Removing provisioned AppX packages...'
|
||||
`$packages = @(
|
||||
$appxList
|
||||
)
|
||||
foreach (`$package in `$packages) {
|
||||
Get-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue |
|
||||
Where-Object { `$_.DisplayName -like "*`$package*" } |
|
||||
ForEach-Object { Remove-AppxProvisionedPackage -Online -PackageName `$_.PackageName -ErrorAction SilentlyContinue | Out-Null }
|
||||
Get-AppxPackage -AllUsers -ErrorAction SilentlyContinue |
|
||||
Where-Object { `$_.Name -like "*`$package*" } |
|
||||
ForEach-Object { Remove-AppxPackage -AllUsers -Package `$_.PackageFullName -ErrorAction SilentlyContinue | Out-Null }
|
||||
}
|
||||
|
||||
$packages | Where-Object { $pkg = $_; $packagePrefixes | Where-Object { $pkg -like "*$_*" } } |
|
||||
ForEach-Object { & dism /English "/image:$ScratchDir" /Remove-ProvisionedAppxPackage "/PackageName:$_" }
|
||||
function Set-WinUtilRegistryValue([string]`$Path, [string]`$Name, [string]`$Type, [string]`$Value) {
|
||||
reg.exe add `$Path /v `$Name /t `$Type /d `$Value /f 2>&1 | Out-Null
|
||||
}
|
||||
|
||||
function Set-WinUtilContentDeliveryManagerValues([string]`$HiveRoot) {
|
||||
`$contentDeliveryManager = "`$HiveRoot\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager"
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'OemPreInstalledAppsEnabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'PreInstalledAppsEnabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'SilentInstalledAppsEnabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'ContentDeliveryAllowed' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'FeatureManagementEnabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'PreInstalledAppsEverEnabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'SoftLandingEnabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'SubscribedContentEnabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'SubscribedContent-310093Enabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'SubscribedContent-338388Enabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'SubscribedContent-338389Enabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'SubscribedContent-338393Enabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'SubscribedContent-353694Enabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'SubscribedContent-353696Enabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue `$contentDeliveryManager 'SystemPaneSuggestionsEnabled' 'REG_DWORD' '0'
|
||||
reg.exe delete "`$contentDeliveryManager\Subscriptions" /f 2>&1 | Out-Null
|
||||
reg.exe delete "`$contentDeliveryManager\SuggestedApps" /f 2>&1 | Out-Null
|
||||
}
|
||||
|
||||
Write-Host 'WinUtil: Applying registry tweaks...'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ReserveManager' 'ShippedWithReserves' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\CurrentControlSet\Control\BitLocker' 'PreventDeviceEncryption' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Chat' 'ChatIcon' 'REG_DWORD' '3'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\OneDrive' 'DisableFileSyncNGSC' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\DataCollection' 'AllowTelemetry' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\CurrentControlSet\Services\dmwappushservice' 'Start' 'REG_DWORD' '4'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot' 'TurnOffWindowsCopilot' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Edge' 'HubsSidebarEnabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\Explorer' 'DisableSearchBoxSuggestions' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Teams' 'DisableInstallation' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Mail' 'PreventRun' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\CloudContent' 'DisableWindowsConsumerFeatures' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\CloudContent' 'DisableConsumerAccountStateContent' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\CloudContent' 'DisableCloudOptimizedContent' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Microsoft\PolicyManager\current\device\Start' 'ConfigureStartPins' 'REG_SZ' '{"pinnedList": [{}]}'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE' 'BypassNRO' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\Setup\LabConfig' 'BypassCPUCheck' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\Setup\LabConfig' 'BypassRAMCheck' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\Setup\LabConfig' 'BypassSecureBootCheck' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\Setup\LabConfig' 'BypassStorageCheck' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\Setup\LabConfig' 'BypassTPMCheck' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\Setup\MoSetup' 'AllowUpgradesWithUnsupportedTPMOrCPU' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\PushToInstall' 'DisablePushToInstall' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\MRT' 'DontOfferThroughWUAU' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate' 'workCompleted' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\OutlookUpdate' 'workCompleted' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\DevHomeUpdate' 'workCompleted' 'REG_DWORD' '1'
|
||||
reg.exe delete 'HKLM\SOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate' /f 2>&1 | Out-Null
|
||||
reg.exe delete 'HKLM\SOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\DevHomeUpdate' /f 2>&1 | Out-Null
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' 'NoAutoUpdate' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' 'AUOptions' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' 'UseWUServer' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' 'DisableWindowsUpdateAccess' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' 'WUServer' 'REG_SZ' 'http://localhost:8080'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' 'WUStatusServer' 'REG_SZ' 'http://localhost:8080'
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler_Oobe\WindowsUpdate' 'workCompleted' 'REG_DWORD' '1'
|
||||
reg.exe delete 'HKLM\SOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\WindowsUpdate' /f 2>&1 | Out-Null
|
||||
Set-WinUtilRegistryValue 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config' 'DODownloadMode' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\CurrentControlSet\Services\BITS' 'Start' 'REG_DWORD' '4'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\CurrentControlSet\Services\wuauserv' 'Start' 'REG_DWORD' '4'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\CurrentControlSet\Services\UsoSvc' 'Start' 'REG_DWORD' '4'
|
||||
Set-WinUtilRegistryValue 'HKLM\SYSTEM\CurrentControlSet\Services\WaaSMedicSvc' 'Start' 'REG_DWORD' '4'
|
||||
|
||||
`$defaultHive = 'HKU\WinUtilDefault'
|
||||
reg.exe load `$defaultHive 'C:\Users\Default\NTUSER.DAT' 2>&1 | Out-Null
|
||||
if (`$LASTEXITCODE -eq 0) {
|
||||
Set-WinUtilRegistryValue "`$defaultHive\Control Panel\UnsupportedHardwareNotificationCache" 'SV1' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue "`$defaultHive\Control Panel\UnsupportedHardwareNotificationCache" 'SV2' 'REG_DWORD' '0'
|
||||
Set-WinUtilContentDeliveryManagerValues `$defaultHive
|
||||
Set-WinUtilRegistryValue "`$defaultHive\Software\Microsoft\Windows\CurrentVersion\AdvertisingInfo" 'Enabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue "`$defaultHive\Software\Microsoft\Windows\CurrentVersion\Privacy" 'TailoredExperiencesWithDiagnosticDataEnabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue "`$defaultHive\Software\Microsoft\Speech_OneCore\Settings\OnlineSpeechPrivacy" 'HasAccepted' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue "`$defaultHive\Software\Microsoft\Input\TIPC" 'Enabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue "`$defaultHive\Software\Microsoft\InputPersonalization" 'RestrictImplicitInkCollection' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue "`$defaultHive\Software\Microsoft\InputPersonalization" 'RestrictImplicitTextCollection' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue "`$defaultHive\Software\Microsoft\InputPersonalization\TrainedDataStore" 'HarvestContacts' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue "`$defaultHive\Software\Microsoft\Personalization\Settings" 'AcceptedPrivacyPolicy' 'REG_DWORD' '0'
|
||||
reg.exe unload `$defaultHive 2>&1 | Out-Null
|
||||
}
|
||||
|
||||
Set-WinUtilContentDeliveryManagerValues 'HKCU'
|
||||
Set-WinUtilRegistryValue 'HKCU\Control Panel\UnsupportedHardwareNotificationCache' 'SV1' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKCU\Control Panel\UnsupportedHardwareNotificationCache' 'SV2' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced' 'TaskbarMn' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKCU\Software\Microsoft\Windows\CurrentVersion\AdvertisingInfo' 'Enabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKCU\Software\Microsoft\Windows\CurrentVersion\Privacy' 'TailoredExperiencesWithDiagnosticDataEnabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKCU\Software\Microsoft\Speech_OneCore\Settings\OnlineSpeechPrivacy' 'HasAccepted' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKCU\Software\Microsoft\Input\TIPC' 'Enabled' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKCU\Software\Microsoft\InputPersonalization' 'RestrictImplicitInkCollection' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKCU\Software\Microsoft\InputPersonalization' 'RestrictImplicitTextCollection' 'REG_DWORD' '1'
|
||||
Set-WinUtilRegistryValue 'HKCU\Software\Microsoft\InputPersonalization\TrainedDataStore' 'HarvestContacts' 'REG_DWORD' '0'
|
||||
Set-WinUtilRegistryValue 'HKCU\Software\Microsoft\Personalization\Settings' 'AcceptedPrivacyPolicy' 'REG_DWORD' '0'
|
||||
|
||||
Write-Host 'WinUtil: Removing scheduled task definitions...'
|
||||
`$taskPaths = @(
|
||||
'C:\Windows\System32\Tasks\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser',
|
||||
'C:\Windows\System32\Tasks\Microsoft\Windows\Customer Experience Improvement Program',
|
||||
'C:\Windows\System32\Tasks\Microsoft\Windows\Application Experience\ProgramDataUpdater',
|
||||
'C:\Windows\System32\Tasks\Microsoft\Windows\Chkdsk\Proxy',
|
||||
'C:\Windows\System32\Tasks\Microsoft\Windows\Windows Error Reporting\QueueReporting',
|
||||
'C:\Windows\System32\Tasks\Microsoft\Windows\InstallService',
|
||||
'C:\Windows\System32\Tasks\Microsoft\Windows\UpdateOrchestrator',
|
||||
'C:\Windows\System32\Tasks\Microsoft\Windows\UpdateAssistant',
|
||||
'C:\Windows\System32\Tasks\Microsoft\Windows\WaaSMedic',
|
||||
'C:\Windows\System32\Tasks\Microsoft\Windows\WindowsUpdate',
|
||||
'C:\Windows\System32\Tasks\Microsoft\WindowsUpdate'
|
||||
)
|
||||
foreach (`$taskPath in `$taskPaths) { Remove-Item -LiteralPath `$taskPath -Recurse -Force -ErrorAction SilentlyContinue }
|
||||
|
||||
Start-Process -FilePath 'C:\Windows\System32\OneDriveSetup.exe' -ArgumentList '/uninstall' -Wait -ErrorAction SilentlyContinue
|
||||
Write-Host 'WinUtil: Post-install customization complete.'
|
||||
} finally {
|
||||
Stop-Transcript -ErrorAction SilentlyContinue
|
||||
}
|
||||
"@
|
||||
|
||||
$xmlDoc = [xml]::new()
|
||||
$xmlDoc.PreserveWhitespace = $true
|
||||
$xmlDoc.LoadXml($XmlContent)
|
||||
$nsMgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||
$nsMgr.AddNamespace('u', 'urn:schemas-microsoft-com:unattend')
|
||||
$nsMgr.AddNamespace('sg', 'https://schneegans.de/windows/unattend-generator/')
|
||||
|
||||
$setupComponent = $xmlDoc.SelectSingleNode('/u:unattend/u:settings[@pass="windowsPE"]/u:component[@name="Microsoft-Windows-Setup"]', $nsMgr)
|
||||
$extensions = $xmlDoc.SelectSingleNode('//sg:Extensions', $nsMgr)
|
||||
$firstLogonFile = $xmlDoc.SelectSingleNode('//sg:File[@path="C:\Windows\Setup\Scripts\FirstLogon.ps1"]', $nsMgr)
|
||||
if (-not $setupComponent -or -not $extensions -or -not $firstLogonFile) {
|
||||
throw 'autounattend.xml is missing a required Windows Setup, Extensions, or FirstLogon.ps1 node.'
|
||||
}
|
||||
|
||||
$imageInstall = $setupComponent.SelectSingleNode('u:ImageInstall', $nsMgr)
|
||||
if (-not $imageInstall) {
|
||||
$imageInstall = $xmlDoc.CreateElement('ImageInstall', $setupComponent.NamespaceURI)
|
||||
[void]$setupComponent.AppendChild($imageInstall)
|
||||
}
|
||||
$osImage = $imageInstall.SelectSingleNode('u:OSImage', $nsMgr)
|
||||
if (-not $osImage) {
|
||||
$osImage = $xmlDoc.CreateElement('OSImage', $setupComponent.NamespaceURI)
|
||||
[void]$imageInstall.AppendChild($osImage)
|
||||
}
|
||||
$installFrom = $osImage.SelectSingleNode('u:InstallFrom', $nsMgr)
|
||||
if (-not $installFrom) {
|
||||
$installFrom = $xmlDoc.CreateElement('InstallFrom', $setupComponent.NamespaceURI)
|
||||
[void]$osImage.AppendChild($installFrom)
|
||||
}
|
||||
foreach ($existingMetadata in @($installFrom.SelectNodes('u:MetaData', $nsMgr))) {
|
||||
[void]$installFrom.RemoveChild($existingMetadata)
|
||||
}
|
||||
$metadata = $xmlDoc.CreateElement('MetaData', $setupComponent.NamespaceURI)
|
||||
$action = $xmlDoc.CreateAttribute('wcm', 'action', 'http://schemas.microsoft.com/WMIConfig/2002/State')
|
||||
$action.Value = 'add'
|
||||
[void]$metadata.Attributes.Append($action)
|
||||
$key = $xmlDoc.CreateElement('Key', $setupComponent.NamespaceURI)
|
||||
$key.InnerText = '/IMAGE/INDEX'
|
||||
[void]$metadata.AppendChild($key)
|
||||
$value = $xmlDoc.CreateElement('Value', $setupComponent.NamespaceURI)
|
||||
$value.InnerText = [string]$InstallImageIndex
|
||||
[void]$metadata.AppendChild($value)
|
||||
[void]$installFrom.AppendChild($metadata)
|
||||
|
||||
$postInstallFile = $xmlDoc.CreateElement('File', $extensions.NamespaceURI)
|
||||
$postInstallFile.SetAttribute('path', 'C:\Windows\Setup\Scripts\WinUtil-PostInstall.ps1')
|
||||
$postInstallFile.InnerText = $postInstallScript
|
||||
[void]$extensions.AppendChild($postInstallFile)
|
||||
|
||||
$firstLogonFile.InnerText = "& 'C:\Windows\Setup\Scripts\WinUtil-PostInstall.ps1';`r`n`r`n$($firstLogonFile.InnerText.Trim())"
|
||||
|
||||
$null = & $Logger 'Added WinUtil post-install AppX, registry, and scheduled-task customizations to autounattend.xml.'
|
||||
return $xmlDoc.OuterXml
|
||||
}
|
||||
|
||||
function Add-WinUtilISOSetupScriptFallback {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$ContentRoot,
|
||||
[Parameter(Mandatory)][string]$XmlContent,
|
||||
[scriptblock]$Logger
|
||||
)
|
||||
|
||||
$xmlDoc = [xml]::new()
|
||||
$xmlDoc.PreserveWhitespace = $true
|
||||
$xmlDoc.LoadXml($XmlContent)
|
||||
$nsMgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||
$nsMgr.AddNamespace('u', 'urn:schemas-microsoft-com:unattend')
|
||||
$nsMgr.AddNamespace('sg', 'https://schneegans.de/windows/unattend-generator/')
|
||||
|
||||
$setupScriptsRoot = Join-Path $ContentRoot 'sources\$OEM$\$$\Setup\Scripts'
|
||||
$stagedCount = 0
|
||||
foreach ($file in $xmlDoc.SelectNodes('//sg:File', $nsMgr)) {
|
||||
$path = $file.GetAttribute('path')
|
||||
if (-not $path.StartsWith('C:\Windows\Setup\Scripts\', [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
continue
|
||||
}
|
||||
|
||||
$relativePath = $path.Substring('C:\Windows\Setup\Scripts\'.Length)
|
||||
$targetPath = Join-Path $setupScriptsRoot $relativePath
|
||||
New-Item -Path (Split-Path $targetPath -Parent) -ItemType Directory -Force | Out-Null
|
||||
|
||||
$encoding = switch ([System.IO.Path]::GetExtension($targetPath)) {
|
||||
{ $_ -in '.ps1', '.xml' } { [System.Text.Encoding]::UTF8; break }
|
||||
{ $_ -in '.reg', '.vbs', '.js' } { [System.Text.UnicodeEncoding]::new($false, $true); break }
|
||||
default { [System.Text.Encoding]::Default }
|
||||
}
|
||||
$bytes = $encoding.GetPreamble() + $encoding.GetBytes($file.InnerText.Trim())
|
||||
[System.IO.File]::WriteAllBytes($targetPath, $bytes)
|
||||
$stagedCount++
|
||||
}
|
||||
|
||||
$useConfigurationSet = $xmlDoc.SelectSingleNode('/u:unattend/u:settings[@pass="windowsPE"]/u:component[@name="Microsoft-Windows-Setup"]/u:UseConfigurationSet', $nsMgr)
|
||||
if ($useConfigurationSet) {
|
||||
$useConfigurationSet.InnerText = 'true'
|
||||
[System.IO.File]::WriteAllText((Join-Path $ContentRoot 'autounattend.xml'), $xmlDoc.OuterXml, [System.Text.UTF8Encoding]::new($false))
|
||||
}
|
||||
& $Logger "Staged $stagedCount WinUtil setup script fallback files at '$setupScriptsRoot'."
|
||||
}
|
||||
|
||||
if (-not (Test-Path $ISOContentsDir)) {
|
||||
throw "ISO contents directory does not exist: $ISOContentsDir"
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($AutoUnattendXml)) {
|
||||
throw "autounattend.xml content is required to prepare setup media."
|
||||
}
|
||||
|
||||
$preparedAutoUnattendXml = Add-WinUtilISOSetupCustomizations -XmlContent $AutoUnattendXml -InstallImageIndex $InstallImageIndex -Logger $Log
|
||||
$unattendPath = Join-Path $ISOContentsDir "autounattend.xml"
|
||||
[System.IO.File]::WriteAllText($unattendPath, $preparedAutoUnattendXml, [System.Text.UTF8Encoding]::new($false))
|
||||
& $Log "Written autounattend.xml with WinUtil setup customizations to ISO root ($unattendPath)."
|
||||
Add-WinUtilISOSetupScriptFallback -ContentRoot $ISOContentsDir -XmlContent $preparedAutoUnattendXml -Logger $Log
|
||||
|
||||
Write-WinUtilISOEditionConfig -ContentRoot $ISOContentsDir -EditionId $InstallEditionId -Logger $Log
|
||||
|
||||
# -- 2. Inject current system drivers (optional) ---------------------------
|
||||
if ($InjectCurrentSystemDrivers) {
|
||||
& $Log "Exporting all drivers from running system..."
|
||||
$driverExportRoot = Join-Path $env:TEMP "WinUtil_DriverExport_$(Get-Random)"
|
||||
New-Item -Path $driverExportRoot -ItemType Directory -Force
|
||||
try {
|
||||
Export-WindowsDriver -Online -Destination $driverExportRoot
|
||||
|
||||
& $Log "Injecting current system drivers into install.wim..."
|
||||
Add-DriversToImage -MountPath $ScratchDir -DriverDir $driverExportRoot -Label "install" -Logger $Log
|
||||
& $Log "install.wim driver injection complete."
|
||||
|
||||
if ($ISOContentsDir -and (Test-Path $ISOContentsDir)) {
|
||||
$bootWim = Join-Path $ISOContentsDir "sources\boot.wim"
|
||||
if (Test-Path $bootWim) {
|
||||
& $Log "Injecting current system drivers into boot.wim..."
|
||||
Invoke-BootWimInject -BootWimPath $bootWim -DriverDir $driverExportRoot -Logger $Log
|
||||
} else {
|
||||
& $Log "Warning: boot.wim not found - skipping boot.wim driver injection."
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
& $Log "Error during driver export/injection: $_"
|
||||
} finally {
|
||||
Remove-Item -Path $driverExportRoot -Recurse -Force
|
||||
}
|
||||
} else {
|
||||
& $Log "Driver injection skipped."
|
||||
}
|
||||
|
||||
# -- 3. Registry tweaks ----------------------------------------------------
|
||||
& $Log "Loading offline registry hives..."
|
||||
reg load HKLM\zCOMPONENTS "$ScratchDir\Windows\System32\config\COMPONENTS"
|
||||
reg load HKLM\zDEFAULT "$ScratchDir\Windows\System32\config\default"
|
||||
reg load HKLM\zNTUSER "$ScratchDir\Users\Default\ntuser.dat"
|
||||
reg load HKLM\zSOFTWARE "$ScratchDir\Windows\System32\config\SOFTWARE"
|
||||
reg load HKLM\zSYSTEM "$ScratchDir\Windows\System32\config\SYSTEM"
|
||||
|
||||
& $Log "Bypassing system requirements..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zDEFAULT\Control Panel\UnsupportedHardwareNotificationCache' -Name 'SV1' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zDEFAULT\Control Panel\UnsupportedHardwareNotificationCache' -Name 'SV2' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Control Panel\UnsupportedHardwareNotificationCache' -Name 'SV1' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Control Panel\UnsupportedHardwareNotificationCache' -Name 'SV2' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\Setup\LabConfig' -Name 'BypassCPUCheck' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\Setup\LabConfig' -Name 'BypassRAMCheck' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\Setup\LabConfig' -Name 'BypassSecureBootCheck' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\Setup\LabConfig' -Name 'BypassStorageCheck' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\Setup\LabConfig' -Name 'BypassTPMCheck' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\Setup\MoSetup' -Name 'AllowUpgradesWithUnsupportedTPMOrCPU' -Type 'REG_DWORD' -Value '1'
|
||||
|
||||
& $Log "Disabling sponsored apps..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'OemPreInstalledAppsEnabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'PreInstalledAppsEnabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'SilentInstalledAppsEnabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\CloudContent' -Name 'DisableWindowsConsumerFeatures' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'ContentDeliveryAllowed' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Microsoft\PolicyManager\current\device\Start' -Name 'ConfigureStartPins' -Type 'REG_SZ' -Value '{"pinnedList": [{}]}'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'FeatureManagementEnabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'PreInstalledAppsEverEnabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'SoftLandingEnabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'SubscribedContentEnabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'SubscribedContent-310093Enabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'SubscribedContent-338388Enabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'SubscribedContent-338389Enabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'SubscribedContent-338393Enabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'SubscribedContent-353694Enabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'SubscribedContent-353696Enabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' -Name 'SystemPaneSuggestionsEnabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\PushToInstall' -Name 'DisablePushToInstall' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\MRT' -Name 'DontOfferThroughWUAU' -Type 'REG_DWORD' -Value '1'
|
||||
Remove-ISOScriptReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager\Subscriptions'
|
||||
Remove-ISOScriptReg 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager\SuggestedApps'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\CloudContent' -Name 'DisableConsumerAccountStateContent' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\CloudContent' -Name 'DisableCloudOptimizedContent' -Type 'REG_DWORD' -Value '1'
|
||||
|
||||
& $Log "Enabling local accounts on OOBE..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\OOBE' -Name 'BypassNRO' -Type 'REG_DWORD' -Value '1'
|
||||
|
||||
if ($AutoUnattendXml) {
|
||||
$preparedAutoUnattendXml = $AutoUnattendXml
|
||||
try {
|
||||
$preparedAutoUnattendXml = ConvertTo-WinUtilISOAnswerFile -XmlContent $AutoUnattendXml -ImageIndex $InstallImageIndex
|
||||
& $Log "Prepared autounattend.xml to install image index $InstallImageIndex without forcing a product key."
|
||||
} catch {
|
||||
& $Log "Warning: could not prepare autounattend.xml image selection: $_"
|
||||
}
|
||||
|
||||
try {
|
||||
$xmlDoc = [xml]::new()
|
||||
$xmlDoc.LoadXml($preparedAutoUnattendXml)
|
||||
|
||||
$nsMgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||
$nsMgr.AddNamespace("sg", "https://schneegans.de/windows/unattend-generator/")
|
||||
|
||||
$fileNodes = $xmlDoc.SelectNodes("//sg:File", $nsMgr)
|
||||
if ($fileNodes -and $fileNodes.Count -gt 0) {
|
||||
foreach ($fileNode in $fileNodes) {
|
||||
$absPath = $fileNode.GetAttribute("path")
|
||||
$relPath = $absPath -replace '^[A-Za-z]:[/\\]', ''
|
||||
$destPath = Join-Path $ScratchDir $relPath
|
||||
New-Item -Path (Split-Path $destPath -Parent) -ItemType Directory -Force
|
||||
|
||||
$ext = [IO.Path]::GetExtension($destPath).ToLower()
|
||||
$encoding = switch ($ext) {
|
||||
{ $_ -in '.ps1', '.xml' } { [System.Text.Encoding]::UTF8 }
|
||||
{ $_ -in '.reg', '.vbs', '.js' } { [System.Text.UnicodeEncoding]::new($false, $true) }
|
||||
default { [System.Text.Encoding]::Default }
|
||||
}
|
||||
[System.IO.File]::WriteAllBytes($destPath, ($encoding.GetPreamble() + $encoding.GetBytes($fileNode.InnerText.Trim())))
|
||||
& $Log "Pre-staged setup script: $relPath"
|
||||
}
|
||||
} else {
|
||||
& $Log "Warning: no <Extensions><File> nodes found in autounattend.xml - setup scripts not pre-staged."
|
||||
}
|
||||
} catch {
|
||||
& $Log "Warning: could not pre-stage setup scripts from autounattend.xml: $_"
|
||||
}
|
||||
|
||||
if ($ISOContentsDir -and (Test-Path $ISOContentsDir)) {
|
||||
$isoDest = Join-Path $ISOContentsDir "autounattend.xml"
|
||||
Set-Content -Path $isoDest -Value $preparedAutoUnattendXml -Encoding UTF8 -Force
|
||||
& $Log "Written autounattend.xml to ISO root ($isoDest)."
|
||||
}
|
||||
} else {
|
||||
& $Log "Warning: autounattend.xml content is empty - skipping OOBE bypass file."
|
||||
}
|
||||
|
||||
if ($ISOContentsDir -and (Test-Path $ISOContentsDir)) {
|
||||
Write-WinUtilISOEditionConfig -ContentRoot $ISOContentsDir -EditionId $InstallEditionId -Logger $Log
|
||||
}
|
||||
|
||||
& $Log "Disabling reserved storage..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\ReserveManager' -Name 'ShippedWithReserves' -Type 'REG_DWORD' -Value '0'
|
||||
|
||||
& $Log "Disabling BitLocker device encryption..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\ControlSet001\Control\BitLocker' -Name 'PreventDeviceEncryption' -Type 'REG_DWORD' -Value '1'
|
||||
|
||||
& $Log "Disabling Chat icon..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Windows Chat' -Name 'ChatIcon' -Type 'REG_DWORD' -Value '3'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name 'TaskbarMn' -Type 'REG_DWORD' -Value '0'
|
||||
|
||||
& $Log "Disabling OneDrive folder backup..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\OneDrive' -Name 'DisableFileSyncNGSC' -Type 'REG_DWORD' -Value '1'
|
||||
|
||||
& $Log "Disabling telemetry..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\AdvertisingInfo' -Name 'Enabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\Privacy' -Name 'TailoredExperiencesWithDiagnosticDataEnabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Speech_OneCore\Settings\OnlineSpeechPrivacy' -Name 'HasAccepted' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Input\TIPC' -Name 'Enabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\InputPersonalization' -Name 'RestrictImplicitInkCollection' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\InputPersonalization' -Name 'RestrictImplicitTextCollection' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\InputPersonalization\TrainedDataStore' -Name 'HarvestContacts' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zNTUSER\Software\Microsoft\Personalization\Settings' -Name 'AcceptedPrivacyPolicy' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\DataCollection' -Name 'AllowTelemetry' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\ControlSet001\Services\dmwappushservice' -Name 'Start' -Type 'REG_DWORD' -Value '4'
|
||||
|
||||
& $Log "Preventing installation of DevHome and Outlook..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate' -Name 'workCompleted' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\OutlookUpdate' -Name 'workCompleted' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\DevHomeUpdate' -Name 'workCompleted' -Type 'REG_DWORD' -Value '1'
|
||||
Remove-ISOScriptReg 'HKLM\zSOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate'
|
||||
Remove-ISOScriptReg 'HKLM\zSOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\DevHomeUpdate'
|
||||
|
||||
& $Log "Disabling Copilot..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsCopilot' -Name 'TurnOffWindowsCopilot' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Edge' -Name 'HubsSidebarEnabled' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Explorer' -Name 'DisableSearchBoxSuggestions' -Type 'REG_DWORD' -Value '1'
|
||||
|
||||
& $Log "Disabling Windows Update during OOBE (re-enabled on first logon via FirstLogon.ps1)..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' -Name 'NoAutoUpdate' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' -Name 'AUOptions' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' -Name 'UseWUServer' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -Name 'DisableWindowsUpdateAccess' -Type 'REG_DWORD' -Value '1'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -Name 'WUServer' -Type 'REG_SZ' -Value 'http://localhost:8080'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -Name 'WUStatusServer' -Type 'REG_SZ' -Value 'http://localhost:8080'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler_Oobe\WindowsUpdate' -Name 'workCompleted' -Type 'REG_DWORD' -Value '1'
|
||||
Remove-ISOScriptReg 'HKLM\zSOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\WindowsUpdate'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config' -Name 'DODownloadMode' -Type 'REG_DWORD' -Value '0'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\ControlSet001\Services\BITS' -Name 'Start' -Type 'REG_DWORD' -Value '4'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\ControlSet001\Services\wuauserv' -Name 'Start' -Type 'REG_DWORD' -Value '4'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\ControlSet001\Services\UsoSvc' -Name 'Start' -Type 'REG_DWORD' -Value '4'
|
||||
Set-ISOScriptReg -Path 'HKLM\zSYSTEM\ControlSet001\Services\WaaSMedicSvc' -Name 'Start' -Type 'REG_DWORD' -Value '4'
|
||||
|
||||
& $Log "Preventing installation of Teams..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Teams' -Name 'DisableInstallation' -Type 'REG_DWORD' -Value '1'
|
||||
|
||||
& $Log "Preventing installation of new Outlook..."
|
||||
Set-ISOScriptReg -Path 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Windows Mail' -Name 'PreventRun' -Type 'REG_DWORD' -Value '1'
|
||||
|
||||
& $Log "Unloading offline registry hives..."
|
||||
reg unload HKLM\zCOMPONENTS
|
||||
reg unload HKLM\zDEFAULT
|
||||
reg unload HKLM\zNTUSER
|
||||
reg unload HKLM\zSOFTWARE
|
||||
reg unload HKLM\zSYSTEM
|
||||
|
||||
# -- 4. Delete scheduled task definition files -----------------------------
|
||||
& $Log "Deleting scheduled task definition files..."
|
||||
$tasksPath = "$ScratchDir\Windows\System32\Tasks"
|
||||
Remove-Item "$tasksPath\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser" -Force
|
||||
Remove-Item "$tasksPath\Microsoft\Windows\Customer Experience Improvement Program" -Recurse -Force
|
||||
Remove-Item "$tasksPath\Microsoft\Windows\Application Experience\ProgramDataUpdater" -Force
|
||||
Remove-Item "$tasksPath\Microsoft\Windows\Chkdsk\Proxy" -Force
|
||||
Remove-Item "$tasksPath\Microsoft\Windows\Windows Error Reporting\QueueReporting" -Force
|
||||
Remove-Item "$tasksPath\Microsoft\Windows\InstallService" -Recurse -Force
|
||||
Remove-Item "$tasksPath\Microsoft\Windows\UpdateOrchestrator" -Recurse -Force
|
||||
Remove-Item "$tasksPath\Microsoft\Windows\UpdateAssistant" -Recurse -Force
|
||||
Remove-Item "$tasksPath\Microsoft\Windows\WaaSMedic" -Recurse -Force
|
||||
Remove-Item "$tasksPath\Microsoft\Windows\WindowsUpdate" -Recurse -Force
|
||||
Remove-Item "$tasksPath\Microsoft\WindowsUpdate" -Recurse -Force
|
||||
& $Log "Scheduled task files deleted."
|
||||
|
||||
# -- 5. Remove ISO support folder -----------------------------------------
|
||||
if ($ISOContentsDir -and (Test-Path $ISOContentsDir)) {
|
||||
& $Log "Removing ISO support\ folder..."
|
||||
Remove-Item -Path (Join-Path $ISOContentsDir "support") -Recurse -Force
|
||||
& $Log "ISO support\ folder removed."
|
||||
Add-WinUtilISOStagedDrivers -ContentRoot $ISOContentsDir -Logger $Log -InstallImagePath $InstallImagePath -InstallImageIndex $InstallImageIndex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ function Invoke-WinUtilISORefreshUSBDrives {
|
||||
$combo.Items.Add("No USB drives detected.")
|
||||
$combo.SelectedIndex = 0
|
||||
$sync["Win11ISOUSBDisks"] = @()
|
||||
Write-Win11ISOLog "No USB drives detected."
|
||||
Write-WinUtilISOLog "No USB drives detected."
|
||||
return
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ function Invoke-WinUtilISORefreshUSBDrives {
|
||||
$combo.Items.Add("Disk $($disk.Number): $($disk.FriendlyName) [$sizeGB GB] - $($disk.PartitionStyle)")
|
||||
}
|
||||
$combo.SelectedIndex = 0
|
||||
Write-Win11ISOLog "Found $($removable.Count) USB drive(s)."
|
||||
Write-WinUtilISOLog "Found $($removable.Count) USB drive(s)."
|
||||
$sync["Win11ISOUSBDisks"] = $removable
|
||||
}
|
||||
|
||||
@@ -30,6 +30,20 @@ function Invoke-WinUtilISOWriteUSB {
|
||||
return
|
||||
}
|
||||
|
||||
$installWim = Join-Path $contentsDir "sources\install.wim"
|
||||
$installEsd = Join-Path $contentsDir "sources\install.esd"
|
||||
if (Test-Path $installEsd) {
|
||||
$installEsdFile = Get-Item $installEsd
|
||||
$esdSizeBytes = $installEsdFile.Length
|
||||
$esdSizeMB = [math]::Ceiling($esdSizeBytes / 1MB)
|
||||
if ($esdSizeBytes -ge 4GB) {
|
||||
[System.Windows.MessageBox]::Show(
|
||||
"This ISO uses an install.esd file that is $esdSizeMB MB. WinUtil's FAT32 USB format cannot store files larger than 4 GB.`n`nExport an ISO instead or use media with install.wim.",
|
||||
"USB Creation Not Supported", "OK", "Warning")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
$combo = $sync["WPFWin11ISOUSBDriveComboBox"]
|
||||
$selectedIndex = $combo.SelectedIndex
|
||||
$selectedItemText = [string]$combo.SelectedItem
|
||||
@@ -56,12 +70,13 @@ function Invoke-WinUtilISOWriteUSB {
|
||||
"Confirm USB Erase", "YesNo", "Warning")
|
||||
|
||||
if ($confirm -ne "Yes") {
|
||||
Write-Win11ISOLog "USB write cancelled by user."
|
||||
Write-WinUtilISOLog "USB write cancelled by user."
|
||||
return
|
||||
}
|
||||
|
||||
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $false
|
||||
Write-Win11ISOLog "Starting USB write to Disk $diskNum..."
|
||||
$sync["Win11ISOProcessRunning"] = $true
|
||||
Write-WinUtilISOLog "Starting USB write to Disk $diskNum..."
|
||||
|
||||
$runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
|
||||
$runspace.ApartmentState = "STA"
|
||||
@@ -86,9 +101,10 @@ function Invoke-WinUtilISOWriteUSB {
|
||||
|
||||
function SetProgress($label, $pct) {
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
$sync.progressBarTextBlock.Text = $label
|
||||
$sync.progressBarTextBlock.ToolTip = $label
|
||||
$sync.ProgressBar.Value = [Math]::Max($pct, 5)
|
||||
$sync["WPFTweaksProgressBar"].Visibility = "Visible"
|
||||
$sync["WPFTweaksProgressLabel"].Text = $label
|
||||
$sync["WPFTweaksProgressLabel"].ToolTip = $label
|
||||
$sync["WPFTweaksProgressValue"].Value = [Math]::Max($pct, 5)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -255,10 +271,12 @@ function Invoke-WinUtilISOWriteUSB {
|
||||
})
|
||||
} finally {
|
||||
Start-Sleep -Milliseconds 800
|
||||
$sync["Win11ISOProcessRunning"] = $false
|
||||
$sync["WPFWin11ISOStatusLog"].Dispatcher.Invoke([action]{
|
||||
$sync.progressBarTextBlock.Text = ""
|
||||
$sync.progressBarTextBlock.ToolTip = ""
|
||||
$sync.ProgressBar.Value = 0
|
||||
$sync["WPFTweaksProgressBar"].Visibility = "Collapsed"
|
||||
$sync["WPFTweaksProgressLabel"].Text = ""
|
||||
$sync["WPFTweaksProgressLabel"].ToolTip = ""
|
||||
$sync["WPFTweaksProgressValue"].Value = 0
|
||||
$sync["WPFWin11ISOWriteUSBButton"].IsEnabled = $true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ function Invoke-WinUtilTweaks {
|
||||
$sync.configs.tweaks.$CheckBox.appx | ForEach-Object {
|
||||
Remove-WinUtilAPPX -Name $psitem
|
||||
}
|
||||
Remove-WinUtilProvisionedAPPX -PackageList $sync.configs.tweaks.$CheckBox.appx
|
||||
}
|
||||
}
|
||||
Write-WinUtilLog -Component "Tweaks" -Message "$action tweak completed: $CheckBox"
|
||||
|
||||
@@ -17,7 +17,20 @@ function Remove-WinUtilAPPX {
|
||||
|
||||
Write-Host "Removing $Name"
|
||||
Write-WinUtilLog -Component "AppX" -Message "Removing AppX package pattern: $Name"
|
||||
Get-AppxPackage $Name -AllUsers | Remove-AppxPackage -AllUsers
|
||||
Get-AppxProvisionedPackage -Online | Where-Object DisplayName -like $Name | Remove-AppxProvisionedPackage -Online
|
||||
|
||||
# We explicitly loop through packages instead of using the pipeline because PowerShell 7 pipeline binding
|
||||
# for Remove-AppxPackage fails silently, and Get-AppxPackage -AllUsers returns duplicate objects for each user profile.
|
||||
$pkgs = Get-AppxPackage "*$Name*" -AllUsers | Sort-Object -Property PackageFullName -Unique
|
||||
if ($null -ne $pkgs) {
|
||||
foreach ($pkg in $pkgs) {
|
||||
try {
|
||||
Remove-AppxPackage -Package $pkg.PackageFullName -AllUsers -ErrorAction Stop
|
||||
}
|
||||
catch {
|
||||
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message "Failed to remove AppX package $($pkg.PackageFullName): $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-WinUtilLog -Component "AppX" -Message "AppX removal completed for package pattern: $Name"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
function Remove-WinUtilProvisionedAPPX {
|
||||
<#
|
||||
|
||||
.SYNOPSIS
|
||||
Removes all AppX provisioned packages that match the given names
|
||||
|
||||
.PARAMETER PackageList
|
||||
An array of names of the APPX packages to remove
|
||||
|
||||
.EXAMPLE
|
||||
Remove-WinUtilProvisionedAPPX -PackageList @("Microsoft.Microsoft3DViewer", "Microsoft.WindowsCalculator")
|
||||
|
||||
#>
|
||||
param (
|
||||
[string[]]$PackageList
|
||||
)
|
||||
|
||||
if ($null -eq $PackageList -or $PackageList.Count -eq 0) {
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "`nRemoving provisioned packages..."
|
||||
Write-WinUtilLog -Component "AppX" -Message "Removing AppX provisioned packages: $($PackageList -join ', ')"
|
||||
|
||||
# DISM cmdlets like Get-AppxProvisionedPackage often fail with "Class not registered" or hang in PowerShell 7.
|
||||
# We shell out to Windows PowerShell 5.1 (powershell.exe) to reliably remove the provisioned packages.
|
||||
$ps5Command = {
|
||||
$pkgs = $args
|
||||
$provisionedPackages = Get-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue
|
||||
$failures = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
foreach ($Package in $pkgs) {
|
||||
$provs = $provisionedPackages |
|
||||
Where-Object DisplayName -Like "*$Package*"
|
||||
|
||||
if ($null -ne $provs) {
|
||||
foreach ($prov in $provs) {
|
||||
try {
|
||||
Remove-AppxProvisionedPackage -Online -PackageName $prov.PackageName -ErrorAction Stop | Out-Null
|
||||
}
|
||||
catch {
|
||||
$failures.Add("Failed to remove provisioned AppX package $($prov.PackageName): $($_.Exception.Message)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($failures.Count -gt 0) {
|
||||
throw ($failures -join [Environment]::NewLine)
|
||||
}
|
||||
}
|
||||
|
||||
$removalOutput = powershell.exe -NoProfile -NonInteractive -Command $ps5Command -args $PackageList 2>&1
|
||||
if ($LASTEXITCODE -ne 0 -or $null -ne $removalOutput) {
|
||||
$failureDetails = ($removalOutput | Out-String).Trim()
|
||||
$errorMessage = "AppX provisioned package removal failed: $failureDetails"
|
||||
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message $errorMessage
|
||||
throw $errorMessage
|
||||
}
|
||||
|
||||
Write-WinUtilLog -Component "AppX" -Message "AppX provisioned package removal completed."
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
function Save-WinUtilFile {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Downloads a file and reports transfer progress.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[uri]$Uri,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$DestinationPath,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[scriptblock]$ProgressCallback
|
||||
)
|
||||
|
||||
$response = $null
|
||||
$responseStream = $null
|
||||
$outputStream = $null
|
||||
|
||||
try {
|
||||
$request = [System.Net.WebRequest]::Create($Uri)
|
||||
$response = $request.GetResponse()
|
||||
$totalBytes = $response.ContentLength
|
||||
$responseStream = $response.GetResponseStream()
|
||||
$outputStream = [System.IO.File]::Create($DestinationPath)
|
||||
$buffer = New-Object byte[] 81920
|
||||
$downloadedBytes = 0L
|
||||
$lastPercent = -1
|
||||
|
||||
while (($bytesRead = $responseStream.Read($buffer, 0, $buffer.Length)) -gt 0) {
|
||||
$outputStream.Write($buffer, 0, $bytesRead)
|
||||
$downloadedBytes += $bytesRead
|
||||
|
||||
if ($totalBytes -gt 0) {
|
||||
$percent = [Math]::Min(100, [int](($downloadedBytes / $totalBytes) * 100))
|
||||
if ($percent -ne $lastPercent) {
|
||||
& $ProgressCallback $percent
|
||||
$lastPercent = $percent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($lastPercent -ne 100) {
|
||||
& $ProgressCallback 100
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if ($null -ne $outputStream) {
|
||||
$outputStream.Dispose()
|
||||
}
|
||||
if ($null -ne $responseStream) {
|
||||
$responseStream.Dispose()
|
||||
}
|
||||
if ($null -ne $response) {
|
||||
$response.Dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
function Set-WinUtilAppCategoryFilter {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Applies an exact application category filter from an Install tab search chip.
|
||||
|
||||
.PARAMETER Category
|
||||
The application category to show. An empty value clears the filter.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Category = ""
|
||||
)
|
||||
|
||||
$sync.SearchBar.Tag = $Category
|
||||
$sync.SearchBar.Text = $Category
|
||||
Find-AppsByNameOrDescription -SearchString $Category -Category $Category
|
||||
}
|
||||
@@ -33,23 +33,71 @@ function Set-WinUtilDNS {
|
||||
}
|
||||
}
|
||||
|
||||
$dohSupported = [bool](Get-Command Add-DnsClientDohServerAddress -ErrorAction SilentlyContinue)
|
||||
$dnscacheBase = "HKLM:\System\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters"
|
||||
|
||||
Foreach ($Adapter in $Adapters) {
|
||||
$interfaceParams = "$dnscacheBase\$($Adapter.InterfaceGuid)"
|
||||
|
||||
if($DNSProvider -eq "DHCP") {
|
||||
Write-WinUtilLog -Component "DNS" -Message "Resetting DNS to DHCP on adapter $($Adapter.Name) (ifIndex: $($Adapter.ifIndex))."
|
||||
Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ResetServerAddresses
|
||||
netsh interface ip set dnsservers name="$($Adapter.Name)" source=dhcp
|
||||
netsh interface ipv6 set dnsservers name="$($Adapter.Name)" source=dhcp
|
||||
|
||||
$dohInterfaceSettings = "$interfaceParams\DohInterfaceSettings"
|
||||
if (Test-Path $dohInterfaceSettings) {
|
||||
if ($dohSupported) {
|
||||
$dohServerAddresses = @(
|
||||
Get-ChildItem -Path "$dohInterfaceSettings\Doh" -ErrorAction SilentlyContinue
|
||||
Get-ChildItem -Path "$dohInterfaceSettings\Doh6" -ErrorAction SilentlyContinue
|
||||
) | Select-Object -ExpandProperty PSChildName -Unique
|
||||
|
||||
foreach ($ip in $dohServerAddresses) {
|
||||
if (Get-DnsClientDohServerAddress -ServerAddress $ip -ErrorAction SilentlyContinue) {
|
||||
Write-WinUtilLog -Component "DNS" -Message "Removing DoH registration for $ip."
|
||||
Remove-DnsClientDohServerAddress -ServerAddress $ip -Confirm:$false -ErrorAction Stop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Remove-Item -Path $dohInterfaceSettings -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
} else {
|
||||
Write-WinUtilLog -Component "DNS" -Message "Setting IPv4 DNS on adapter $($Adapter.Name) (ifIndex: $($Adapter.ifIndex)) to $($dns.Primary), $($dns.Secondary)."
|
||||
Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ServerAddresses ($dns.Primary, $dns.Secondary)
|
||||
Write-WinUtilLog -Component "DNS" -Message "Setting IPv6 DNS on adapter $($Adapter.Name) (ifIndex: $($Adapter.ifIndex)) to $($dns.Primary6), $($dns.Secondary6)."
|
||||
Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ServerAddresses ($dns.Primary6, $dns.Secondary6)
|
||||
|
||||
if ($dohSupported -and $dns.DohTemplate) {
|
||||
$ips = @($dns.Primary, $dns.Secondary, $dns.Primary6, $dns.Secondary6) | Where-Object { $_ }
|
||||
foreach ($ip in $ips) {
|
||||
$existing = Get-DnsClientDohServerAddress -ServerAddress $ip -ErrorAction SilentlyContinue
|
||||
if ($existing) {
|
||||
Set-DnsClientDohServerAddress -ServerAddress $ip -DohTemplate $dns.DohTemplate -AllowFallbackToUdp $false -AutoUpgrade $true -ErrorAction Stop
|
||||
} else {
|
||||
Write-WinUtilLog -Component "DNS" -Message "Registering DoH template for $ip."
|
||||
Add-DnsClientDohServerAddress -ServerAddress $ip -DohTemplate $dns.DohTemplate -AllowFallbackToUdp $false -AutoUpgrade $true -ErrorAction Stop
|
||||
}
|
||||
|
||||
$leaf = if ($ip.Contains(':')) { 'Doh6' } else { 'Doh' }
|
||||
$regPath = "$interfaceParams\DohInterfaceSettings\$leaf\$ip"
|
||||
|
||||
if (-not (Test-Path $regPath)) {
|
||||
New-Item -Path $regPath -Force -ErrorAction Stop | Out-Null
|
||||
}
|
||||
New-ItemProperty -Path $regPath -Name "DohFlags" -Value 1 -PropertyType QWord -Force -ErrorAction Stop | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($DNSProvider -ne "DHCP" -and $dohSupported -and $dns.DohTemplate) {
|
||||
Clear-DnsClientCache
|
||||
}
|
||||
Write-WinUtilLog -Component "DNS" -Message "DNS provider change completed: $DNSProvider"
|
||||
} catch {
|
||||
Write-Warning "Unable to set DNS Provider due to an unhandled exception."
|
||||
Write-Warning $psitem.Exception.StackTrace
|
||||
Write-WinUtilLog -Level "ERROR" -Component "DNS" -Message "Unable to set DNS provider $DNSProvider`: $($psitem.Exception.Message)"
|
||||
Write-Warning "DNS provider $DNSProvider was not completed because an error occurred."
|
||||
Write-Warning $psitem.Exception.Message
|
||||
Write-WinUtilLog -Level "ERROR" -Component "DNS" -Message "DNS provider $DNSProvider was not completed: $($psitem.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
function Set-WinUtilProgressbar{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This function is used to Update the Progress Bar displayed in the winutil GUI.
|
||||
It will be automatically hidden if the user clicks something and no process is running
|
||||
.PARAMETER Label
|
||||
The Text to be overlaid onto the Progress Bar
|
||||
.PARAMETER PERCENT
|
||||
The percentage of the Progress Bar that should be filled (0-100)
|
||||
#>
|
||||
param(
|
||||
[string]$Label,
|
||||
[ValidateRange(0,100)]
|
||||
[int]$Percent
|
||||
)
|
||||
|
||||
$progressLabel = $Label
|
||||
|
||||
Invoke-WPFUIThread -ScriptBlock {$sync.progressBarTextBlock.Text = $progressLabel}
|
||||
Invoke-WPFUIThread -ScriptBlock {$sync.progressBarTextBlock.ToolTip = $progressLabel}
|
||||
if ($Percent -lt 5 ) {
|
||||
$Percent = 5 # Ensure the progress bar is not empty, as it looks weird
|
||||
}
|
||||
Invoke-WPFUIThread -ScriptBlock { $sync.ProgressBar.Value = $Percent}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
function Set-WinUtilTweaksProgressIndicator {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Shows, updates, or hides the window-level progress indicator used by long-running
|
||||
workflows such as app management, Tweaks, AppX management, and Win11 Creator.
|
||||
It lives outside the TabControl, so it stays visible no matter which tab is active.
|
||||
.PARAMETER Visible
|
||||
Whether the indicator should be shown or hidden.
|
||||
.PARAMETER Label
|
||||
The text to display above the progress bar.
|
||||
.PARAMETER Percent
|
||||
The percentage of the progress bar that should be filled (0-100).
|
||||
#>
|
||||
param(
|
||||
[bool]$Visible,
|
||||
[string]$Label,
|
||||
[ValidateRange(0,100)]
|
||||
[int]$Percent
|
||||
)
|
||||
|
||||
$indicatorVisible = if ($Visible) { [Windows.Visibility]::Visible } else { [Windows.Visibility]::Collapsed }
|
||||
$indicatorLabel = $Label
|
||||
$hasLabel = $PSBoundParameters.ContainsKey('Label')
|
||||
$hasPercent = $PSBoundParameters.ContainsKey('Percent')
|
||||
|
||||
Invoke-WPFUIThread -ScriptBlock {
|
||||
$sync.WPFTweaksProgressBar.Visibility = $indicatorVisible
|
||||
if ($hasLabel) {
|
||||
$sync.WPFTweaksProgressLabel.Text = $indicatorLabel
|
||||
}
|
||||
if ($hasPercent) {
|
||||
$sync.WPFTweaksProgressValue.Value = $Percent
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user