mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2024-11-14 22:55:52 -06:00
3b133e704e
* change logseq url and add .net 8 (#1385) * Update applications.json Add Official logseq url Add .net runtime 8.0 * compile with new logseq and .net 8 runtime * add thorium avx2 * Compile Winutil * Remove Cider Music Player (#1400) * Update winutil.ps1 * Update applications.json * Compile Winutil * Import/Export is now global, Settings menu added and many more improvements (#1398) * Anoter one of those huge PRs - Fix version inefficiency the version is already stored in sync, no need to slow down loading by one extra replace. - Created custom dialog and About message - Create a menu with Import/Export values - press on teh Settings cog in the right upper corner and save all the checkboxes - then you can either load them or load and run automatically - Made Import Export load accross the whole app - Optimized the way checkbox controls are looked up, it is 20% faster now - Added a switch to load all the boxes from a config file - example: .winutil.ps1 -Config "C:UsersasdfDesktop\111.json" - Added a switch to run all the action in unattended mode by passing -Run siwthc - example: .winutil.ps1 -Config "C:UsersasdfDesktop\111.json" -Run - This will run all the tweaks and install all the apps * Fixing a couple of bugs and blur fonts, also menu now closes when focus is lost --------- Co-authored-by: KonTy <KonTy@github.com> Co-authored-by: Chris Titus <contact@christitus.com> * Update applications.json * Compile Winutil * Add F8 Recovery Menu and Windows Reg Backup and others (#1389) * Update feature.json append F8 legacy startup and Win Reg Backup and web search suggestion in search app - enable automatic windows registry backup and do schedule for it as well (disabled by default in Win10, Win11) this will help when doing last known Good Configuration thru the F8 startup menu. - enable / disable legacy F8 startup recovery option. - enable / disable web search suggestions in the windows search in task bar. * new tick boxes features. F8 recovery, regbackup, search web suggestions - enable automatic windows registry backup and do schedule for it as well (disabled by default in Win10, Win11) this will help when doing last known Good Configuration thru the F8 startup menu. - enable / disable legacy F8 startup recovery option. - enable / disable web search suggestions in the windows search in task bar. * Compile Winutil * add Parsec to installable applications (#1157) (#1396) Identifiers: - Winget: Parsec.parsec - Chocolatey: parsec * Compile Winutil * add Konty to About page * Compile Winutil * fix description (#1388) (#1402) Co-authored-by: howell2024 <156375832+howell2024@users.noreply.github.com> --------- Co-authored-by: Cristian Negulescu <cristian@clamsen.com> Co-authored-by: ChrisTitusTech <ChrisTitusTech@users.noreply.github.com> Co-authored-by: Tommi Pöntinen <98650216+hamburgerghini1@users.noreply.github.com> Co-authored-by: KonTy <9524513+KonTy@users.noreply.github.com> Co-authored-by: KonTy <KonTy@github.com> Co-authored-by: Smartek <70715469+smartekIT@users.noreply.github.com> Co-authored-by: Saikrishnan K <53394202+K-Saikrishnan@users.noreply.github.com> Co-authored-by: howell2024 <156375832+howell2024@users.noreply.github.com>
501 lines
17 KiB
PowerShell
501 lines
17 KiB
PowerShell
# SPDX-License-Identifier: MIT
|
|
# Set the maximum number of threads for the RunspacePool to the number of threads on the machine
|
|
$maxthreads = [int]$env:NUMBER_OF_PROCESSORS
|
|
|
|
# Create a new session state for parsing variables into our runspace
|
|
$hashVars = New-object System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'sync',$sync,$Null
|
|
$InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
|
|
|
|
# Add the variable to the session state
|
|
$InitialSessionState.Variables.Add($hashVars)
|
|
|
|
# Get every private function and add them to the session state
|
|
$functions = Get-ChildItem function:\ | Where-Object {$_.name -like "*winutil*" -or $_.name -like "*WPF*"}
|
|
foreach ($function in $functions){
|
|
$functionDefinition = Get-Content function:\$($function.name)
|
|
$functionEntry = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $($function.name), $functionDefinition
|
|
|
|
$initialSessionState.Commands.Add($functionEntry)
|
|
}
|
|
|
|
# Create the runspace pool
|
|
$sync.runspace = [runspacefactory]::CreateRunspacePool(
|
|
1, # Minimum thread count
|
|
$maxthreads, # Maximum thread count
|
|
$InitialSessionState, # Initial session state
|
|
$Host # Machine to create runspaces on
|
|
)
|
|
|
|
# Open the RunspacePool instance
|
|
$sync.runspace.Open()
|
|
|
|
# Create classes for different exceptions
|
|
|
|
class WingetFailedInstall : Exception {
|
|
[string] $additionalData
|
|
|
|
WingetFailedInstall($Message) : base($Message) {}
|
|
}
|
|
|
|
class ChocoFailedInstall : Exception {
|
|
[string] $additionalData
|
|
|
|
ChocoFailedInstall($Message) : base($Message) {}
|
|
}
|
|
|
|
class GenericException : Exception {
|
|
[string] $additionalData
|
|
|
|
GenericException($Message) : base($Message) {}
|
|
}
|
|
|
|
|
|
$inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^<Win.*', '<Window'
|
|
|
|
$organizedData = @{}
|
|
# Iterate through JSON data and organize by panel and category
|
|
foreach ($appName in $sync.configs.applications.PSObject.Properties.Name) {
|
|
$appInfo = $sync.configs.applications.$appName
|
|
|
|
# Create an object for the application
|
|
$appObject = [PSCustomObject]@{
|
|
Name = $appName
|
|
Category = $appInfo.Category
|
|
Content = $appInfo.Content
|
|
Choco = $appInfo.choco
|
|
Winget = $appInfo.winget
|
|
Panel = $appInfo.panel
|
|
Link = $appInfo.link
|
|
Description = $appInfo.description
|
|
}
|
|
|
|
if (-not $organizedData.ContainsKey($appInfo.panel)) {
|
|
$organizedData[$appInfo.panel] = @{}
|
|
}
|
|
|
|
if (-not $organizedData[$appInfo.panel].ContainsKey($appInfo.Category)) {
|
|
$organizedData[$appInfo.panel][$appInfo.Category] = @{}
|
|
}
|
|
|
|
# Store application data in a sub-array under the category
|
|
$organizedData[$appInfo.panel][$appInfo.Category][$appName] = $appObject
|
|
}
|
|
|
|
# Iterate through organizedData by panel, category, and application
|
|
foreach ($panel in $organizedData.Keys) {
|
|
foreach ($category in $organizedData[$panel].Keys) {
|
|
$blockXml += "<Label Content=""$($category)"" FontSize=""16""/>`n"
|
|
$sortedApps = $organizedData[$panel][$category].Keys | Sort-Object
|
|
foreach ($appName in $sortedApps) {
|
|
$appInfo = $organizedData[$panel][$category][$appName]
|
|
if ($null -eq $appInfo.Link)
|
|
{
|
|
$blockXml += "<CheckBox Name=""$appName"" Content=""$($appInfo.Content)"" ToolTip=""$($appInfo.Description)""/>`n"
|
|
}
|
|
else
|
|
{
|
|
$blockXml += "<StackPanel Orientation=""Horizontal""><CheckBox Name=""$appName"" Content=""$($appInfo.Content)"" ToolTip=""$($appInfo.Description)"" Margin=""0,0,2,0""/><TextBlock Name=""$($appName)Link"" Style=""{StaticResource HoverTextBlockStyle}"" Text=""(?)"" ToolTip=""$($appInfo.Link)"" /></StackPanel>`n"
|
|
}
|
|
}
|
|
}
|
|
|
|
$inputXML = $inputXML -replace "{{InstallPanel$panel}}", $blockXml
|
|
$blockXml = ""
|
|
}
|
|
|
|
if ((Get-WinUtilToggleStatus WPFToggleDarkMode) -eq $True) {
|
|
$ctttheme = 'Matrix'
|
|
}
|
|
else {
|
|
$ctttheme = 'Classic'
|
|
}
|
|
$inputXML = Set-WinUtilUITheme -inputXML $inputXML -themeName $ctttheme
|
|
|
|
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
|
|
[xml]$XAML = $inputXML
|
|
|
|
# Read the XAML file
|
|
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
|
|
try { $sync["Form"] = [Windows.Markup.XamlReader]::Load( $reader ) }
|
|
catch [System.Management.Automation.MethodInvocationException] {
|
|
Write-Warning "We ran into a problem with the XAML code. Check the syntax for this control..."
|
|
Write-Host $error[0].Exception.Message -ForegroundColor Red
|
|
If ($error[0].Exception.Message -like "*button*") {
|
|
write-warning "Ensure your <button in the `$inputXML does NOT have a Click=ButtonClick property. PS can't handle this`n`n`n`n"
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."
|
|
}
|
|
|
|
#===========================================================================
|
|
# Store Form Objects In PowerShell
|
|
#===========================================================================
|
|
|
|
$xaml.SelectNodes("//*[@Name]") | ForEach-Object {$sync["$("$($psitem.Name)")"] = $sync["Form"].FindName($psitem.Name)}
|
|
|
|
$sync.keys | ForEach-Object {
|
|
if($sync.$psitem){
|
|
if($($sync["$psitem"].GetType() | Select-Object -ExpandProperty Name) -eq "CheckBox" `
|
|
-and $sync["$psitem"].Name -like "WPFToggle*"){
|
|
$sync["$psitem"].IsChecked = Get-WinUtilToggleStatus $sync["$psitem"].Name
|
|
|
|
$sync["$psitem"].Add_Click({
|
|
[System.Object]$Sender = $args[0]
|
|
Invoke-WPFToggle $Sender.name
|
|
})
|
|
}
|
|
|
|
if($($sync["$psitem"].GetType() | Select-Object -ExpandProperty Name) -eq "ToggleButton"){
|
|
$sync["$psitem"].Add_Click({
|
|
[System.Object]$Sender = $args[0]
|
|
Invoke-WPFButton $Sender.name
|
|
})
|
|
}
|
|
|
|
if($($sync["$psitem"].GetType() | Select-Object -ExpandProperty Name) -eq "Button"){
|
|
$sync["$psitem"].Add_Click({
|
|
[System.Object]$Sender = $args[0]
|
|
Invoke-WPFButton $Sender.name
|
|
})
|
|
}
|
|
|
|
if ($($sync["$psitem"].GetType() | Select-Object -ExpandProperty Name) -eq "TextBlock") {
|
|
if ($sync["$psitem"].Name.EndsWith("Link")) {
|
|
$sync["$psitem"].Add_MouseUp({
|
|
[System.Object]$Sender = $args[0]
|
|
Start-Process $Sender.ToolTip -ErrorAction Stop
|
|
Write-Debug "Opening: $($Sender.ToolTip)"
|
|
})
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
#===========================================================================
|
|
# Setup background config
|
|
#===========================================================================
|
|
|
|
# Load computer information in the background
|
|
Invoke-WPFRunspace -ScriptBlock {
|
|
$sync.ConfigLoaded = $False
|
|
$sync.ComputerInfo = Get-ComputerInfo
|
|
$sync.ConfigLoaded = $True
|
|
} | Out-Null
|
|
|
|
#===========================================================================
|
|
# Setup and Show the Form
|
|
#===========================================================================
|
|
|
|
# Print the logo
|
|
Invoke-WPFFormVariables
|
|
|
|
# Check if Chocolatey is installed
|
|
Install-WinUtilChoco
|
|
|
|
# Set the titlebar
|
|
$sync["Form"].title = $sync["Form"].title + " " + $sync.version
|
|
# Set the commands that will run when the form is closed
|
|
$sync["Form"].Add_Closing({
|
|
$sync.runspace.Dispose()
|
|
$sync.runspace.Close()
|
|
[System.GC]::Collect()
|
|
})
|
|
|
|
# Attach the event handler to the Click event
|
|
$sync.CheckboxFilterClear.Add_Click({
|
|
$sync.CheckboxFilter.Text = ""
|
|
$sync.CheckboxFilterClear.Visibility = "Collapsed"
|
|
})
|
|
|
|
# add some shortcuts for people that don't like clicking
|
|
$commonKeyEvents = {
|
|
if ($sync.ProcessRunning -eq $true) {
|
|
return
|
|
}
|
|
|
|
if ($_.Key -eq "Escape")
|
|
{
|
|
$sync.CheckboxFilter.SelectAll()
|
|
$sync.CheckboxFilter.Text = ""
|
|
$sync.CheckboxFilterClear.Visibility = "Collapsed"
|
|
return
|
|
}
|
|
|
|
# don't ask, I know what I'm doing, just go...
|
|
if (($_.Key -eq "Q" -and $_.KeyboardDevice.Modifiers -eq "Ctrl"))
|
|
{
|
|
$this.Close()
|
|
}
|
|
if ($_.KeyboardDevice.Modifiers -eq "Alt") {
|
|
if ($_.SystemKey -eq "I") {
|
|
Invoke-WPFButton "WPFTab1BT"
|
|
}
|
|
if ($_.SystemKey -eq "T") {
|
|
Invoke-WPFButton "WPFTab2BT"
|
|
}
|
|
if ($_.SystemKey -eq "C") {
|
|
Invoke-WPFButton "WPFTab3BT"
|
|
}
|
|
if ($_.SystemKey -eq "U") {
|
|
Invoke-WPFButton "WPFTab4BT"
|
|
}
|
|
if ($_.SystemKey -eq "M") {
|
|
Invoke-WPFButton "WPFTab5BT"
|
|
}
|
|
if ($_.SystemKey -eq "P") {
|
|
Write-Host "Your Windows Product Key: $((Get-WmiObject -query 'select * from SoftwareLicensingService').OA3xOriginalProductKey)"
|
|
}
|
|
}
|
|
# shortcut for the filter box
|
|
if ($_.Key -eq "F" -and $_.KeyboardDevice.Modifiers -eq "Ctrl") {
|
|
if ($sync.CheckboxFilter.Text -eq "Ctrl-F to filter") {
|
|
$sync.CheckboxFilter.SelectAll()
|
|
$sync.CheckboxFilter.Text = ""
|
|
}
|
|
$sync.CheckboxFilter.Focus()
|
|
}
|
|
}
|
|
|
|
$sync["Form"].Add_PreViewKeyDown($commonKeyEvents)
|
|
|
|
$sync["Form"].Add_MouseLeftButtonDown({
|
|
if ($sync["SettingsPopup"].IsOpen) {
|
|
$sync["SettingsPopup"].IsOpen = $false
|
|
}
|
|
$sync["Form"].DragMove()
|
|
})
|
|
|
|
$sync["Form"].Add_MouseDoubleClick({
|
|
if ($sync["Form"].WindowState -eq [Windows.WindowState]::Normal)
|
|
{
|
|
$sync["Form"].WindowState = [Windows.WindowState]::Maximized;
|
|
}
|
|
else
|
|
{
|
|
$sync["Form"].WindowState = [Windows.WindowState]::Normal;
|
|
}
|
|
})
|
|
|
|
$sync["Form"].Add_Deactivated({
|
|
Write-Debug "WinUtil lost focus"
|
|
if ($sync["SettingsPopup"].IsOpen) {
|
|
$sync["SettingsPopup"].IsOpen = $false
|
|
}
|
|
})
|
|
|
|
$sync["Form"].Add_ContentRendered({
|
|
|
|
try {
|
|
[void][Window]
|
|
} catch {
|
|
Add-Type @"
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
public class Window {
|
|
[DllImport("user32.dll")]
|
|
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
|
|
|
[DllImport("user32.dll")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
|
|
|
|
[DllImport("user32.dll")]
|
|
public static extern int GetSystemMetrics(int nIndex);
|
|
};
|
|
public struct RECT {
|
|
public int Left; // x position of upper-left corner
|
|
public int Top; // y position of upper-left corner
|
|
public int Right; // x position of lower-right corner
|
|
public int Bottom; // y position of lower-right corner
|
|
}
|
|
"@
|
|
}
|
|
|
|
foreach ($proc in (Get-Process | Where-Object { $_.MainWindowTitle -and $_.MainWindowTitle -like "*titus*" })) {
|
|
if ($proc.Id -ne [System.IntPtr]::Zero) {
|
|
Write-Debug "MainWindowHandle: $($proc.Id) $($proc.MainWindowTitle) $($proc.MainWindowHandle)"
|
|
$windowHandle = $proc.MainWindowHandle
|
|
}
|
|
}
|
|
|
|
# need to experiemnt more
|
|
# setting icon for the windows is still not working
|
|
# $pngUrl = "https://christitus.com/images/logo-full.png"
|
|
# $pngPath = "$env:TEMP\cttlogo.png"
|
|
# $iconPath = "$env:TEMP\cttlogo.ico"
|
|
# # Download the PNG file
|
|
# Invoke-WebRequest -Uri $pngUrl -OutFile $pngPath
|
|
# if (Test-Path -Path $pngPath) {
|
|
# ConvertTo-Icon -bitmapPath $pngPath -iconPath $iconPath
|
|
# }
|
|
# $icon = [System.Drawing.Icon]::ExtractAssociatedIcon($iconPath)
|
|
# Write-Host $icon.Handle
|
|
# [Window]::SendMessage($windowHandle, 0x80, [IntPtr]::Zero, $icon.Handle)
|
|
|
|
$rect = New-Object RECT
|
|
[Window]::GetWindowRect($windowHandle, [ref]$rect)
|
|
$width = $rect.Right - $rect.Left
|
|
$height = $rect.Bottom - $rect.Top
|
|
|
|
Write-Debug "UpperLeft:$($rect.Left),$($rect.Top) LowerBottom:$($rect.Right),$($rect.Bottom). Width:$($width) Height:$($height)"
|
|
|
|
# Load the Windows Forms assembly
|
|
Add-Type -AssemblyName System.Windows.Forms
|
|
$primaryScreen = [System.Windows.Forms.Screen]::PrimaryScreen
|
|
# Check if the primary screen is found
|
|
if ($primaryScreen) {
|
|
# Extract screen width and height for the primary monitor
|
|
$screenWidth = $primaryScreen.Bounds.Width
|
|
$screenHeight = $primaryScreen.Bounds.Height
|
|
|
|
# Print the screen size
|
|
Write-Debug "Primary Monitor Width: $screenWidth pixels"
|
|
Write-Debug "Primary Monitor Height: $screenHeight pixels"
|
|
|
|
# Compare with the primary monitor size
|
|
if ($width -gt $screenWidth -or $height -gt $screenHeight) {
|
|
Write-Debug "The specified width and/or height is greater than the primary monitor size."
|
|
[void][Window]::MoveWindow($windowHandle, 0, 0, $screenWidth, $screenHeight, $True)
|
|
} else {
|
|
Write-Debug "The specified width and height are within the primary monitor size limits."
|
|
}
|
|
} else {
|
|
Write-Debug "Unable to retrieve information about the primary monitor."
|
|
}
|
|
|
|
Invoke-WPFTab "WPFTab1BT"
|
|
$sync["Form"].Focus()
|
|
|
|
# maybe this is not the best place to load and execute config file?
|
|
# maybe community can help?
|
|
if ($PARAM_CONFIG){
|
|
Invoke-WPFImpex -type "import" -Config $PARAM_CONFIG
|
|
if ($PARAM_RUN){
|
|
while ($sync.ProcessRunning) {
|
|
Start-Sleep -Seconds 5
|
|
}
|
|
Start-Sleep -Seconds 5
|
|
|
|
Write-Host "Applying tweaks..."
|
|
Invoke-WPFtweaksbutton
|
|
while ($sync.ProcessRunning) {
|
|
Start-Sleep -Seconds 5
|
|
}
|
|
Start-Sleep -Seconds 5
|
|
|
|
Write-Host "Installing features..."
|
|
Invoke-WPFFeatureInstall
|
|
while ($sync.ProcessRunning) {
|
|
Start-Sleep -Seconds 5
|
|
}
|
|
|
|
Start-Sleep -Seconds 5
|
|
Write-Host "Installing applications..."
|
|
while ($sync.ProcessRunning) {
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
Invoke-WPFInstall
|
|
Start-Sleep -Seconds 5
|
|
|
|
Write-Host "Done."
|
|
}
|
|
}
|
|
|
|
})
|
|
|
|
$sync["CheckboxFilter"].Add_TextChanged({
|
|
|
|
if ($sync.CheckboxFilter.Text -ne "") {
|
|
$sync.CheckboxFilterClear.Visibility = "Visible"
|
|
}
|
|
else {
|
|
$sync.CheckboxFilterClear.Visibility = "Collapsed"
|
|
}
|
|
|
|
$filter = Get-WinUtilVariables -Type CheckBox
|
|
$CheckBoxes = $sync.GetEnumerator() | Where-Object { $psitem.Key -in $filter }
|
|
|
|
foreach ($CheckBox in $CheckBoxes) {
|
|
# Check if the checkbox is null or if it doesn't have content
|
|
if ($CheckBox -eq $null -or $CheckBox.Value -eq $null -or $CheckBox.Value.Content -eq $null) {
|
|
continue
|
|
}
|
|
|
|
$textToSearch = $sync.CheckboxFilter.Text
|
|
$checkBoxName = $CheckBox.Key
|
|
$textBlockName = $checkBoxName + "Link"
|
|
|
|
# Retrieve the corresponding text block based on the generated name
|
|
$textBlock = $sync[$textBlockName]
|
|
|
|
if ($CheckBox.Value.Content.ToLower().Contains($textToSearch)) {
|
|
$CheckBox.Value.Visibility = "Visible"
|
|
# Set the corresponding text block visibility
|
|
if ($textBlock -ne $null) {
|
|
$textBlock.Visibility = "Visible"
|
|
}
|
|
}
|
|
else {
|
|
$CheckBox.Value.Visibility = "Collapsed"
|
|
# Set the corresponding text block visibility
|
|
if ($textBlock -ne $null) {
|
|
$textBlock.Visibility = "Collapsed"
|
|
}
|
|
}
|
|
}
|
|
|
|
})
|
|
|
|
# Define event handler for button click
|
|
$sync["SettingsButton"].Add_Click({
|
|
Write-Debug "SettingsButton clicked"
|
|
if ($sync["SettingsPopup"].IsOpen) {
|
|
$sync["SettingsPopup"].IsOpen = $false
|
|
}
|
|
else {
|
|
$sync["SettingsPopup"].IsOpen = $true
|
|
}
|
|
$_.Handled = $false
|
|
})
|
|
|
|
# Define event handlers for menu items
|
|
$sync["ImportMenuItem"].Add_Click({
|
|
# Handle Import menu item click
|
|
Write-Debug "Import clicked"
|
|
$sync["SettingsPopup"].IsOpen = $false
|
|
Invoke-WPFImpex -type "import"
|
|
$_.Handled = $false
|
|
})
|
|
|
|
$sync["ExportMenuItem"].Add_Click({
|
|
# Handle Export menu item click
|
|
Write-Debug "Export clicked"
|
|
$sync["SettingsPopup"].IsOpen = $false
|
|
Invoke-WPFImpex -type "export"
|
|
$_.Handled = $false
|
|
})
|
|
|
|
$sync["AboutMenuItem"].Add_Click({
|
|
# Handle Export menu item click
|
|
Write-Debug "About clicked"
|
|
$sync["SettingsPopup"].IsOpen = $false
|
|
# Example usage
|
|
$authorInfo = @"
|
|
Author : @christitustech
|
|
Runspace : @DeveloperDurp
|
|
GUI : @KonTy
|
|
MicroWin : @KonTy
|
|
GitHub : https://github.com/ChrisTitusTech/winutil
|
|
Version : $($sync.version)
|
|
"@
|
|
Show-CustomDialog -Message $authorInfo -Width 400
|
|
})
|
|
|
|
$sync["Form"].ShowDialog() | out-null
|
|
Stop-Transcript |