如何对比.lnk文件生成脚本修改PowerShell/CMD控制台字体且不损坏快捷方式?
问题:Windows 10/11控制台快捷方式(.lnk)字体与布局修改脚本损坏问题
之前提问《Change PowerShell Console Font and Layout in Windows 10》未获明确答案,现换思路解决Windows 10中PowerShell/CMD控制台字体与布局修改问题:
发现Windows控制台的字体、缓冲区及布局信息存储在.lnk文件中,这类控制台快捷方式.lnk与URL链接、文件/文件夹快捷方式的.lnk扩展名相同,但结构不同。
执行操作如下:
- 将开始菜单中的PowerShell图标拖到桌面,命名为
PowerShell.lnk - 复制该快捷方式,命名为
PowerShell20.lnk - 打开
PowerShell20.lnk的属性,将字体大小改为20后关闭属性窗口
原本计划通过位对比两个.lnk文件的差异,生成可修改原PowerShell.lnk的PowerShell脚本,进而将该修改应用到任意控制台.lnk,实现Windows 10/11中按需调整控制台属性。但编写的脚本(如下)虽能生成修改脚本并应用更改,却会导致PowerShell.lnk快捷方式损坏。请问如何利用控制台快捷方式的位差异,构建可修改字体等属性且不损坏快捷方式的通用脚本?
# Paths to the shortcut files $originalShortcutPath = "$env:UserProfile\Desktop\PowerShell.lnk" $modifiedShortcutPath = "$env:UserProfile\Desktop\PowerShell20.lnk" $fixScriptPath = "$env:UserProfile\Desktop\FixPowerShell.ps1" # Read shortcut files as binary data $originalBytes = [System.IO.File]::ReadAllBytes($originalShortcutPath) $modifiedBytes = [System.IO.File]::ReadAllBytes($modifiedShortcutPath) # Find differing bits and generate fix script $differences = @() $fixScriptContent = @() for ($i = 0; $i -lt $originalBytes.Length; $i++) { $originalByte = $originalBytes[$i] $modifiedByte = $modifiedBytes[$i] for ($j = 0; $j -lt 8; $j++) { $originalBit = ($originalByte -band ([math]::Pow(2, $j))) -shr $j $modifiedBit = ($modifiedByte -band ([math]::Pow(2, $j))) -shr $j if ($originalBit -ne $modifiedBit) { $bitIndex = $i * 8 + $j $differences += "Bit $bitIndex is different, has value: $modifiedBit" $fixScriptContent += "Set-Content -Path '$originalShortcutPath' -Value ([System.BitConverter]::ToString([System.BitConverter]::ToInt32(@(Get-Content -Path '$originalShortcutPath' -Encoding Byte), 0) -bxor (1 -shl $bitIndex)), 0) -Encoding Byte -NoNewline" } } } # Write fix script $fixScriptContent | Out-File -FilePath $fixScriptPath -Encoding ASCII if ($differences.Count -eq 0) { Write-Host "No differences found between the two files." } else { Write-Host "Listing every bit that is different in $($modifiedShortcutPath):" $differences Write-Host "Fix script generated at $fixScriptPath" }
解决方案
原脚本损坏快捷方式的原因
直接逐位修改.lnk文件的方式忽略了Windows快捷方式的结构化特性:
.lnk文件包含校验和字段用于验证完整性,逐位修改会破坏校验和,导致系统识别为损坏的快捷方式。- 控制台快捷方式的属性(如字体大小)存储在特定结构区块中,原脚本错误地将整个文件当成32位整数操作,完全不符合
.lnk的二进制结构逻辑。
正确实现方法
方法1:精准替换控制台属性区块
控制台快捷方式的扩展属性(布局、字体等)存储在.lnk的ConsoleData区块中,可定位该区块并直接替换:
$originalPath = "$env:UserProfile\Desktop\PowerShell.lnk" $modifiedPath = "$env:UserProfile\Desktop\PowerShell20.lnk" # 读取二进制数据 $originalBytes = [System.IO.File]::ReadAllBytes($originalPath) $modifiedBytes = [System.IO.File]::ReadAllBytes($modifiedPath) # ConsoleData区块的唯一GUID标记 $consoleGuidBytes = [byte[]](0xA0,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46) # 在修改后的文件中定位ConsoleData区块 $startIndex = -1 for ($i = 0; $i -lt $modifiedBytes.Length - 15; $i++) { if (([System.Linq.Enumerable]::SequenceEqual($modifiedBytes[$i..($i+15)], $consoleGuidBytes))) { $startIndex = $i break } } # 提取完整区块内容 $blockLength = [BitConverter]::ToUInt32($modifiedBytes, $startIndex + 16) $endIndex = $startIndex + 16 + 4 + $blockLength $consoleDataBlock = $modifiedBytes[$startIndex..$endIndex] # 在原文件中找到对应区块并替换 $originalStartIndex = -1 for ($i = 0; $i -lt $originalBytes.Length - 15; $i++) { if (([System.Linq.Enumerable]::SequenceEqual($originalBytes[$i..($i+15)], $consoleGuidBytes))) { $originalStartIndex = $i break } } # 生成新的二进制数据并写入 $newBytes = $originalBytes[0..($originalStartIndex-1)] + $consoleDataBlock + $originalBytes[($originalStartIndex + $consoleDataBlock.Length)..($originalBytes.Length-1)] [System.IO.File]::WriteAllBytes($originalPath, $newBytes) Write-Host "控制台属性已成功修改"
方法2:调用Windows Shell API修改(更稳定)
直接通过系统提供的COM接口修改控制台属性,避免手动操作二进制文件:
$shortcutPath = "$env:UserProfile\Desktop\PowerShell.lnk" $targetFontSize = 20 # 创建Shell快捷方式对象 $shell = New-Object -ComObject WScript.Shell $shortcut = $shell.CreateShortcut($shortcutPath) # 获取控制台属性并修改字体大小 $consoleProps = $shortcut.GetLinkInfo().GetConsoleProperties() $consoleProps.FontSize = $targetFontSize # 保存修改并释放COM资源 $shortcut.Save() [System.Runtime.Interopservices.Marshal]::ReleaseComObject($consoleProps) | Out-Null [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shortcut) | Out-Null [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null Write-Host "字体大小已设置为 $targetFontSize"
注意:此方法需以管理员身份运行PowerShell,确保COM组件权限正常。
内容的提问来源于stack exchange,提问作者YorSubs
相关产品推荐
相关产品推荐

