|
using namespace System.Collections.Generic
|
|
using namespace System.IO
|
|
|
|
# Write verbose messages on import
|
|
if ((Get-PSCallStack)[1].Arguments -imatch 'Verbose=True') { $PSDefaultParameterValues['*:Verbose'] = $true }
|
|
|
|
|
|
if ($env:OS -ne 'Windows_NT') {
|
|
Write-Verbose 'TM-WindowsUtility should only be run on windows.'
|
|
# Do not export any module commands.
|
|
Export-ModuleMember
|
|
return
|
|
}
|
|
# https://github.com/PowerShell/PowerShell/issues/17730#issuecomment-1190678484
|
|
$ExportedMembers = [List[string]]::new()
|
|
$ExportedAliases = [List[string]]::new()
|
|
|
|
|
|
Set-Alias -Name 'which' -Value 'where.exe'
|
|
$ExportedAliases.Add('which')
|
|
|
|
|
|
function Get-COMDetails {
|
|
<#
|
|
.SYNOPSIS
|
|
Gets extended COM file properties.
|
|
|
|
.DESCRIPTION
|
|
This function retrieves the first 1000 extended file properties using COM (Component Object Model) and returns
|
|
the Key/Value pairs in a PSCustomObject.
|
|
|
|
.PARAMETER File
|
|
The FileInfo object that has extended properties to analyze.
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([PSCustomObject])]
|
|
param (
|
|
[Parameter(Mandatory, ValueFromPipeline)]
|
|
[Alias('Path', 'PSPath')]
|
|
[Validation.ValidatePathExists('File')]
|
|
[FileInfo]$File
|
|
)
|
|
|
|
begin { $Output = [PSCustomObject]@{ FullName = $File.FullName } }
|
|
|
|
process {
|
|
$objShell = New-Object -ComObject Shell.Application
|
|
$objFolder = $objShell.Namespace($File.DirectoryName)
|
|
$objFile = $objFolder.ParseName($File.Name)
|
|
|
|
if ($objFile) {
|
|
for ($ColumnNumber = 0; $ColumnNumber -le 1000; $ColumnNumber++) {
|
|
$ColumnName = $null
|
|
$ColumnValue = $null
|
|
$ColumnName = $objFolder.GetDetailsOf($null, $ColumnNumber)
|
|
$ColumnValue = $objFolder.GetDetailsOf($ObjFile, $ColumnNumber)
|
|
if ($ColumnValue) {
|
|
# Ensure we always have a property name.
|
|
if (-Not $ColumnName) { $ColumnName = "ColumnNumber$ColumnNumber" }
|
|
$Output.PSObject.Properties.Add( [PSNoteProperty]::New($ColumnName, $ColumnValue) )
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
end { return $Output }
|
|
}
|
|
$ExportedMembers.Add('Get-COMDetails')
|
|
|
|
|
|
function Get-DNSNamesBySubnet {
|
|
<#
|
|
.SYNOPSIS
|
|
Attempts to retrieve DNS names for devices active within in a specified subnet.
|
|
|
|
.DESCRIPTION
|
|
This function queries DNS names for devices in a specified subnet and returns a list of objects
|
|
containing their IPAddress, DeviceName, and ping statuses.
|
|
|
|
.PARAMETER Subnet
|
|
The subnet to query for DNS names. Should be a valid IP address.
|
|
|
|
.PARAMETER CIDR
|
|
The CIDR value for the subnet (default is 24). Acceptable values are between 24 and 30.
|
|
|
|
.PARAMETER PassThru
|
|
A switch that indicates that the output should be returned as a list of objects instead of a formatted table.
|
|
|
|
.PARAMETER IncludeAllResults
|
|
A switch that includes all results, including for IPs with empty DNS names.
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([void], [PSCustomObject])]
|
|
param (
|
|
[Parameter(
|
|
Position = 0,
|
|
Mandatory,
|
|
ValueFromPipeline,
|
|
ValueFromPipelineByPropertyName
|
|
)]
|
|
[Validation.ValidateIPv4Format()]
|
|
[string]$IPV4Address,
|
|
|
|
[Parameter(
|
|
Position = 1,
|
|
Mandatory = $false,
|
|
ValueFromPipeline
|
|
)]
|
|
[ValidateRange(24, 30)]
|
|
[int]$CIDR = 24,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$PassThru,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$IncludeAllResults
|
|
)
|
|
|
|
begin {
|
|
if ((Test-ApplicationExistsInPath -ApplicationName 'nslookup') -eq $false) {
|
|
throw 'Get-DNSNamesBySubnet requires nslookup to run but it is not available in the PATH.'
|
|
}
|
|
|
|
$FirstThreeOctets = $IPV4Address -replace "$($IPV4Address.Split('.')[-1])$", ''
|
|
[int]$FinalOctet = $IPV4Address -replace "^$FirstThreeOctets", ''
|
|
$Output = [List[object]]::New()
|
|
$CIDRAdressCount = switch ($CIDR) {
|
|
24 { 254 }
|
|
25 { 126 }
|
|
26 { 62 }
|
|
27 { 30 }
|
|
28 { 14 }
|
|
29 { 7 }
|
|
30 { 2 }
|
|
}
|
|
|
|
$End = $FinalOctet + $CIDRAdressCount
|
|
if ($End -gt 255) { $End = 255 }
|
|
}
|
|
|
|
process {
|
|
$JobOutput = if (($env:OS -eq 'Windows_NT') -and (-Not $IsWindows)) {
|
|
$Jobs = for ([int]$i = $FinalOctet;$i -le $End;$i++){
|
|
Start-Job -Name "DNSSearch$i" -ScriptBlock {
|
|
param(
|
|
[int]$InputNumber,
|
|
[string]$FirstThreeOctets,
|
|
[int]$FinalOctet,
|
|
[bool]$IncludeAllResults
|
|
)
|
|
try {
|
|
$Address = $stdout = $DNSName = $null
|
|
$Address = "$FirstThreeOctets$($FinalOctet + $InputNumber)"
|
|
$null = . { nslookup $Address | Set-Variable stdout } 2>&1 | ForEach-Object ToString
|
|
try {
|
|
$selectStringSplat = @{
|
|
InputObject = $stdout
|
|
Pattern = 'Name:'
|
|
SimpleMatch = $true
|
|
ErrorAction = 'Stop'
|
|
}
|
|
$DNSName = (Select-String @selectStringSplat)[0].line.split(':')[-1].trim()
|
|
} catch { }
|
|
|
|
if ($IncludeAllResults -or (-Not [string]::IsNullOrWhiteSpace($DNSName))) {
|
|
return [PSCustomObject]@{
|
|
FinalOctet = ($FinalOctet + $InputNumber)
|
|
IPAddress = $Address
|
|
DeviceName = $DNSName
|
|
Connection = try {(Test-Connection $Address -ErrorAction Stop)} catch { }
|
|
}
|
|
}
|
|
} catch {}
|
|
} -ArgumentList $i,$FirstThreeOctets,$FinalOctet,$IncludeAllResults
|
|
}
|
|
$Jobs | Receive-Job -Wait
|
|
} else {
|
|
$FinalOctet..$End | ForEach-Object -Parallel {
|
|
try {
|
|
$Address = $stdout = $DNSName = $null
|
|
$Address = "$using:FirstThreeOctets$($using:FinalOctet + $_)"
|
|
$null = . { nslookup $Address | Set-Variable stdout } 2>&1 | ForEach-Object ToString
|
|
try {
|
|
$selectStringSplat = @{
|
|
InputObject = $stdout
|
|
Pattern = 'Name:'
|
|
SimpleMatch = $true
|
|
ErrorAction = 'Stop'
|
|
}
|
|
$DNSName = (Select-String @selectStringSplat)[0].line.split(':')[-1].trim()
|
|
} catch { }
|
|
|
|
if ($using:IncludeAllResults -or (-Not [string]::IsNullOrWhiteSpace($DNSName))) {
|
|
return [PSCustomObject]@{
|
|
FinalOctet = ($using:FinalOctet + $_)
|
|
IPAddress = $Address
|
|
DeviceName = $DNSName
|
|
Connection = (Test-Connection $Address)
|
|
}
|
|
}
|
|
} catch { }
|
|
}
|
|
}
|
|
|
|
$JobOutput = $JobOutput | Sort-Object -Property FinalOctet
|
|
foreach ($Job in $JobOutput) {
|
|
$Output.Add(
|
|
[PSCustomObject]@{
|
|
IPAddress = $Job.IPAddress
|
|
DeviceName = $Job.DeviceName
|
|
Ping01 = $Job.Connection[0].status
|
|
Ping02 = $Job.Connection[1].status
|
|
Ping03 = $Job.Connection[2].status
|
|
Ping04 = $Job.Connection[3].status
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
end {
|
|
if ($Output.Length -eq 0) { Write-Host 'No DNS names found.' }
|
|
if ($PassThru) { return $Output } else { ($Output | Format-Table -Property *) }
|
|
}
|
|
}
|
|
$ExportedMembers.Add('Get-DNSNamesBySubnet')
|
|
|
|
|
|
function Get-ShortCutPath {
|
|
<#
|
|
.SYNOPSIS
|
|
Gets the path to the target of a windows shortcut (.lnk / .url) file.
|
|
|
|
.DESCRIPTION
|
|
Uses wscript to retrieve the TargetPath and Arguments, if set, of a windows shortcut (.lnk / .url) file.
|
|
|
|
.PARAMETER Path
|
|
The path(s) to the shortcut file to analyze.
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([System.String])]
|
|
param(
|
|
[Parameter(
|
|
Mandatory,
|
|
ValueFromPipeline,
|
|
ValueFromPipelineByPropertyName,
|
|
Position = 0
|
|
)]
|
|
[Alias('FullName')]
|
|
[string[]]$Path
|
|
)
|
|
|
|
begin { $wShell = New-Object -ComObject WScript.Shell }
|
|
|
|
process {
|
|
foreach ($Item in $Path){
|
|
$ItemPath = [IO.Path]::GetFullPath($Item)
|
|
$ItemExt = [IO.Path]::GetExtension($ItemPath)
|
|
$VerboseMessage = "Processing '$Item'"
|
|
if ($Item -ne $ItemPath) { $VerboseMessage += "as '$ItemPath'" }
|
|
Write-Verbose -Message "$VerboseMessage."
|
|
if ((Test-Path $ItemPath -PathType Leaf) -eq $false) {
|
|
Write-Error "Path '$ItemPath' is not a file that exists."
|
|
continue
|
|
} elseif (($ItemExt -ine '.lnk') -and ($ItemExt -ine '.url')) {
|
|
Write-Error "Path '$ItemPath' does not have the required '.lnk' or '.url' extension!"
|
|
continue
|
|
}
|
|
$shortCut = $wShell.CreateShortcut($ItemPath)
|
|
$Result = $shortCut.TargetPath
|
|
if ($shortCut.Arguments){ $Result += " $($shortCut.Arguments)" }
|
|
Write-Output $Result
|
|
}
|
|
}
|
|
}
|
|
$ExportedMembers.Add('Get-ShortCutPath')
|
|
|
|
|
|
function New-ConsoleArgWriter {
|
|
<#
|
|
.SYNOPSIS
|
|
Creates a new 'ConsoleArgWriter' console application if it does not already exist.
|
|
|
|
.DESCRIPTION
|
|
The New-ConsoleArgWriter function generates an executable, ConsoleArgWriter.exe, in the specified $ShellBinPath.
|
|
Use this executable as a mock for other executables when testing. ConsoleArgWriter.exe will write the input
|
|
arguments to the console, rather than executing the actual application. This is useful for testing and debugging
|
|
scripts that utilize command-line applications when you don't actually want to run the executables.
|
|
|
|
This function checks if the 'ConsoleArgWriter.exe' already exists in the specified path. If it doesn't,
|
|
it uses the Add-Type cmdlet to compile a new console application from a C# script. The new application
|
|
simply writes the provided arguments to the console.
|
|
|
|
To use the executable after running this function, alias the desired executable name to ConsoleArgWriter.exe.
|
|
|
|
For example:
|
|
Set-Alias -Name 'gpg.exe' -Value (Join-Path $ShellPath 'bin' 'ConsoleArgWriter.exe')
|
|
& 'gpg.exe' --pinentry-mode=loopback "--d=$true"
|
|
|
|
In this case, any arguments passed to 'gpg.exe' would be written to the console instead of being passed to the
|
|
actual gpg.exe executable.
|
|
|
|
.PARAMETER ShellBinPath
|
|
The path where the ConsoleArgWriter.exe will be created.
|
|
|
|
.EXAMPLE
|
|
New-ConsoleArgWriter -ShellBinPath C:\temp
|
|
This will create a new ConsoleArgWriter.exe in the C:\temp directory.
|
|
|
|
.NOTES
|
|
The New-ConsoleArgWriter function should be used for debugging and testing purposes. Avoid using it
|
|
in production environments as it could potentially expose sensitive command-line arguments.
|
|
#>
|
|
[CmdletBinding()]
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[Validation.ValidatePathExists('Folder')]
|
|
[string]$ShellBinPath
|
|
)
|
|
|
|
$ArgWriter = Join-Path -Path $ShellBinPath -ChildPath 'ConsoleArgWriter.exe'
|
|
|
|
if ((Test-Path -Path $ArgWriter) -eq $false) {
|
|
if ($env:Path.Split([System.IO.Path]::PathSeparator) -notcontains $ShellBinPath) {
|
|
Write-Warning (
|
|
"The ShellBinPath '$ShellBinPath' is not in your `$Env:Path. To use the ConsoleArgWriter you will need " +
|
|
"to alias the entire path '$ArgWriter'."
|
|
)
|
|
}
|
|
|
|
# Starting this in Windows PowerShell because it can emit console applications.
|
|
$CreateConsoleWriter = ([hashtable]@{
|
|
FilePath = 'powershell.exe'
|
|
NoNewWindow = $true
|
|
WorkingDirectory = $ShellBinPath
|
|
ArgumentList = (
|
|
'-noprofile',
|
|
'-ec',
|
|
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes(@'
|
|
[hashtable]$ConsoleWriterSplat = @{
|
|
OutputType = 'ConsoleApplication'
|
|
Language = 'CSharp'
|
|
OutputAssembly = 'ConsoleArgWriter.exe'
|
|
TypeDefinition = @"
|
|
using System;
|
|
public class Program {
|
|
public static int Main(string[] args) {
|
|
int count = 0;
|
|
foreach (string arg in args) {
|
|
if (null == arg) {
|
|
Console.WriteLine("{0}: null", count);
|
|
} else {
|
|
Console.WriteLine("{0}: \"{1}\"", count, arg);
|
|
}
|
|
count++;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
"@
|
|
}
|
|
Add-Type @ConsoleWriterSplat
|
|
'@)))
|
|
})
|
|
Start-Process @CreateConsoleWriter
|
|
}
|
|
}
|
|
$ExportedMembers.Add('New-ConsoleArgWriter')
|
|
|
|
|
|
function touch {
|
|
<#
|
|
.SYNOPSIS
|
|
Create a new file at the specified path.
|
|
|
|
.DESCRIPTION
|
|
The touch function emulates the Unix touch command and creates a new file at
|
|
the specified path if it does not already exist.
|
|
|
|
.PARAMETER Path
|
|
The path where the new file will be created.
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([Void])]
|
|
param (
|
|
[Parameter(
|
|
Mandatory,
|
|
ValueFromPipeline,
|
|
Position = 0
|
|
)]
|
|
[string]$Path
|
|
)
|
|
process {
|
|
New-Item -ItemType File -Path $Path
|
|
}
|
|
}
|
|
$ExportedMembers.Add('touch')
|
|
|
|
|
|
function tail {
|
|
<#
|
|
.SYNOPSIS
|
|
Display the last 'n' lines of a file.
|
|
|
|
.DESCRIPTION
|
|
The tail function emulates the Unix tail command and displays the last 'n' lines of a file.
|
|
|
|
.PARAMETER Path
|
|
The path of the file to display.
|
|
|
|
.PARAMETER LiteralPath
|
|
The literal path of the file to display.
|
|
|
|
.PARAMETER follow
|
|
A switch that indicates that the function should follow the file as it grows.
|
|
|
|
.PARAMETER lines
|
|
The number of lines to display from the end of the file. Default is 10.
|
|
|
|
.EXAMPLE
|
|
# Use Control C to exit the function and stop tailing the file.
|
|
tail -f 'C:\Path\To\File.txt'
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([Void])]
|
|
param (
|
|
[Parameter(
|
|
Mandatory,
|
|
ValueFromPipeline,
|
|
ParameterSetName = 'Path',
|
|
Position = 0
|
|
)]
|
|
[Validation.ValidatePathExists('File')]
|
|
[FileInfo]$Path,
|
|
|
|
[Parameter(
|
|
Mandatory,
|
|
ValueFromPipeline,
|
|
ParameterSetName = 'LiteralPath',
|
|
Position = 0
|
|
)]
|
|
[Validation.ValidatePathExists('File')]
|
|
[string]$LiteralPath,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$follow,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[Alias('n')]
|
|
[int]$lines = 10
|
|
)
|
|
|
|
$ParameterSplat = @{}
|
|
|
|
if ($Path) { $ParameterSplat.Add('Path', $Path) }
|
|
if ($LiteralPath) { $ParameterSplat.Add('LiteralPath', $LiteralPath) }
|
|
if ($follow) { $ParameterSplat.Add('Wait', $true) }
|
|
$ParameterSplat.Add('Tail', $lines)
|
|
|
|
Get-Content @ParameterSplat
|
|
}
|
|
$ExportedMembers.Add('tail')
|
|
|
|
|
|
function Search-Registry {
|
|
<#
|
|
.SYNOPSIS
|
|
Search the Windows Registry for key names, key value names, or key value data matching specified criteria.
|
|
|
|
.DESCRIPTION
|
|
The Search-Registry function allows you to search the Windows Registry for
|
|
- Key names
|
|
- Value names
|
|
- Value data
|
|
It outputs PSCustomObjects that contain the key, matched content type (KeyName, ValueName, or ValueData), and the
|
|
matching content.
|
|
|
|
.PARAMETER Path
|
|
The registry path to start the search from.
|
|
|
|
.PARAMETER Recurse
|
|
Indicates whether to search in subkeys.
|
|
|
|
.PARAMETER SearchRegex
|
|
The regular expression used as the search criteria.
|
|
|
|
.PARAMETER KeyName
|
|
Used in conjunction with the SearchRegex parameter. Specifies that key names will be tested.
|
|
By default, when the SingleSearchString switches have not been specified, all content types are evaluated.
|
|
When using this switch, only the specified type(s) (KeyName, ValueName, and/or ValueData) will be evaluated.
|
|
|
|
.PARAMETER ValueName
|
|
Used in conjunction with the SearchRegex parameter. Specifies that the value names will be tested.
|
|
By default, when the SingleSearchString switches have not been specified, all content types are evaluated.
|
|
When using this switch, only the specified type(s) (KeyName, ValueName, and/or ValueData) will be evaluated.
|
|
|
|
.PARAMETER ValueData
|
|
Used in conjunction with the SearchRegex parameter. Specifies that the value data will be tested.
|
|
By default, when the SingleSearchString switches have not been specified, all content types are evaluated.
|
|
When using this switch, only the specified type(s) (KeyName, ValueName, and/or ValueData) will be evaluated.
|
|
|
|
.PARAMETER KeyNameRegex
|
|
Specifies a regex that will be checked against key names only.
|
|
|
|
.PARAMETER ValueNameRegex
|
|
Specifies a regex that will be checked against value names only.
|
|
|
|
.PARAMETER ValueDataRegex
|
|
Specifies a regex that will be checked against value data only.
|
|
|
|
.EXAMPLE
|
|
# Search for key names matching "Microsoft," "Adobe," or "Google" under the specified registry path that has value
|
|
# data consisting of only numeric characters.
|
|
Search-Registry -Path HKLM:\SOFTWARE -KeyNameRegex "Microsoft|Adobe|Google" -ValueDataRegex "^\d+$"
|
|
|
|
.EXAMPLE
|
|
# Recursively search for value names matching "DNS*" or "IP*" under the given path, including subkeys.
|
|
Search-Registry -Path HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters -Recurse -ValueNameRegex "DNS*|IP*"
|
|
|
|
.EXAMPLE
|
|
# Search for key names containing the word "Security" under the specified registry path.
|
|
Search-Registry -Path HKCU:\Software -KeyNameRegex "Security"
|
|
|
|
.EXAMPLE
|
|
# Search for keys with empty value data (no data) under the specified registry path.
|
|
Search-Registry -Path HKCU:\Software -ValueDataRegex "^$"
|
|
|
|
.EXAMPLE
|
|
# Search for value names containing "Program Files" under the specified registry path.
|
|
Search-Registry -Path HKLM:\SOFTWARE -ValueNameRegex "Program Files"
|
|
|
|
.EXAMPLE
|
|
# Find key names that match "ProgramKeyName". This will not evaluate either ValueNames or ValueData.
|
|
Search-Registry -Path HKLM:\SOFTWARE -SearchRegex "ProgramKeyName" -KeyName
|
|
#>
|
|
[CmdletBinding()]
|
|
param (
|
|
[Alias('PsPath')]
|
|
[Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName)]
|
|
[string[]]$Path,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$Recurse,
|
|
|
|
[Parameter(ParameterSetName = 'SingleSearchString', Mandatory)]
|
|
[string]$SearchRegex,
|
|
|
|
[Parameter(ParameterSetName = 'SingleSearchString', Mandatory = $false)]
|
|
[switch]$KeyName,
|
|
|
|
[Parameter(ParameterSetName = 'SingleSearchString', Mandatory = $false)]
|
|
[switch]$ValueName,
|
|
|
|
[Parameter(ParameterSetName = 'SingleSearchString', Mandatory = $false)]
|
|
[switch]$ValueData,
|
|
|
|
[Parameter(ParameterSetName = 'MultipleSearchStrings', Mandatory = $false)]
|
|
[string]$KeyNameRegex,
|
|
|
|
[Parameter(ParameterSetName = 'MultipleSearchStrings', Mandatory = $false)]
|
|
[string]$ValueNameRegex,
|
|
|
|
[Parameter(ParameterSetName = 'MultipleSearchStrings', Mandatory = $false)]
|
|
[string]$ValueDataRegex
|
|
)
|
|
|
|
begin {
|
|
if ($PSCmdlet.ParameterSetName -eq 'SingleSearchString') {
|
|
$NoSwitchesSpecified = $false -eq (
|
|
$PSBoundParameters.ContainsKey('KeyName') -or
|
|
$PSBoundParameters.ContainsKey('ValueName') -or
|
|
$PSBoundParameters.ContainsKey('ValueData')
|
|
)
|
|
if ($KeyName -or $NoSwitchesSpecified) { $KeyNameRegex = $SearchRegex }
|
|
if ($ValueName -or $NoSwitchesSpecified) { $ValueNameRegex = $SearchRegex }
|
|
if ($ValueData -or $NoSwitchesSpecified) { $ValueDataRegex = $SearchRegex }
|
|
}
|
|
}
|
|
|
|
process {
|
|
foreach ($CurrentPath in $Path) {
|
|
foreach ($Key in (Get-ChildItem $CurrentPath -Recurse:$Recurse)) {
|
|
if ($KeyNameRegex) {
|
|
Write-Verbose "$($Key.Name): Checking KeyNamesRegex"
|
|
if ($Key.PSChildName -match $KeyNameRegex) {
|
|
Write-Verbose "$($Key.Name): Found KeyName match for `"$KeyNameRegex`"!"
|
|
Write-Output -InputObject ([PSCustomObject]@{
|
|
Key = $Key
|
|
ContentMatch = 'KeyName'
|
|
MatchContent = $Key.PSChildName
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
if ($ValueNameRegex -or $ValueDataRegex) {
|
|
foreach ($KeyValueName in $Key.GetValueNames()) {
|
|
if ($ValueNameRegex) {
|
|
Write-Verbose "$($Key.Name): Checking ValueNamesRegex"
|
|
if ($KeyValueName -match $ValueNameRegex) {
|
|
Write-Verbose "$($Key.Name): Found ValueName match for `"$ValueNameRegex`"!"
|
|
Write-Output -InputObject ([PSCustomObject]@{
|
|
Key = $Key
|
|
ContentMatch = 'ValueName'
|
|
MatchContent = $KeyValueName
|
|
}
|
|
)
|
|
}
|
|
}
|
|
if ($ValueDataRegex) {
|
|
Write-Verbose "$($Key.Name): Checking ValueDataRegex"
|
|
$KeyValueData = $Key.GetValue($KeyValueName)
|
|
if ($KeyValueData -match $ValueDataRegex) {
|
|
Write-Verbose "$($Key.Name): Found ValueData match for `"$ValueDataRegex`"!"
|
|
Write-Output -InputObject ([PSCustomObject] @{
|
|
Key = $Key
|
|
ContentMatch = 'ValueData'
|
|
MatchContent = $KeyValueData
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$ExportedMembers.Add('Search-Registry')
|
|
|
|
|
|
function Set-WindowFocus {
|
|
<#
|
|
.SYNOPSIS
|
|
Sets the focus on a specified window.
|
|
|
|
.DESCRIPTION
|
|
This function shifts focus to a specified window by utilizing either the process name or the process object.
|
|
By invoking Windows APIs, the function manages the visibility to show or hide GUI process windows.
|
|
|
|
.PARAMETER ProcessName
|
|
The name of the process whose window you want to focus.
|
|
|
|
.PARAMETER Process
|
|
The process object whose window you want to focus.
|
|
|
|
.PARAMETER Minimize
|
|
A switch that indicates if the window should be minimized after setting the focus.
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([Void])]
|
|
param (
|
|
[Parameter(
|
|
Mandatory,
|
|
ValueFromPipeline,
|
|
ParameterSetName = 'ProcessName',
|
|
Position = 0
|
|
)]
|
|
[string]$ProcessName,
|
|
|
|
[Parameter(
|
|
Mandatory,
|
|
ValueFromPipeline,
|
|
ParameterSetName = 'Process',
|
|
Position = 0
|
|
)]
|
|
[ComponentModel.Component]$Process,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$Minimize
|
|
)
|
|
|
|
# https://stackoverflow.com/a/58548853
|
|
Add-Type -Namespace ProfileUtility -Name WindowVisibility -MemberDefinition @'
|
|
[DllImport("user32.dll", SetLastError=true)]
|
|
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
[DllImport("user32.dll", SetLastError=true)]
|
|
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
[DllImport("user32.dll", SetLastError=true)]
|
|
public static extern bool IsIconic(IntPtr hWnd);
|
|
'@
|
|
|
|
switch ($PSCmdlet.ParameterSetName) {
|
|
'ProcessName' {
|
|
$ProcessName = $ProcessName -replace '\.exe$'
|
|
$Processes = (Get-Process -ErrorAction Ignore $ProcessName)
|
|
if ($Processes.Count -gt 1) {
|
|
$Index = 0
|
|
$ProcessList = Foreach ($Process in $Processes) {
|
|
[PSCustomObject]@{
|
|
Number = $Index
|
|
WindowTitle = $Process.MainWindowTitle
|
|
}
|
|
++$Index
|
|
}
|
|
$PromptAnswer = Read-Host -Prompt (
|
|
"Found multiple processes for '$ProcessName'. Which would you like to select.`n" +
|
|
($ProcessList | Format-Table | Out-String)
|
|
)
|
|
$hWnd = $Processes[$PromptAnswer].MainWindowHandle
|
|
} else {
|
|
try { $hWnd = $Processes[0].MainWindowHandle } catch { <# Do Nothing #> }
|
|
}
|
|
}
|
|
'Process' { $hWnd = $Process.MainWindowHandle }
|
|
}
|
|
|
|
if (-not $hWnd) { Throw 'Failed to retrieve MainWindowHandle.' }
|
|
|
|
# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
|
if ($Minimize) {
|
|
[ProfileUtility.WindowVisibility]::ShowWindow($hwnd, 6) | Out-Null
|
|
} else {
|
|
# Set the focus on the window.
|
|
[ProfileUtility.WindowVisibility]::SetForegroundWindow($hWnd) | Out-Null
|
|
# If the window is minimized, restore it.
|
|
if ([ProfileUtility.WindowVisibility]::IsIconic($hwnd)) {
|
|
[ProfileUtility.WindowVisibility]::ShowWindow($hwnd, 9) | Out-Null
|
|
}
|
|
}
|
|
}
|
|
$ExportedMembers.Add('Set-WindowFocus')
|
|
|
|
|
|
function Wait-Active {
|
|
<#
|
|
.SYNOPSIS
|
|
Keeps the computer active for a specified amount of time.
|
|
|
|
.DESCRIPTION
|
|
This function opens Notepad and sends a key press to keep the computer active.
|
|
The duration can be specified in minutes, as a TimeSpan, or as a DateTime.
|
|
|
|
.PARAMETER StopTime
|
|
The DateTime at which the function should stop keeping the computer active.
|
|
|
|
.PARAMETER Duration
|
|
The TimeSpan duration to keep the computer active.
|
|
|
|
.PARAMETER Minutes
|
|
The number of minutes to keep the computer active.
|
|
|
|
.PARAMETER SleepSeconds
|
|
The number of seconds to sleep between each activity (default is 60 seconds).
|
|
#>
|
|
[CmdletBinding(DefaultParameterSetName = 'Minutes')]
|
|
[OutputType([Void])]
|
|
param (
|
|
[Parameter(
|
|
Position = 0,
|
|
ParameterSetName = 'DateTime',
|
|
Mandatory = $false,
|
|
ValueFromPipeline,
|
|
ValueFromPipelineByPropertyName
|
|
)]
|
|
[DateTime]$StopTime = (Get-Date).AddMinutes(5),
|
|
|
|
[Parameter(
|
|
Position = 0,
|
|
ParameterSetName = 'TimeSpan',
|
|
Mandatory = $false,
|
|
ValueFromPipeline,
|
|
ValueFromPipelineByPropertyName
|
|
)]
|
|
[ValidateRange(0, [int]::MaxValue)]
|
|
[TimeSpan]$Duration = (New-TimeSpan -Start (Get-Date) -End (Get-Date).AddMinutes(5)),
|
|
|
|
[Parameter(
|
|
Position = 0,
|
|
ParameterSetName = 'Minutes',
|
|
Mandatory = $false,
|
|
ValueFromPipeline,
|
|
ValueFromPipelineByPropertyName
|
|
)]
|
|
[ValidateRange(0, [int]::MaxValue)]
|
|
[int]$Minutes = 5,
|
|
|
|
[Parameter(
|
|
Position = 1,
|
|
Mandatory = $false,
|
|
ValueFromPipelineByPropertyName
|
|
)]
|
|
[ValidateRange(0, [int]::MaxValue)]
|
|
[Int]$SleepSeconds = 60
|
|
)
|
|
|
|
$WaitTill = switch ($PSCmdlet.ParameterSetName) {
|
|
'DateTime' { $StopTime }
|
|
'TimeSpan' { (Get-Date).AddSeconds($timespan.TotalSeconds) }
|
|
'Minutes' { (Get-Date).AddMinutes($Minutes) }
|
|
}
|
|
$Process = Start-Process 'Notepad.exe' -PassThru
|
|
|
|
Start-Sleep -Seconds 5
|
|
do {
|
|
if ((Get-Process -InputObject $Process).HasExited) {
|
|
Write-Warning 'Notepad has exited.'
|
|
return
|
|
}
|
|
Set-WindowFocus $Process
|
|
(New-Object -ComObject wscript.shell).SendKeys('.')
|
|
Set-WindowFocus $Process -Minimize
|
|
Start-Sleep -Seconds $SleepSeconds
|
|
$CurrentTime = Get-Date
|
|
} while ($CurrentTime -lt $WaitTill)
|
|
|
|
Stop-Process $Process
|
|
}
|
|
$ExportedMembers.Add('Wait-Active')
|
|
|
|
|
|
Export-ModuleMember -Function $ExportedMembers.ToArray() -Alias $ExportedAliases.ToArray()
|