functions/KeyboardLayout.ps1
$modulePath = (get-item (Split-Path -parent $MyInvocation.MyCommand.Path)).Parent.FullName # check os version $private:Ver = ([System.Environment]::OSVersion).Version switch (('{0}.{1}' -f $Ver.Major, $Ver.Minor)) { '6.1' { $OS = '7' } #Win7 '10.0' { $OS = '10'} #Win10 } $private:lang = Join-Path $modulePath ('lang_w{0}.json' -f $OS) $private:kbl = Join-Path $modulePath ('kbl_w{0}.json' -f $OS) if(Test-Path $lang){ $LanguageList = gc $lang | ConvertFrom-Json -ea SilentlyContinue } if(Test-Path $kbl){ $KeyboardList = gc $kbl | ConvertFrom-Json -ea SilentlyContinue } function Convert-KblNameToId { [CmdletBinding()] Param( [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName='Name')] [Alias('Tag')] [string]$LanguageTag, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName='Name')] [Alias('Name')] [string]$KeyboardLayoutName ) Process{ $private:langId = $LanguageList.where({$_.tag -eq $LanguageTag}).Id $private:kblId = $KeyboardList.where({$_.name -eq $KeyboardLayoutName}).Id if($langId -and $kblId){ ("{0}:{1}" -f $langId, $kblId) } } } function Convert-LangIdToTag { [CmdletBinding()] Param( [Parameter(Mandatory, ValueFromPipeline, Position=0)] [Alias('Id')] [string]$LanguageId ) Process{ @($LanguageList.where({$_.id -eq $LanguageId}).tag)[0] } } function Convert-KblIdToName { [CmdletBinding()] Param( [Parameter(Mandatory, ValueFromPipeline, Position=0)] [Alias('Id')] [string]$KeyboardLayoutId ) Process{ @($KeyboardList.where({$_.name -eq $KeyboardLayoutId}).id)[0] } } function New-GsXml { [CmdletBinding()] Param( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidatePattern('^[0-9a-f]{4}:[0-9a-f\-\{\}]{8,}$')] [string]$KeyboardLayoutId, [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('add','remove')] [string]$Action = 'add', [Parameter(ValueFromPipelineByPropertyName)] [bool]$Default = $false, [switch]$CopySettingsToDefaultUserAcct, [switch]$CopySettingsToSystemAcct ) Begin{ $local:ConstantData = @{ GsBaseXml = @' <gs:GlobalizationServices xmlns:gs="urn:longhornGlobalizationUnattend"> <gs:UserList> <gs:User UserID="Current" CopySettingsToDefaultUserAcct="{0}" CopySettingsToSystemAcct="{1}"/> </gs:UserList> <gs:InputPreferences> {2} </gs:InputPreferences> </gs:GlobalizationServices> '@ GsKbXmlRaw = '<gs:InputLanguageID Action="{0}" ID="{1}" Default="{2}"/>' } $KblRaws = [string[]]@() } Process{ $KblRaws += ($ConstantData.GsKbXmlRaw -f $Action, $KeyboardLayoutId, $Default.ToString().ToLower()) } End{ [Xml]($ConstantData.GsBaseXml -f $CopySettingsToDefaultUserAcct.ToString().ToLower(), $CopySettingsToSystemAcct.ToString().ToLower(), [string]$KblRaws) } } <# .Synopsis Add or Remove keyboard layout .DESCRIPTION Add or Remove keyboard layout to current user & computer. This cmdlet support Windows 7 & 10 system only. .PARAMETER KeyboardLayoutId Specify keyboard layout ID (KLID) KLID is special formatted string like "0409:00000409" (Lang tag:Layout id) If you don't know desired KLID, you can refer to JSON files in this module's root directory or Use Get-KeyboardLayout cmdlet (or Google it). .PARAMETER LanguageTag If you don't wont see unsuitable KLID, Use LanguageTag and KeyboardLayoutName arguments. You can use friendly name to specify keyboard layout instead of KLID. Specify LanguageTag for IETF Language Tag like "en-US" or "fr-FR" .PARAMETER KeyboardLayoutName Specify name of keyboard layout. The list of all available layout names are written in JSON file that saved in this module's root directory e.g.) "United States-International" , "Chinese (Traditional) - US Keyboard" .PARAMETER Action You can choose "add" or "remove". default: 'add' .PARAMETER Default If this param set to $true, keyboard layout will configure to default keyboard of current user. default: False .PARAMETER CopySettingsToDefaultUserAcct Copy settings to default user account profile. This parameter only works on Windows 7 systems. default: False .PARAMETER CopySettingsToSystemAcct Copy settings to system account profile. This parameter only works on Windows 7 systems. default: False .PARAMETER ClearExist Clear existence keyboard layouts. This parameter only works on Windows 10 systems. default: False .EXAMPLE Add "US - US Keyboard" using KLID Set-KeyboardLayout -KeyboardLayoutId "0409:00000409" .EXAMPLE Add "Chinese (Taiwan) - Chinese Simplified QuanPin" using friendly name Set-KeyboardLayout -LanguageTag 'zh-TW' -KeyboardLayoutName "Chinese Simplified QuanPin" #> function Set-KeyboardLayout { [CmdletBinding(DefaultParameterSetName='ID')] Param( [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName='ID')] [ValidatePattern('^[0-9a-f]{4}:[0-9a-f\-\{\}]{8,}$')] [Alias('Id')] [string]$KeyboardLayoutId, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName='Name')] [ValidateScript({$LanguageList.tag -eq $_})] [Alias('Tag')] [Alias('Language')] [string]$LanguageTag, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName='Name')] [ValidateScript({$KeyboardList.name -eq $_})] [Alias('Name')] [string]$KeyboardLayoutName, [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('add','remove')] [string]$Action = 'add', [Parameter(ValueFromPipelineByPropertyName)] [bool]$Default = $false, [switch]$CopySettingsToDefaultUserAcct, # Win7 only [switch]$CopySettingsToSystemAcct, # Win7 only [switch]$ClearExist # Win10 only ) Begin{ $KblRaws = @() switch ($OS) { '7' { if($PSBoundParameters.ClearExist){ Write-Warning ('"ClearExist" parameter works only Windows 10 systems !') } } '10' { if($PSBoundParameters.CopySettingsToDefaultUserAcct){ Write-Warning ('"CopySettingsToDefaultUserAcct" parameter works only Windows 7 systems !') } if($PSBoundParameters.CopySettingsToSystemAcct){ Write-Warning ('"CopySettingsToSystemAcct" parameter works only Windows 7 systems !') } } Default { Write-Warning ('Non supported Operating System !') } } } Process{ if($PSCmdlet.ParameterSetName -eq 'ID'){ $LanguageTag = Convert-LangIdToTag $KeyboardLayoutId.Substring(0, 4) $KeyboardLayoutName = Convert-KblIdToName $KeyboardLayoutId.Substring(5) } if($LanguageTag -and $KeyboardLayoutName){ $kblRaws += [PSCustomObject]@{ LanguageTag = $LanguageTag KeyboardLayoutName = $KeyboardLayoutName Action = $Action Default = $Default } Write-Verbose ('Adding Keyboard (Lang:"{0}" / Layout:"{1}")' -f $LanguageTag,$KeyboardLayoutName) } elseif ($KeyboardLayoutId) { $kblRaws += [PSCustomObject]@{ KeyboardLayoutId = $KeyboardLayoutId Action = $Action Default = $Default } Write-Verbose ('Adding Keyboard (Id:"{0}")' -f $KeyboardLayoutId) } } End { switch ($OS) { '7' { $kblRaws | Set-KeyboardLayout-Win7 -CopySettingsToDefaultUserAcct:$CopySettingsToDefaultUserAcct -CopySettingsToSystemAcct:$CopySettingsToSystemAcct } '10' { $KblRaws | Set-KeyboardLayout-Win10 -ClearExist:$ClearExist } Default { Write-Error ('Non supported Operating System !') $KblRaws | Set-KeyboardLayout-Win10 -ClearExist:$ClearExist } } } } function Set-KeyboardLayout-Win7 { [CmdletBinding(DefaultParameterSetName='ID')] Param( [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName='ID')] [ValidatePattern('^[0-9a-f]{4}:[0-9a-f\-\{\}]{8,}$')] [Alias('Id')] [string]$KeyboardLayoutId, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName='Name')] [ValidateScript({$LanguageList.tag -eq $_})] [Alias('Tag')] [Alias('Language')] [string]$LanguageTag, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName='Name')] [ValidateScript({$KeyboardList.name -eq $_})] [Alias('Name')] [string]$KeyboardLayoutName, [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('add','remove')] [string]$Action = 'add', [Parameter(ValueFromPipelineByPropertyName)] [bool]$Default = $false, [switch]$CopySettingsToDefaultUserAcct, [switch]$CopySettingsToSystemAcct ) Begin{ $KblRaws = @() } Process{ if($PSCmdlet.ParameterSetName -eq 'Name'){ $KeyboardLayoutId = Convert-KblNameToId -LanguageTag $LanguageTag -KeyboardLayoutName $KeyboardLayoutName } if($KeyboardLayoutId){ $kblRaws += [pscustomobject]@{ KeyboardLayoutId = $KeyboardLayoutId Action = $Action Default = $Default } } } End{ try{ if($kblRaws.count -le 0){ Write-Error 'Set keyboard layout failed. invalid arguments' } $GsXml = [string]($kblRaws | New-GsXml -CopySettingsToDefaultUserAcct:$CopySettingsToDefaultUserAcct -CopySettingsToSystemAcct:$CopySettingsToSystemAcct -ErrorAction Stop).OuterXml $tmpFile = New-Item (Join-Path $env:TEMP ('{0}\gstmp.xml' -f [guid]::NewGuid())) -ItemType File -Force -ErrorAction Stop $GsXml | Out-File -FilePath $tmpFile -Encoding utf8 -Force if(Test-Path $tmpFile){ $StartTime = [datetime]::Now $Process = Start-Command -FilePath 'control.exe' -ArgumentList ('intl.cpl,, /f:"{0}"' -f $tmpFile) -Timeout (300 * 1000) $error = Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-International/Operational'; StartTime=$StartTime} -MaxEvents 5 -Verbose:$false -ErrorAction SilentlyContinue | where {$_.Level -ne 4} | select -First 1 ($kblRaws | where {$_.Action -eq 'add'}).KeyboardLayoutId | Set-KeyboardLayoutRegistry if($error){ Write-Error $error.Message } else{ Write-Verbose 'Set keyboard layout completed successfully' } } else{ Write-Error 'Set keyboard layout failed. xml not found' } } catch [Exception]{ Write-Error $_.Exception.Message } finally{ if($tmpFile -and (Test-Path $tmpFile.Directory)){ Remove-Item $tmpFile.Directory -Recurse -Force -ErrorAction Continue } } } } function Set-KeyboardLayout-Win10 { [CmdletBinding()] Param( [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ID')] [ValidatePattern('^[0-9a-f]{4}:[0-9a-f\-\{\}]{8,}$')] [Alias('Id')] [string]$KeyboardLayoutId, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Alias('Tag')] [Alias('Language')] [string]$LanguageTag, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Name')] [Alias('Name')] [string]$KeyboardLayoutName, [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('add', 'remove')] [string]$Action = 'add', [Parameter(ValueFromPipelineByPropertyName)] [bool]$Default = $false, [switch]$ClearExist ) Begin { if ($ClearExist) { $KblList = New-Object -TypeName (New-WinUserLanguageList -Language 'en-US').GetType().FullName } else { $KblList = Get-WinUserLanguageList } $DefaultKbl = '' } Process { if ($PSCmdlet.ParameterSetName -eq 'Name') { $KeyboardLayoutId = Convert-KblNameToId -LanguageTag $LanguageTag -KeyboardLayoutName $KeyboardLayoutName } if (-not $LanguageTag) { $LanguageTag = Convert-LangIdToTag $KeyboardLayoutId.Substring(0, 4) } if ($private:LangObj = $KblList.Find( {$args.LanguageTag -eq $LanguageTag})) { #既に言語は存在するのでキーボードレイアウトだけを追加or削除する switch ($Action) { 'add' { if ($LangObj.InputMethodTips.FindIndex( {$args -eq $KeyboardLayoutId}) -lt 0) { Write-Verbose ('Installing keyboard layout ({0})' -f $KeyboardLayoutId) $LangObj.InputMethodTips.Add($KeyboardLayoutId) } else{ Write-Verbose 'The keyboard layout is already exist.' } } 'remove' { $index = $LangObj.InputMethodTips.FindIndex( {$args -eq $KeyboardLayoutId}) if ($index -ge 0) { $LangObj.InputMethodTips.RemoveAt($index) } if ($LangObj.InputMethodTips.Count -eq 0) { #レイアウトを消した結果その言語にレイアウトが一つもなくなった場合、言語自体も消す $index = $KblList.FindIndex( {$args.LanguageTag -eq $LanguageTag}) $KblList.RemoveAt($index) } } } } else { #言語+キーボードレイアウトを追加する(削除は何もする必要なし) switch ($Action) { 'add' { $private:Lang = New-WinUserLanguageList $LanguageTag -ea SilentlyContinue if ((-not $Lang) -or (-not $Lang.EnglishName)) { Write-Error ('"{0}" is not valid LanguageTag' -f $LanguageTag) } else { if ($Lang[0].InputMethodTips.FindIndex( {$args -eq $KeyboardLayoutId}) -lt 0) { Write-Verbose ('Installing keyboard layout ({0})' -f $KeyboardLayoutId) $Lang[0].InputMethodTips.Clear() $Lang[0].InputMethodTips.Add($KeyboardLayoutId) } $KblList.Add($Lang[0]) } } 'remove' { # nothing to do Write-Verbose 'The keyboard layout is already removed. Nothing need to do.' } } } if ($Default -and ($Action) -eq 'add') { $DefaultKbl = $KeyboardLayoutId } } End { if ($KblList.Count -ge 1) { Set-WinUserLanguageList $KblList -Force Write-Verbose ('Keyboard layouts installed successfully') if ($DefaultKbl) { Write-Verbose ('Set default keyboard layout ({0})' -f $DefaultKbl) Set-WinDefaultInputMethodOverride $DefaultKbl } } else { Write-Error ('No language will remain.') } } } <# .Synopsis Get installed keyboard layout .DESCRIPTION Get installed keyboard layout id (KLID) .PARAMETER Target You can select 'CurrentUser' or 'System' default: 'CurrentUser' #> function Get-KeyboardLayout { [CmdletBinding()] [OutputType([System.String[]])] Param( [ValidateSet('CurrentUser','System')] [string]$Target = 'CurrentUser' ) $RegKeyLanguage = 'HKCU:\Software\Microsoft\CTF\SortOrder\Language' $RegKeyLayout = 'HKCU:\Software\Microsoft\CTF\SortOrder\AssemblyItem' $RegKeyIdMap = 'HKLM:\SYSTEM\CurrentControlSet\Control\Keyboard Layouts' $MysteriousGUID = '{34745C63-B2F0-4784-8B67-5E12C8701A31}' if($Target -eq 'CurrentUser'){ if(-not (Test-Path $RegKeyLanguage)){ Write-Error 'Registry key not found.' return } $KeyIdMap = @{} Get-ChildItem $RegKeyIdMap | Get-ItemProperty | where {$_.'Layout Id'} | % {$KeyIdMap.Add($_.'Layout Id', $_.PsChildName)} $RegLangs = Get-Item $RegKeyLanguage $InstalledLangsId = $RegLangs.GetValueNames() | sort | foreach {$RegLangs.GetValue($_)} $InstalledLayoutId = New-Object 'System.Collections.Generic.List[string]' foreach($lang in $InstalledLangsId){ $private:RegKeyTmp1 = (Join-Path $RegKeyLayout ("0x$lang\$MysteriousGUID")) if(Test-Path $RegKeyTmp1){ $private:counter = 0 while($true){ $private:RegKeyTmp2 = Join-Path $RegKeyTmp1 $counter.ToString("00000000") if(Test-Path $RegKeyTmp2){ $private:tmp = (get-item $RegKeyTmp2).GetValue('KeyboardLayout') if($tmp){ $private:keyId = $tmp.ToString('X').PadLeft(8, '0').Substring(0,4) # $private:langId = $tmp.ToString('X').PadLeft(8, '0').Substring(4,4) if($keyId -and ($keyId.StartsWith('F'))){ $KeyId = ("0{0}" -f $KeyId.Substring(1)) $layoutId = $KeyIdMap.$Keyid } else{ $layoutId = $keyId.PadLeft(8, '0') } $InstalledLayoutId.Add(('{0}:{1}' -f $lang.Substring(4,4), $layoutId).ToUpper()) } elseif($tmp -eq 0){ $private:CLSID = (get-item $RegKeyTmp2).GetValue('CLSID') $private:Profile = (get-item $RegKeyTmp2).GetValue('Profile') $InstalledLayoutId.Add(('{0}:{1}{2}' -f $lang.Substring(4,4), $CLSID, $Profile).ToUpper()) } } else{ break } $counter++ } } } } elseif($Target -eq 'System'){ $InstalledLayoutId = New-Object 'System.Collections.Generic.List[string]' (DISM.exe /Online /Get-Intl /English) | Select-String -SimpleMatch 'Active keyboard(s)' | ForEach-Object { if($_ -match ':\s*(.*)'){$Matches[1]} } | ForEach-Object { ($_ -split ',') } | ForEach-Object { $InstalledLayoutId.Add($_.Trim()) } } if(-not $InstalledLayoutId){ # Write-Error 'Cannot get keyboard layout' } else{ $InstalledLayoutId.ToArray() } } function Get-DefaultKeyboardLayout { [CmdletBinding()] Param() $private:RegPreload = 'HKCU:\Keyboard Layout\Preload' $private:RegSubstitutes = 'HKCU:\Keyboard Layout\Substitutes' $private:RegAssemblies = 'HKCU:\Software\Microsoft\CTF\Assemblies' $private:MysteriousGUID = '{34745C63-B2F0-4784-8B67-5E12C8701A31}' $private:ZeroGUID = '{00000000-0000-0000-0000-000000000000}' if(-not ($PreloadId = (Get-ItemProperty $RegPreload).1)){ # $PreloadId = d0010411 Write-Error "Coudn't get default keyboardLayout" return } # langId equals last 4 chars of Preload value $langId = $PreloadId.PadLeft(8, '0').Substring(4,4) # $langId = 0411 if($layoutId = (Get-ItemProperty $RegSubstitutes).$PreloadId){ # $layoutId = 0002041e # if preload id include in the list of substitutes key, data of substitutes is layoutId } else{ $tmpKey = Join-Path $RegAssemblies ("0x{0}\{1}" -f $langId.PadLeft(8, '0'), $MysteriousGUID) # 'HKCU:\Software\Microsoft\CTF\Assemblies\0x00000411\{34745C63-B2F0-4784-8B67-5E12C8701A31}' if(-not (Test-Path $tmpKey)){ Write-Error "Coudn't get default keyboardLayout" return } $defaultGuid = (get-item $tmpKey).GetValue('Default') # GUID $profileGuid = (get-item $tmpKey).GetValue('Profile') # GUID $layoutId = (get-item $tmpKey).GetValue('KeyboardLayout') # hex 0x04110411 if($ZeroGuid -eq $defaultGuid){ # if default = allzero guid => first 4 chars of keyboardLayout value = layoutId $layoutId = $layoutId.ToString('X').PadLeft(8, '0').Substring(0,4).PadLeft(8, '0') # 00000411 } else{ # layout id = combined default guid & profile guid $layoutId = ('{0}{1}' -f $defaultGuid, $profileGuid) # {4518B9B5-7112-4855-B64F-2EC0DD2831E6}{54EDCC94-1524-4bb1-9FB7-7BABE4F4CA64} } } return ("{0}:{1}" -f $langId,$layoutId) } function Set-KeyboardLayoutRegistry { [CmdletBinding()] Param( [Parameter(Mandatory,ValueFromPipeline)] [ValidatePattern('^[0-9a-f]{4}:[0-9a-f\-\{\}]{8,}$')] [string[]]$KeyboardLayoutIds ) Begin { # Constant parameters $RegKeyAssembly = 'HKCU:\Software\Microsoft\CTF\SortOrder\AssemblyItem' $RegKeyLanguage = 'HKCU:\Software\Microsoft\CTF\SortOrder\Language' $RegKeyIdMap = 'HKLM:\SYSTEM\CurrentControlSet\Control\Keyboard Layouts' $MysteriousGUID = '{34745C63-B2F0-4784-8B67-5E12C8701A31}' $AllZeroGUID = '{00000000-0000-0000-0000-000000000000}' # Registry key initialize (if not yet) ($RegKeyAssembly, $RegKeyLanguage) | foreach { if(-not (Test-Path $_)){ New-Item $_ -Force } } } Process { foreach($KeyboardLayoutId in $KeyboardLayoutIds){ # Split KeyboardLayoutId to LanguageID and LayoutID e.g) 0411:00000409 $LangId = $KeyboardLayoutId.Substring(0,4) # First 4 chars is LanguageID (0411) $LayoutId = $KeyboardLayoutId.Substring(5) # After ':' is LayoutID (00000409) # Parse IDs to registry values $RegValue_Language = $LangId.PadLeft(8, '0') # Registry value under Language (00000411) $RegKey_AssemblyItem = ('0x{0}' -f $RegValue_Language) # Registry key name under AssemblyItem (0x00000411) if($LayoutId -match "(\{[0-9a-f\-]{36}\})(\{[0-9a-f\-]{36}\})"){ # PatternA: LayoutID is combined GUIDs style $RegValue_CLSID = $Matches[1] # CLSID is first guid $RegValue_KeyboardLayout = 0 # keyboardLayout is 0 $RegValue_Profile = $Matches[2] # Profile is second guid } else{ # PatternB: LayoutID is number style if($SpecialId = [string](Get-ItemProperty (Join-Path $RegKeyIdMap $LayoutId)).'Layout Id'){ $SpecialId = 'F' + $SpecialId.Substring(1) $RegValue_KeyboardLayout = [Convert]::ToInt32(('0x{0}{1}' -f $SpecialId, $LangId), 16) } else{ $RegValue_KeyboardLayout = [Convert]::ToInt32(('0x{0}{1}' -f $LayoutId.Substring(4), $LangId), 16) # Combine (last 4 digits of LayoutID) and (LangugaeID) then Convert to Int. (0x04090411 => 67699729) } $RegValue_CLSID = $AllZeroGUID $RegValue_Profile = $AllZeroGUID } # STEP1: Set LanguageID to registry under 'HKCU:\Software\Microsoft\CTF\SortOrder\Language' # Check registry value already exists or not $LastIndex = 0 for ($Index = 0; $Index -lt 64; $Index++) { #Max 64 (Prevent Infinite Loop) $LastIndex = $Index $strIndex = ([string]$Index).PadLeft(8, '0') $tmpItem = Get-ItemProperty -Path $RegKeyLanguage if($tmpItem.$strIndex){ if($tmpItem.$strIndex -eq $RegValue_Language){ $LastIndex = -1 # Reg value is exist break } } else{ break } } # Set registry values if($LastIndex -eq -1){ Write-Verbose ('Language ID is already exist') } else{ $strIndex = ([string]$LastIndex).PadLeft(8, '0') Write-Verbose ('Setting LanguageID registry in "{0}"' -f $RegKeyLanguage) New-ItemProperty -Path $RegKeyLanguage -Name $strIndex -Value $RegValue_Language -PropertyType 'String' | Out-Null } # STEP2: Set LayoutID to registry under 'HKCU:\Software\Microsoft\CTF\SortOrder\Language' # Check registry set already exist $LastIndex = 0 for ($Index = 0; $Index -lt 64; $Index++) { #Max 64 (Prevent Infinite Loop) $LastIndex = $Index $strIndex = ([string]$Index).PadLeft(8, '0') $tmpPath = ($RegKeyAssembly, $RegKey_AssemblyItem, $MysteriousGUID, $strIndex) -join '\' if(Test-Path $tmpPath){ $tmpItem = Get-ItemProperty -Path $tmpPath if($tmpItem.'CLSID' -ne $RegValue_CLSID){ Continue } if($tmpItem.'KeyboardLayout' -ne $RegValue_KeyboardLayout){ Continue } if($tmpItem.'Profile' -ne $RegValue_Profile){ Continue } $LastIndex = -1 # Reg set is exist break } else{ break } } # Set registry key and values if($LastIndex -eq -1){ Write-Verbose ('LayoutID is already exist') } else{ $strIndex = ([string]$LastIndex).PadLeft(8, '0') $tmpPath = ($RegKeyAssembly, $RegKey_AssemblyItem, $MysteriousGUID, $strIndex) -join '\' if(-not (Test-Path $tmpPath)){ Write-Verbose ('Setting LayoutID registry in "{0}"' -f $tmpPath) New-Item $tmpPath -Force | Out-Null New-ItemProperty -Path $tmpPath -Name 'CLSID' -Value $RegValue_CLSID -PropertyType 'String' | Out-Null New-ItemProperty -Path $tmpPath -Name 'KeyboardLayout' -Value $RegValue_KeyboardLayout -PropertyType 'DWord' | Out-Null New-ItemProperty -Path $tmpPath -Name 'Profile' -Value $RegValue_Profile -PropertyType 'String' | Out-Null } } } # End Write-Verbose ('Operation completed successfully') } } function Start-Command { [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position=0)] [string] $FilePath, [Parameter(Mandatory=$false, Position=1)] [string[]]$ArgumentList, [int]$Timeout = [int]::MaxValue ) $ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo $ProcessInfo.FileName = $FilePath $ProcessInfo.UseShellExecute = $false $ProcessInfo.Arguments = [string]$ArgumentList $Process = New-Object System.Diagnostics.Process $Process.StartInfo = $ProcessInfo $Process.Start() | Out-Null if(!$Process.WaitForExit($Timeout)){ $Process.Kill() Write-Error ('Process timeout. Terminated. (Timeout:{0}s, Process:{1})' -f ($Timeout * 0.001), $FilePath) } $Process } |