本帖最后由 magic_key 于 2026-10-13 19:36 编辑
之前从youtube上偶然浏览到,老外极力推荐的QSPICE仿真软件。
第一次安装,只能在线下载安装,对软件印象挺好,但也果断卸载掉!
没有提供离线安装包,总会觉得,缺乏安全感。
当时,也不了解软件作者是谁。根本不知道,原来被大量使用的传奇现象级,免费电路仿真软件LTspice,也是出自这位作者之手。
大半生,都在做仿真软件。称其为,免费电路仿真软件仙人,应该也不过分吧?
该软件有更快的仿真速度、更高的精度、更强的可靠性。支持使用c/c++编写数字电路模块。支持模数混合电路仿真。
支持45度摆放元器件,经典的4个二极管组成整流桥,也在程序demo中。
不使用弹窗设置电路参数,鼠标各种点,到处点,这种方式低效过时。直接命令行加SPICE语法提示,高效优雅、体面。
.plot语法设置各种波形,仿真文件丢给对方,一运行即可看到想要关注的波形。
很多功能,讲不完的,自行探索。
一般在Google宇宙,All About Circuits论坛、eevblog论坛,是经常有甩出电路仿真文件,进行电路讨论的。
这种讨论氛围,感觉挺好的。
Qorvo不提供离线安装包的理由,程序更新极其频繁。
避免用户使用旧版本,又跑到论坛反馈那些已经被修复的问题,增加软件维护成本。
搜索到,老外讨论的离线安装包的手动获取方式。QSPICE更新程序会下载一个几十MB的压缩包,。
更新程序再把这个压缩包,解压出来会有100多MB,查看文件头,压缩包不是常见压缩格式。
在不点击安装程序之前,使用Everything搜索".0"文件后缀。
按照最新时间排序,文件名为4个小写字母加数字混合,把文件修改成".exe"就可以正常安装。
QSPICE程序版本其实很不明确,About QSPICE里并没有明确的、具体的版本号。
我使用安装界面Build 后面跟的一串日期和时间,作为离线安装包的文件名。
经过实测,主程序中的Check for Updates或者文件安装路径下的‪C:\Program Files\QSPICE\Update.exe,并不会在界面上显示检测到新版本等信息,脚本无法从这里捕获到有用信息。
使用旧版本安装程序,程序启动时,提示发现新版本的小弹窗,并不能可靠的、稳定的出现!
这会漏掉一些新版本推送,这是一些不影响使用的小瑕疵!
也使脚本,无法先通过检测比对新版本,再决定是否下载离线安装包。
需要在启动软件时,有出现提示新版本更新,再执行脚本。
总之,就折腾到这里了。
更新程序
软件安装界面
powershell脚本执行
离线安装包
脚本内容,保存为get_qspice.ps1,编码必须使用UTF-8 with BOM
[C] 纯文本查看 复制代码 <#
=====================================================================
QSPICE 离线安装包自动捕获脚本(详细注释版)
=====================================================================
这个脚本是做什么的?
自动完成以下流程,帮你拿到 QSPICE 的离线安装包:
1. 启动 QSPICE 的更新下载器 Update.exe
2. 自动点击弹窗上的 “Yes”,开始下载
3. 监视系统临时目录 %TEMP%,捕获下载好的 .0 文件
4. 从安装界面读取 Build 日期时间(例如 Build Oct 12 2026 17:22:16)
5. 把 .0 文件重命名为 QSPICE_Build_YYMMDD_HHMMSS.exe 并保存
保存编码要求(非常重要!):
- 请将本文件保存为 UTF-8 with BOM 或 ANSI(GB2312)。
- 如果保存为 UTF-8 无 BOM,Windows PowerShell 5.1 会中文乱码。
- 推荐:记事本 → 另存为 → 编码选择 “UTF-8 带 BOM”。
运行方法:
前提条件:必须有安装QSPICE程序
1. 以管理员身份打开 PowerShell:
按 Win+X → 选择 “Windows PowerShell (管理员)”
2. 允许本次会话运行脚本:
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
(可选项)永久允许脚本运行,对当前用户永久生效,且相对安全
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
3. 切换到脚本所在目录:
cd C:\Users\uesr\Desktop\temp\QSPICE_TMP
4. 执行脚本:
.\get_qspice.ps1
注意事项:
- 运行前请关闭所有浏览器窗口,避免脚本误抓窗口。
- 确保 Update.exe 路径和输出目录配置正确(见配置区)。
- 若杀毒软件拦截,请将脚本和输出目录加入白名单。
=====================================================================
#>
# ==================== 配置区(按需修改) ====================
# QSPICE 的安装目录
$QSPICE_PATH = "C:\Program Files\QSPICE"
# 更新程序的完整路径(把安装目录和文件名拼起来)
$UPDATE_EXE = Join-Path $QSPICE_PATH "Update.exe"
# 系统临时目录,QSPICE 会在这里生成 .0 文件
$TEMP_DIR = $env:TEMP
# 离线安装包保存到哪里(可自定义)
$OUTPUT_DIR = "C:\Users\uesr\Downloads"
# 只捕获大于这个大小的 .0 文件,避免抓到无关的小文件
$MIN_SIZE_MB = 30
# 整个脚本最长运行时间(秒),超时就放弃
$MAX_WAIT_SEC = 600
# ===========================================================
# ---------- 准备工作 1:确保输出目录存在 ----------
# 如果目录不存在就创建,-Force 表示即使已存在也不报错
if (!(Test-Path $OUTPUT_DIR)) {
New-Item -ItemType Directory -Path $OUTPUT_DIR -Force | Out-Null
}
# ---------- 准备工作 2:加载 UI Automation 程序集 ----------
# UI Automation 是 Windows 提供的接口,可以读取窗口和控件的文字
# 我们用它来读取安装窗口里的 “Build Oct 12 2026 17:22:16” 这类信息
Add-Type -AssemblyName UIAutomationClient
Add-Type -AssemblyName UIAutomationTypes
# ============================================================
# 函数 1:Find-QspiceWindows
# 作用:查找当前所有和 QSPICE 相关的窗口(排除浏览器、编辑器)
# 返回:一个列表,每项包含 Window(窗口对象)、Title(标题)、ProcessName(进程名)
# ============================================================
function Find-QspiceWindows {
# 获取“桌面”根元素,所有窗口都是它的子元素
$desktop = [System.Windows.Automation.AutomationElement]::RootElement
# 定义一个条件:只要 ControlType 是 Window 的元素
$condition = New-Object System.Windows.Automation.PropertyCondition(
[System.Windows.Automation.AutomationElement]::ControlTypeProperty,
[System.Windows.Automation.ControlType]::Window)
# 在桌面下查找所有符合条件(是窗口)的元素
$windows = $desktop.FindAll([System.Windows.Automation.TreeScope]::Children, $condition)
# 用来存放结果的空列表
$result = @()
# 逐个检查每个窗口
foreach ($win in $windows) {
# 读取窗口的标题
$title = $win.GetCurrentPropertyValue([System.Windows.Automation.AutomationElement]::NameProperty)
# 读取窗口所属进程的 ID
$procId = $win.GetCurrentPropertyValue([System.Windows.Automation.AutomationElement]::ProcessIdProperty)
# 如果标题为空,跳过这个窗口
if (-not $title) { continue }
# 排除文件属性对话框(标题里带“属性”)
if ($title -match "属性") { continue }
# 通过进程 ID 找到对应的进程对象
$proc = Get-Process -Id $procId -ErrorAction SilentlyContinue
if (-not $proc) { continue }
# 排除浏览器和代码编辑器,避免误抓
if ($proc.ProcessName -match "chrome|msedge|firefox|Code|devenv|notepad") { continue }
# 只保留标题含 QSPICE 或 Installation Program 的窗口
if ($title -match "QSPICE" -or $title -match "Installation Program for") {
# 把结果加入列表
$result += [PSCustomObject]@{
Window = $win # 窗口对象,后面用来读取内容或点按钮
Title = $title # 窗口标题
ProcessName = $proc.ProcessName # 进程名
}
}
}
# 返回找到的所有窗口
return $result
}
# ============================================================
# 函数 2:Get-AllTexts
# 作用:递归读取某个窗口内所有控件上的文字
# 参数:$Element —— 要读取的窗口元素
# 返回:字符串数组,包含窗口里所有可见文字
# ============================================================
function Get-AllTexts {
param($Element)
# 用来存放文字
$texts = @()
# 先读取传入元素自身的标题
try {
$name = $Element.GetCurrentPropertyValue([System.Windows.Automation.AutomationElement]::NameProperty)
if ($name -and $name.Trim()) { $texts += $name.Trim() }
} catch {}
# 再查找该元素下所有后代元素(Descendants)
try {
$children = $Element.FindAll([System.Windows.Automation.TreeScope]::Descendants,
[System.Windows.Automation.Condition]::TrueCondition)
# 逐个读取每个后代元素的文字
foreach ($child in $children) {
try {
$t = $child.GetCurrentPropertyValue([System.Windows.Automation.AutomationElement]::NameProperty)
if ($t -and $t.Trim()) { $texts += $t.Trim() }
} catch {}
}
} catch {}
# 返回所有读到的文字
return $texts
}
# ============================================================
# 函数 3:Invoke-ButtonByName
# 作用:在指定窗口里找到名称为 $ButtonName 的按钮并点击
# 参数:$Window —— 目标窗口;$ButtonName —— 按钮文字,比如 "Yes"
# 返回:$true 表示点击成功,$false 表示没找到
# ============================================================
function Invoke-ButtonByName {
param($Window, [string]$ButtonName)
try {
# 定义查找条件:名字等于 $ButtonName
$cond = New-Object System.Windows.Automation.PropertyCondition(
[System.Windows.Automation.AutomationElement]::NameProperty, $ButtonName)
# 在窗口下查找第一个符合条件的元素
$btn = $Window.FindFirst([System.Windows.Automation.TreeScope]::Descendants, $cond)
# 如果找到了按钮
if ($btn) {
# 获取该按钮的“调用模式”,用它来模拟点击
$pattern = $btn.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern)
$pattern.Invoke()
return $true
}
} catch {}
# 没找到或出错都返回 false
return $false
}
# ============================================================
# 函数 4:Parse-QSPICEBuild
# 作用:从窗口文字中解析 Build 日期,生成文件名
# 例如找到 “Build Oct 12 2026 17:22:16”
# 就会返回 “QSPICE_Build_261012_172216”
# 参数:$Texts —— 字符串数组(窗口里所有文字)
# 返回:文件名(不含 .exe),或 $null 表示没找到
# ============================================================
function Parse-QSPICEBuild {
param([string[]]$Texts)
# 英文月份缩写 → 两位数字月份
$monthMap = @{
'Jan'='01'; 'Feb'='02'; 'Mar'='03'; 'Apr'='04'; 'May'='05'; 'Jun'='06'
'Jul'='07'; 'Aug'='08'; 'Sep'='09'; 'Oct'='10'; 'Nov'='11'; 'Dec'='12'
}
# 遍历每一段文字,尝试匹配
foreach ($t in $Texts) {
# 正则解释:
# Build\s+ —— “Build” 后面至少一个空格
# ([A-Za-z]{3})\w* —— 英文月份前三个字母(如 Oct)
# \s+(\d{1,2}) —— 空格后跟 1~2 位数字(日)
# \s+(\d{4}) —— 空格后跟 4 位数字(年)
# \s+(\d{1,2}):(\d{2}):(\d{2}) —— 空格后跟 时:分:秒
$m = [regex]::Match($t, 'Build\s+([A-Za-z]{3})\w*\s+(\d{1,2})\s+(\d{4})\s+(\d{1,2}):(\d{2}):(\d{2})')
# 如果匹配成功
if ($m.Success) {
# 月份英文缩写转成数字
$mon = $monthMap[$m.Groups[1].Value.Substring(0,3)]
if (-not $mon) { continue }
# 取出各部分,位数不够就前面补 0
$day = $m.Groups[2].Value.PadLeft(2, '0') # 日
$year = $m.Groups[3].Value # 年(4 位)
$hh = $m.Groups[4].Value.PadLeft(2, '0') # 时
$mm = $m.Groups[5].Value.PadLeft(2, '0') # 分
$ss = $m.Groups[6].Value.PadLeft(2, '0') # 秒
# 年份只取后两位,比如 2026 → 26
$yy = $year.Substring(2, 2)
# 拼成文件名并返回
return "QSPICE_Build_${yy}${mon}${day}_${hh}${mm}${ss}"
}
}
# 没找到任何匹配
return $null
}
# ============================================================
# 主流程开始
# ============================================================
# ---- 第 1 步:启动 Update.exe ----
Write-Host " 启动更新程序: $UPDATE_EXE"
# 先检查 Update.exe 是否存在
if (-not (Test-Path $UPDATE_EXE)) {
Write-Host "[!] 找不到更新程序: $UPDATE_EXE" -ForegroundColor Red
exit 1
}
# 用 Start-Process 启动它(和手动双击效果一样)
Start-Process -FilePath $UPDATE_EXE
# ---- 第 2 步:进入主循环 ----
Write-Host " 开始监视 Temp 目录并等待 QSPICE 窗口..."
# 记录开始时间,用于超时判断
$startTime = Get-Date
# 存放已捕获的 .0 文件路径(复制到输出目录后的临时名)
$capturedFile = $null
# 存放解析出的文件名(例如 QSPICE_Build_261012_172216)
$buildBase = $null
# 记录是否已经点过 Yes,避免重复点击
$clickedYes = $false
# 主循环:只要还没超时就一直跑
while (((Get-Date) - $startTime).TotalSeconds -lt $MAX_WAIT_SEC) {
# ---- 2.1 查找所有 QSPICE 相关窗口 ----
$qspiceWindows = Find-QspiceWindows
# 逐个处理每个窗口
foreach ($item in $qspiceWindows) {
$win = $item.Window
$title = $item.Title
$pname = $item.ProcessName
# ---- 2.1.1 如果是第一次看到弹窗,尝试点 Yes ----
if (-not $clickedYes) {
if (Invoke-ButtonByName -Window $win -ButtonName "Yes") {
Write-Host "[✓] 已自动点击 Yes,开始下载。" -ForegroundColor Green
$clickedYes = $true
Start-Sleep -Seconds 2 # 等一下再继续
continue
}
}
# ---- 2.1.2 尝试从窗口文字里解析 Build 日期 ----
if (-not $buildBase) {
# 读取窗口里所有文字
$texts = Get-AllTexts -Element $win
# 尝试解析
$bb = Parse-QSPICEBuild -Texts $texts
if ($bb) {
$buildBase = $bb
Write-Host "[✓] 从窗口 '$title' 解析到版本: $buildBase" -ForegroundColor Green
}
}
}
# ---- 2.2 尝试捕获 Temp 目录下的 .0 文件(只捕一次) ----
if (-not $capturedFile) {
# 列出 Temp 目录下所有扩展名为 .0 且大于阈值的大文件
$candidates = Get-ChildItem -Path $TEMP_DIR -File -ErrorAction SilentlyContinue |
Where-Object {
$_.Name -match '\.0$' -and
$_.Length -gt ($MIN_SIZE_MB * 1MB)
}
# 逐个检查候选文件
foreach ($file in $candidates) {
# 读第一次大小
$size1 = $file.Length
# 等 800 毫秒再读一次
Start-Sleep -Milliseconds 800
# 读第二次大小
$size2 = (Get-Item $file.FullName).Length
# 如果两次大小一样,说明文件已经写完,可以复制
if ($size1 -eq $size2) {
# 先复制到输出目录,用临时名(后面再改名)
$tempDest = Join-Path $OUTPUT_DIR "QSPICE_tmp_capture.exe"
Copy-Item -Path $file.FullName -Destination $tempDest -Force
$capturedFile = $tempDest
Write-Host "[✓] 已捕获 .0 文件: $($file.FullName) -> $tempDest" -ForegroundColor Green
break
}
}
}
# ---- 2.3 判断是否可以退出循环 ----
# 只有 .0 文件已捕获 且 Build 日期已解析,才能退出
if ($capturedFile -and $buildBase) { break }
# 没完成就等 2 秒再试一次
Start-Sleep -Seconds 2
}
# ============================================================
# 第 3 步:结果处理
# ============================================================
# ---- 3.1 没捕获到 .0 文件 ----
if (-not $capturedFile) {
Write-Host "[!] 超时:未捕获到 .0 文件" -ForegroundColor Red
exit 2
}
# ---- 3.2 没解析到 Build 日期 ----
if (-not $buildBase) {
Write-Host "[!] 超时:未能从安装窗口解析到 Build 日期" -ForegroundColor Red
Write-Host "已捕获的文件保留在: $capturedFile"
Write-Host "你可以手动重命名,例如 QSPICE_Build_261012_172216.exe"
exit 3
}
# ---- 3.3 拼出最终文件名和路径 ----
$finalName = "$buildBase.exe"
$finalPath = Join-Path $OUTPUT_DIR $finalName
# ---- 3.4 如果同名文件已存在,先删除 ----
if (Test-Path $finalPath) { Remove-Item $finalPath -Force }
# ---- 3.5 重命名临时文件为最终名字 ----
Rename-Item -Path $capturedFile -NewName $finalName -Force
# ---- 3.6 打印成功信息 ----
Write-Host "[✓] 完成!离线安装包已保存为: $finalPath" -ForegroundColor Green
|