Created
March 1, 2023 11:21
-
-
Save FriedrichWeinmann/3f0c86089e3b70273962ae04d06f89d4 to your computer and use it in GitHub Desktop.
Tools to update speaker settings on windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
function Disable-Privilege { | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory = $true)] | |
[ValidateSet('SeAssignPrimaryTokenPrivilege','SeAuditPrivilege','SeBackupPrivilege','SeChangeNotifyPrivilege','SeCreateGlobalPrivilege','SeCreatePagefilePrivilege','SeCreatePermanentPrivilege','SeCreateSymbolicLinkPrivilege','SeCreateTokenPrivilege','SeDebugPrivilege','SeDelegateSessionUserImpersonatePrivilege','SeEnableDelegationPrivilege','SeImpersonatePrivilege','SeIncreaseBasePriorityPrivilege','SeIncreaseQuotaPrivilege','SeIncreaseWorkingSetPrivilege','SeLoadDriverPrivilege','SeLockMemoryPrivilege','SeMachineAccountPrivilege','SeManageVolumePrivilege','SeProfileSingleProcessPrivilege','SeRelabelPrivilege','SeRemoteShutdownPrivilege','SeRestorePrivilege','SeSecurityPrivilege','SeShutdownPrivilege','SeSyncAgentPrivilege','SeSystemEnvironmentPrivilege','SeSystemProfilePrivilege','SeSystemtimePrivilege','SeTakeOwnershipPrivilege','SeTcbPrivilege','SeTimeZonePrivilege','SeTrustedCredManAccessPrivilege','SeUndockPrivilege','SeUnsolicitedInputPrivilege')] | |
[string] | |
$Privilege | |
) | |
begin { | |
#region Source Code | |
$source = @" | |
using System; | |
using System.Diagnostics; | |
using System.Runtime.InteropServices; | |
using System.Security.Principal; | |
[StructLayout(LayoutKind.Sequential, Pack = 1)] | |
public struct TokPriv1Luid | |
{ | |
public int Count; | |
public long Luid; | |
public int Attr; | |
} | |
public static class Advapi32 | |
{ | |
[DllImport("advapi32.dll", SetLastError=true)] | |
public static extern bool OpenProcessToken( | |
IntPtr ProcessHandle, | |
int DesiredAccess, | |
ref IntPtr TokenHandle); | |
[DllImport("advapi32.dll", SetLastError=true)] | |
public static extern bool LookupPrivilegeValue( | |
string lpSystemName, | |
string lpName, | |
ref long lpLuid); | |
[DllImport("advapi32.dll", SetLastError = true)] | |
public static extern bool AdjustTokenPrivileges( | |
IntPtr TokenHandle, | |
bool DisableAllPrivileges, | |
ref TokPriv1Luid NewState, | |
int BufferLength, | |
IntPtr PreviousState, | |
IntPtr ReturnLength); | |
} | |
public static class Kernel32 | |
{ | |
[DllImport("kernel32.dll")] | |
public static extern uint GetLastError(); | |
} | |
[Flags()] | |
public enum TokenAccess | |
{ | |
AssignPrimary = 0x0001, | |
Duplicate = 0x0002, | |
Impersonate = 0x0004, | |
Query = 0x0008, | |
QuerySource = 0x0010, | |
AdjustPrivileges = 0x0020, | |
AdjustGroups = 0x0040, | |
AdjustDefault = 0x0080, | |
AdjustSessionID = 0x0100, | |
StandardRightsRead = 0x00020000, | |
StandardRightsRequired = 0x000F0000, | |
Read = StandardRightsRead | Query, | |
ModifyRights = Query | AdjustPrivileges, | |
FullControl = AssignPrimary | Duplicate | Impersonate | Query | QuerySource | AdjustPrivileges | AdjustGroups | AdjustDefault | AdjustSessionID | StandardRightsRead | StandardRightsRequired | |
} | |
"@ | |
Add-Type -TypeDefinition $source -ErrorAction Ignore | |
#endregion Source Code | |
} | |
process { | |
$ProcHandle = (Get-Process -Id $pid).Handle | |
$hTokenHandle = [IntPtr]::Zero | |
$null = [Advapi32]::OpenProcessToken($ProcHandle, [TokenAccess]::ModifyRights, [ref]$hTokenHandle) | |
$TokPriv1Luid = [TokPriv1Luid]::new() | |
$TokPriv1Luid.Count = 1 | |
$TokPriv1Luid.Attr = 0x00000000 # SE_PRIVILEGE_DISABLED | |
$LuidVal = $Null | |
$null = [Advapi32]::LookupPrivilegeValue($null, $Privilege, [ref]$LuidVal) | |
$TokPriv1Luid.Luid = $LuidVal | |
$null = [Advapi32]::AdjustTokenPrivileges($hTokenHandle, $False, [ref]$TokPriv1Luid, 0, [IntPtr]::Zero, [IntPtr]::Zero) | |
} | |
} | |
function Enable-Privilege { | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory = $true)] | |
[ValidateSet('SeAssignPrimaryTokenPrivilege','SeAuditPrivilege','SeBackupPrivilege','SeChangeNotifyPrivilege','SeCreateGlobalPrivilege','SeCreatePagefilePrivilege','SeCreatePermanentPrivilege','SeCreateSymbolicLinkPrivilege','SeCreateTokenPrivilege','SeDebugPrivilege','SeDelegateSessionUserImpersonatePrivilege','SeEnableDelegationPrivilege','SeImpersonatePrivilege','SeIncreaseBasePriorityPrivilege','SeIncreaseQuotaPrivilege','SeIncreaseWorkingSetPrivilege','SeLoadDriverPrivilege','SeLockMemoryPrivilege','SeMachineAccountPrivilege','SeManageVolumePrivilege','SeProfileSingleProcessPrivilege','SeRelabelPrivilege','SeRemoteShutdownPrivilege','SeRestorePrivilege','SeSecurityPrivilege','SeShutdownPrivilege','SeSyncAgentPrivilege','SeSystemEnvironmentPrivilege','SeSystemProfilePrivilege','SeSystemtimePrivilege','SeTakeOwnershipPrivilege','SeTcbPrivilege','SeTimeZonePrivilege','SeTrustedCredManAccessPrivilege','SeUndockPrivilege','SeUnsolicitedInputPrivilege')] | |
[string] | |
$Privilege | |
) | |
begin { | |
#region Source Code | |
$source = @" | |
using System; | |
using System.Diagnostics; | |
using System.Runtime.InteropServices; | |
using System.Security.Principal; | |
[StructLayout(LayoutKind.Sequential, Pack = 1)] | |
public struct TokPriv1Luid | |
{ | |
public int Count; | |
public long Luid; | |
public int Attr; | |
} | |
public static class Advapi32 | |
{ | |
[DllImport("advapi32.dll", SetLastError=true)] | |
public static extern bool OpenProcessToken( | |
IntPtr ProcessHandle, | |
int DesiredAccess, | |
ref IntPtr TokenHandle); | |
[DllImport("advapi32.dll", SetLastError=true)] | |
public static extern bool LookupPrivilegeValue( | |
string lpSystemName, | |
string lpName, | |
ref long lpLuid); | |
[DllImport("advapi32.dll", SetLastError = true)] | |
public static extern bool AdjustTokenPrivileges( | |
IntPtr TokenHandle, | |
bool DisableAllPrivileges, | |
ref TokPriv1Luid NewState, | |
int BufferLength, | |
IntPtr PreviousState, | |
IntPtr ReturnLength); | |
} | |
public static class Kernel32 | |
{ | |
[DllImport("kernel32.dll")] | |
public static extern uint GetLastError(); | |
} | |
[Flags()] | |
public enum TokenAccess | |
{ | |
AssignPrimary = 0x0001, | |
Duplicate = 0x0002, | |
Impersonate = 0x0004, | |
Query = 0x0008, | |
QuerySource = 0x0010, | |
AdjustPrivileges = 0x0020, | |
AdjustGroups = 0x0040, | |
AdjustDefault = 0x0080, | |
AdjustSessionID = 0x0100, | |
StandardRightsRead = 0x00020000, | |
StandardRightsRequired = 0x000F0000, | |
Read = StandardRightsRead | Query, | |
ModifyRights = Query | AdjustPrivileges, | |
FullControl = AssignPrimary | Duplicate | Impersonate | Query | QuerySource | AdjustPrivileges | AdjustGroups | AdjustDefault | AdjustSessionID | StandardRightsRead | StandardRightsRequired | |
} | |
"@ | |
Add-Type -TypeDefinition $source -ErrorAction Ignore | |
#endregion Source Code | |
} | |
process { | |
$ProcHandle = (Get-Process -Id $pid).Handle | |
$hTokenHandle = [IntPtr]::Zero | |
$null = [Advapi32]::OpenProcessToken($ProcHandle, [TokenAccess]::ModifyRights, [ref]$hTokenHandle) | |
$TokPriv1Luid = [TokPriv1Luid]::new() | |
$TokPriv1Luid.Count = 1 | |
$TokPriv1Luid.Attr = 0x00000002 # SE_PRIVILEGE_ENABLED | |
$LuidVal = $Null | |
$null = [Advapi32]::LookupPrivilegeValue($null, $Privilege, [ref]$LuidVal) | |
$TokPriv1Luid.Luid = $LuidVal | |
$null = [Advapi32]::AdjustTokenPrivileges($hTokenHandle, $False, [ref]$TokPriv1Luid, 0, [IntPtr]::Zero, [IntPtr]::Zero) | |
} | |
} | |
function Enable-RegistryAccess { | |
[CmdletBinding()] | |
param ( | |
[string] | |
$Path | |
) | |
$rootPath = $Path -replace '^HKLM:\\' | |
$changeData = @{ | |
Owner = $null | |
NewRule = $null | |
} | |
Enable-Privilege -Privilege SeBackupPrivilege | |
Enable-Privilege -Privilege SeRestorePrivilege | |
Enable-Privilege -Privilege SeTakeOwnershipPrivilege | |
if (-not $script:_registryAccess) { | |
$script:_registryAccess = @{ } | |
} | |
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().User | |
# Step 1: Take Ownership | |
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($rootPath, 'ReadWriteSubTree', 'QueryValues') | |
$acl = $key.GetAccessControl() | |
$changeData.Owner = $acl.GetOwner([System.Security.Principal.SecurityIdentifier]) | |
$acl.SetOwner($currentUser) | |
$key.SetAccessControl($acl) | |
$key.Close() | |
# Step 2: Set Access Rights | |
$rule = [System.Security.AccessControl.RegistryAccessRule]::new( | |
$currentUser, | |
[System.Security.AccessControl.RegistryRights]::FullControl, | |
'Allow' | |
) | |
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($rootPath, 'ReadWriteSubTree', 'QueryValues') | |
$acl = $key.GetAccessControl() | |
$acl.AddAccessRule($rule) | |
$key.SetAccessControl($acl) | |
$key.Close() | |
$changeData.NewRule = $rule | |
$script:_registryAccess[$rootPath] = $changeData | |
Disable-Privilege -Privilege SeBackupPrivilege | |
Disable-Privilege -Privilege SeRestorePrivilege | |
Disable-Privilege -Privilege SeTakeOwnershipPrivilege | |
} | |
function Disable-RegistryAccess { | |
[CmdletBinding()] | |
param ( | |
[string] | |
$Path | |
) | |
$rootPath = $Path -replace '^HKLM:\\' | |
Enable-Privilege -Privilege SeBackupPrivilege | |
Enable-Privilege -Privilege SeRestorePrivilege | |
Enable-Privilege -Privilege SeTakeOwnershipPrivilege | |
if (-not $script:_registryAccess) { | |
$script:_registryAccess = @{ } | |
} | |
$config = $script:_registryAccess[$rootPath] | |
# Step 1: Remove new rule | |
if ($config.NewRule) { | |
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($rootPath, 'ReadWriteSubTree', 'QueryValues') | |
$acl = $key.GetAccessControl() | |
$acl.RemoveAccessRuleSpecific($config.NewRule) | |
$key.SetAccessControl($acl) | |
$key.Close() | |
} | |
# Step 2: Restore Ownership | |
if ($config.Owner) { | |
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($rootPath, 'ReadWriteSubTree', 'QueryValues') | |
$acl = $key.GetAccessControl() | |
$acl.SetOwner($config.Owner) | |
$key.SetAccessControl($acl) | |
$key.Close() | |
} | |
$script:_registryAccess.Remove($rootPath) | |
Disable-Privilege -Privilege SeBackupPrivilege | |
Disable-Privilege -Privilege SeRestorePrivilege | |
Disable-Privilege -Privilege SeTakeOwnershipPrivilege | |
} | |
function Get-Speaker { | |
[CmdletBinding()] | |
param ( | |
[string[]] | |
$ComputerName, | |
[PSCredential] | |
$Credential, | |
[string] | |
$Name = '*', | |
[ValidateSet('Enabled', 'Removed', 'Disconnected', 'All')] | |
[string[]] | |
$Type = @('Enabled', 'Disconnected') | |
) | |
begin { | |
#region Code | |
$scriptblock = { | |
param ( | |
$Data | |
) | |
$Name = $Data.Name | |
$Type = $Data.Type | |
$rootKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render" | |
:main foreach ($speakerRoot in Get-ChildItem -Path $rootKey) { | |
$rootProperties = Get-ItemProperty -LiteralPath $speakerRoot.PSPath | |
$properties = Get-ItemProperty -LiteralPath "$($speakerRoot.PSPath)\Properties" | |
if ($properties.'{a45c254e-df1c-4efd-8020-67d146a850e0},2' -notlike $Name) { continue } | |
if ($Type -notcontains 'All') { | |
switch ($rootProperties.DeviceState) { | |
1 { if ($Type -notcontains 'Enabled') { continue main } } | |
4 { if ($Type -notcontains 'Removed') { continue main } } | |
8 { if ($Type -notcontains 'Disconnected') { continue main } } | |
default { continue main } | |
} | |
} | |
$state = switch ($rootProperties.DeviceState) { | |
1 { 'Enabled' } | |
4 { 'Removed' } | |
8 { 'Disconnected' } | |
default { "Unknown ($_)" } | |
} | |
[PSCustomObject]@{ | |
ID = $speakerRoot.PSChildName | |
State = $state | |
# Resolved Properties | |
Name = $properties.'{b3f8fa53-0004-438e-9003-51a46e139bfc},6' | |
DisplayName = $properties.'{a45c254e-df1c-4efd-8020-67d146a850e0},2' | |
Description = $properties.'{b3f8fa53-0004-438e-9003-51a46e139bfc},26' | |
Driver = $properties.'{a8b865dd-2e3d-4094-ad97-e593a70c75d6},5' | |
DriverDetails = $properties.'{83da6326-97a6-4088-9453-a1923f573b29},3' | |
# All Data | |
Properties = $properties | |
ComputerName = $env:COMPUTERNAME | |
} | |
} | |
} | |
#endregion Code | |
} | |
process { | |
$param = @{ | |
ArgumentList = @{ | |
Name = $Name | |
Type = $Type | |
} | |
} | |
if ($ComputerName) { $param.ComputerName = $ComputerName } | |
if ($Credential) { $param.Credential = $Credential } | |
$results = Invoke-Command @param -ScriptBlock $scriptblock | |
$results | |
} | |
} | |
function Set-Speaker { | |
[CmdletBinding()] | |
param ( | |
[Parameter(ValueFromPipelineByPropertyName = $true)] | |
[string] | |
$ComputerName, | |
[PSCredential] | |
$Credential, | |
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] | |
[string] | |
$ID, | |
[string] | |
$DisplayName, | |
[string] | |
$Description | |
) | |
begin { | |
#region Scriptblock | |
$scriptblock = { | |
param ( | |
$Data | |
) | |
foreach ($command in $Data.Commands) { | |
Set-Item -Path "function:\$($command.Name)" -Value $command.Definition | |
} | |
$ID = $Data.ID | |
$DisplayName = $Data.DisplayName | |
$Description = $Data.Description | |
# Enable-Privilege -Privilege SeRestorePrivilege | |
# Enable-Privilege -Privilege SeBackupPrivilege | |
$rootKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render" | |
$speakerRoot = Join-Path -Path $rootKey -ChildPath $ID | |
if (-not (Test-Path -Path $speakerRoot)) { | |
Write-Error "Speaker not found: $ID" | |
return | |
} | |
$propertyRoot = Join-Path -Path $speakerRoot -ChildPath Properties | |
Enable-RegistryAccess -Path $propertyRoot | |
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey(($propertyRoot -replace '^HKLM:\\'), $true) | |
try { | |
if ($DisplayName) { | |
$key.SetValue('{a45c254e-df1c-4efd-8020-67d146a850e0},2', $DisplayName) | |
} | |
if ($Description) { | |
$key.SetValue('{b3f8fa53-0004-438e-9003-51a46e139bfc},26', $Description) | |
} | |
} | |
finally { | |
$key.Close() | |
Disable-RegistryAccess -Path $propertyRoot | |
} | |
} | |
#endregion Scriptblock | |
} | |
process { | |
$param = @{ | |
ArgumentList = @{ | |
ID = $ID | |
DisplayName = $DisplayName | |
Description = $Description | |
Commands = @( | |
Get-Command Enable-Privilege | |
Get-Command Disable-Privilege | |
Get-Command Enable-RegistryAccess | |
Get-Command Disable-RegistryAccess | |
) | |
} | |
} | |
if ($ComputerName) { $param.ComputerName = $ComputerName } | |
if ($Credential) { $param.Credential = $Credential } | |
Invoke-Command @param -ScriptBlock $scriptblock | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment