<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>问题解决 &#8211; 张三太爷</title>
	<atom:link href="https://www.somedoc.net/category/%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.somedoc.net</link>
	<description>看前面，黑洞洞</description>
	<lastBuildDate>Tue, 13 Oct 2026 13:43:29 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://www.somedoc.net/wp-content/uploads/2016/12/cropped-dandycheung-1-32x32.jpg</url>
	<title>问题解决 &#8211; 张三太爷</title>
	<link>https://www.somedoc.net</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>GitHub Desktop 团队，也是草台班子</title>
		<link>https://www.somedoc.net/2026/10/13/github-desktop-%e5%9b%a2%e9%98%9f%ef%bc%8c%e4%b9%9f%e6%98%af%e8%8d%89%e5%8f%b0%e7%8f%ad%e5%ad%90/</link>
					<comments>https://www.somedoc.net/2026/10/13/github-desktop-%e5%9b%a2%e9%98%9f%ef%bc%8c%e4%b9%9f%e6%98%af%e8%8d%89%e5%8f%b0%e7%8f%ad%e5%ad%90/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Tue, 13 Oct 2026 13:43:18 +0000</pubDate>
				<category><![CDATA[Windows]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6720</guid>

					<description><![CDATA[在电脑里看到的最讨厌的事情之一就是有的程序/组件的重复。Ch <a href="https://www.somedoc.net/2026/10/13/github-desktop-%e5%9b%a2%e9%98%9f%ef%bc%8c%e4%b9%9f%e6%98%af%e8%8d%89%e5%8f%b0%e7%8f%ad%e5%ad%90/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">在电脑里看到的最讨厌的事情之一就是有的程序/组件的重复。Chromium 的 CEF 最垃圾，但是惹不起啊，只能忍受，但是别的东东，有时候还是要抗争一下。</p>



<p class="wp-block-paragraph">例如 git。好些个工具都自己带这个玩意儿，但又有好些个总是要检查系统里有没有现成的。既然这样，给你们合二为一不好吗？于是，俺就把 GitHub Desktop 自带的 git 放到了环境变量里。</p>



<p class="wp-block-paragraph">好日子没过几天，某次突然发现在命令行里找不到 git 了。再一看，GitHub Desktop 升级了，它的安装路径下，有一个入口可执行程序，与之平级会有不止一个 <code>app-x.y.x</code> 的目录（很显然，x.y.z 是版本号），每个目录下都是一份完整的对应版本。一般来说，会总是保留最近的两个版本，大概是为了出了问题好回退。这样交替更迭几回，那么死守着旧版本的环境变量总归会失效，需要手动去系统设置里修改一下路径，把版本号改到最新的一版。烦。</p>



<p class="wp-block-paragraph">于是请 WorkBuddy 来帮忙，让它写个批处理（本人一般不会动用 PowerShell 这种重器）。使用它这两天优惠着的 Space-Bunny 模型，好家伙，批处理里面的坑把它折腾的死去活来，整整迭代了九轮，才算出来个能用的，看了下大小，接近 20KB，是我这辈子产出的个头最大的批处理文件了。</p>



<p class="wp-block-paragraph">思路特简单，就是在 app-x.y.z 平级创建一个名为 app-latest 的 Junction（这个单词把老夫搞住了，不知道怎么翻译，之前以为是软链接，但 AI 说不是），如果在命令行里找不到 git 了，就把批处理执行一下，没有这个 Junction 则创建之，有则更新之，而系统环境变量里永远是 latest 不需要改。</p>



<p class="wp-block-paragraph">事情就是这么巧。昨天刚刚把批处理搞定，app-latest 创建好，也指向了最新的 app-3.6.6 目录，GitHub Desktop 工作正常。今天，就在刚刚，把窗口切换到 GitHub Desktop 时，突然弹框报错，说 git 找不到了。拿脚趾头想都知道跟俺昨天的移花接木小手术有关。那个弹框是源源不断的，鼠标点击永远点不完。这个时候要上手速，Alt-F4 连续按，症状消失。到安装目录里查看，果然 app-3.6.7 赫然在目。但这个时候，你如果运行安装目录里的入口程序，则没有任何效果，光标转一两秒后没有任何情况发生。但是，如果双进入到任一版本的目录中，再执行其中的可执行文件，GitHub Desktop 就会正常运行起来。</p>



<p class="wp-block-paragraph">其实昨天写批处理的时候，跟 AI 已经就这个 Junction 的名字讨论过一番了，当时它害怕的是，GitHub Desktop 的升级程序会根据 app- 前缀来过滤出所有匹配的子目录，除去它自己标明为最新版的那个的话，其余的都会一概删除，而这里面一定会包括有 app-latest。老夫当时的逻辑是，app-latest 指向的不管是哪个版本，对于升级器来说一定是指向了一个旧版本，它删就删了。出于洁癖，命名依然定为 app-latest。但今天的铁定事实证明，入口程序也会被 app-latest 影响：因为当俺把 app-latest 的首字母改掉以后，入口程序就也可以加载到正确的最新版本而成功运行起来了。</p>



<p class="wp-block-paragraph">看来还得退避一舍，把批处理里的 app-latest 改成别的名字，就叫 ghd-latest 吧。</p>



<p class="wp-block-paragraph">尽管问题因咱而起，但正如题目所说，开发团队实在是也相当粗疏，app- 的前缀之后，好歹也该检查一下版本格式的。为了表彰 WorkBuddy 虽然资质一般但毕竟勤勤恳恳，把它生成的批处理附录于此。</p>



<pre class="crayon-plain-tag">@echo off
rem ===========================================================================
rem  GitHub Desktop - ghd-latest directory-link maintainer (pure batch)
rem
rem  Usage:
rem    ghd-app-latest.bat                create/update the link as needed
rem    ghd-app-latest.bat -CheckOnly     report only, change nothing
rem    ghd-app-latest.bat latest         use a different link name
rem    ghd-app-latest.bat -Debug         extra tracing while diagnosing
rem
rem  Exit code: 0 ok | 2 install not found | 3 refused | 4 link failed | 1 error
rem
rem  NOTE: keep this file pure ASCII with NO byte order mark.
rem        A BOM-less UTF-8 batch is read by cmd.exe byte by byte, and CJK
rem        text gets chopped into fragments that are then parsed as commands
rem        (it produced errors like "'URTGT' is not recognized"). All prompts
rem        here are ASCII on purpose, so the file works under any code page.
rem
rem  ---------------------------------------------------------------------------
rem  Implementation notes (all native cmd, no PowerShell):
rem    find install : reg query on uninstall keys, else %LOCALAPPDATA%
rem    match names  : pure string substitution, no findstr and no regex. A
rem                   caret-anchored /c:"^app-&#91;0-9]$" never matches inside cmd,
rem                   whatever findstr really receives is not what it looks like.
rem    pick version : split on dots, compare segment by segment numerically.
rem                   Never sort / compare as string - that puts 3.6.10 before
rem                   3.6.9. Also never take the first entry of dir /o-n:
rem                   that is time-ordered and our own ghd-latest sorts first.
rem    read target  : fsutil reparsepoint query, parse the "Substitute Name"
rem                   line. Do NOT use "Print Name" - for a junction its
rem                   length is always 0.
rem    create link  : mklink /J (directory junction). No mklink /D fallback:
rem                   /D needs admin or developer mode, so it fails anyway.
rem    delete link  : rmdir, never with the recursive switch.
rem  ---------------------------------------------------------------------------
setlocal EnableExtensions EnableDelayedExpansion

rem ===========================================================================
rem  Parse arguments
rem ===========================================================================
rem The default link name is "ghd-latest", NOT "app-latest". Squirrel removes
rem stale version directories with a loose test for names CONTAINING "app-",
rem and its recursive delete does not recognise reparse points. A link named
rem app-latest therefore gets destroyed on the next GitHub Desktop update,
rem and the delete follows the junction into the real version directory and
rem removes files from it. That was observed happening on this machine.
rem "ghd-latest" contains no "app-" and stays outside that sweep entirely.
set "LINKNAME=ghd-latest"
set "CHECKONLY=0"
set "DEBUG=0"

:ParseArgs
if "%~1"=="" goto AfterArgs
if /i "%~1"=="-CheckOnly" (set "CHECKONLY=1" &amp; shift &amp; goto ParseArgs)
if /i "%~1"=="/CheckOnly" (set "CHECKONLY=1" &amp; shift &amp; goto ParseArgs)
if /i "%~1"=="-Debug" (set "DEBUG=1" &amp; shift &amp; goto ParseArgs)
if /i "%~1"=="/Debug" (set "DEBUG=1" &amp; shift &amp; goto ParseArgs)
set "LINKNAME=%~1"
shift
goto ParseArgs
:AfterArgs
if "%DEBUG%"=="1" echo (debug mode on)

set "RC=0"
set "E_NOFIND=2"
set "E_REFUSE=3"
set "E_LINKFAIL=4"

echo.
echo GitHub Desktop - ghd-latest link maintainer
echo --------------------------------------------
echo   Link name: %LINKNAME%

rem ===========================================================================
rem  Guard: the link name must not look like a version directory
rem
rem  Done with substitution, not findstr. A caret-anchored regex on a cmd
rem  command line is unreliable (see the note at :ConsiderDir), and a guard
rem  that silently never fires is worse than no guard at all.
rem  Rule: reject if the name starts with "app-" followed by a digit.
rem
rem  Written as a subroutine that returns E_GUARD=1 when it must refuse, so
rem  that no goto ever appears inside a nested if/for block.
rem ===========================================================================
set "E_GUARD=0"
call :CheckGuard
if "%E_GUARD%"=="1" (
    echo    x Link name "%LINKNAME%" clashes with the app-VERSION naming rule.
    echo      Refusing to run. Pick another name, e.g. ghd-app-latest.bat latest
    set "RC=%E_REFUSE%"
    goto Finish
)

rem ===========================================================================
rem  1. Locate the install root
rem ===========================================================================
echo.
echo &#91; Locate install ]

set "ROOT="
for /f "tokens=2,*" %%A in ('reg query "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s /v InstallLocation 2^>nul ^| findstr /i /c:"REG_SZ"') do (
    if exist "%%~B\GitHubDesktop.exe" set "ROOT=%%~B"
)
if not defined ROOT if exist "%LOCALAPPDATA%\GitHubDesktop\GitHubDesktop.exe" set "ROOT=%LOCALAPPDATA%\GitHubDesktop"

if not defined ROOT (
    echo    x GitHub Desktop install directory not found.
    echo      Checked: HKCU uninstall InstallLocation and %%LOCALAPPDATA%%\GitHubDesktop
    set "RC=%E_NOFIND%"
    goto Finish
)
echo    + Install root: %ROOT%

rem ===========================================================================
rem  2. Enumerate version directories
rem ===========================================================================
echo.
echo &#91; Version directories ]

set "CAND="
for /f "delims=" %%D in ('dir /b /ad "%ROOT%" 2^>nul') do call :ConsiderDir "%%D"

if not defined CAND (
    echo    x No complete and usable app-VERSION directory found.
    set "RC=%E_NOFIND%"
    goto Finish
)

rem ===========================================================================
rem  3. Pick the highest version (numeric, segment by segment)
rem ===========================================================================
echo.
set "BEST="
for %%D in (%CAND%) do call :PickBest "%%D"

if not defined BEST (
    echo    x Cannot determine the effective version.
    set "RC=%E_NOFIND%"
    goto Finish
)
echo    + Effective version: !BEST!

rem ===========================================================================
rem  4. Inspect the existing link
rem ===========================================================================
echo.
echo &#91; Link state ]
set "LINK=%ROOT%\!LINKNAME!"

if exist "%LINK%\" (
    call :ReadTarget
    if defined CURTGT (
        echo    !LINKNAME! points to: !CURTGT!
        if /i "!CURTGT!"=="%ROOT%\!BEST!" (
            echo.
            echo &#91; Result ]
            echo    Already up to date. Nothing to do.
            set "RC=0"
            goto Finish
        )
        echo    * !LINKNAME! points to an older directory, repointing it to !BEST! ...
    ) else (
        rem Target unreadable: if it is a link, it is broken (rebuild it);
        rem if dir cannot even list it, it is a real directory (refuse).
        dir /a:l "%LINK%" >nul 2>&amp;1
        if errorlevel 1 (
            echo    x "%LINKNAME%" exists but is NOT a link, it is a real directory.
            echo      Refusing to delete it. Please inspect it manually.
            set "RC=%E_REFUSE%"
            goto Finish
        )
        echo    * Found a broken link, no readable target, rebuilding.
    )
) else (
    echo    !LINKNAME! does not exist yet.
    echo    Creating it to point to !BEST! ...
)

if "%CHECKONLY%"=="1" (
    echo.
    echo &#91; Result ]
    echo    CheckOnly mode, nothing was changed.
    echo    It should point to: %ROOT%\!BEST!
    set "RC=0"
    goto Finish
)

rem ===========================================================================
rem  5. Risk warning
rem ===========================================================================
rem WARN=1 when the chosen link name contains app-, which puts the link
rem inside Squirrel's stale-version sweep. That is not theoretical: a link
rem named app-latest was destroyed by a real GitHub Desktop update, and the
rem recursive delete followed the junction and emptied the old version
rem directory it pointed at. The default ghd-latest avoids it entirely.
rem
rem When the chosen name IS out of range, also check whether a leftover link
rem from the old default is still sitting there, and say so.
set "WARN=0"
call :ContainsAppDash
if "%WARN%"=="1" (
    echo.
    echo &#91; Risk warning ]
    echo    * Squirrel removes old versions with a loose "name contains app-"
    echo    * test, not an exact app-DIGIT match, so "%LINKNAME%" is in range.
    echo    * A GitHub Desktop update can delete this link, and the recursive
    echo    * delete does not recognise reparse points, so it follows the link
    echo    * into the real version directory and removes files from it.
    echo      Observed happening with the previous name "app-latest".
    echo      Workaround: ghd-app-latest.bat latest
)

rem leftover check: the old default name is dangerous, flag it if still present
if not "%LINKNAME%"=="app-latest" (
    if exist "%ROOT%\app-latest\" (
        echo.
        echo &#91; Leftover warning ]
        echo    * %ROOT%\app-latest still exists.
        echo    * It is inside the Squirrel sweep and its target directory may
        echo    * already have been emptied. Delete it, you will not need it.
    )
)

rem ===========================================================================
rem  6. Rebuild the link
rem ===========================================================================
echo.
echo &#91; Update link ]

if exist "%LINK%\" (
    rem rmdir removes the link entry itself.
    rem Never add the recursive switch: it would empty the real target.
    rmdir "%LINK%" >nul 2>&amp;1
    if exist "%LINK%\" (
        echo    x Failed to remove the old link, permission denied?
        set "RC=%E_LINKFAIL%"
        goto Finish
    )
    echo    Old link removed, target directory untouched.
    if not exist "%ROOT%\!BEST!\GitHubDesktop.exe" (
        echo    x Target vanished during the operation, aborting.
        set "RC=%E_NOFIND%"
        goto Finish
    )
)

echo    Creating directory junction with mklink /J ...
rem No mklink /D fallback: it needs admin or developer mode, so on a normal
rem user session it always fails. A junction is enough for a local NTFS dir.
mklink /J "%LINK%" "%ROOT%\!BEST!" >nul 2>&amp;1
if not errorlevel 1 goto Verify

echo    x mklink /J failed, check free disk space and permissions.
set "RC=%E_LINKFAIL%"
goto Finish

:Verify
call :ReadTarget
if not defined CURTGT (
    echo    x Cannot read the target after creating the link.
    set "RC=%E_LINKFAIL%"
    goto Finish
)
if /i not "%CURTGT%"=="%ROOT%\!BEST!" (
    echo    x Verification failed, actual target: %CURTGT%
    set "RC=%E_LINKFAIL%"
    goto Finish
)
if not exist "%LINK%\GitHubDesktop.exe" (
    echo    x GitHubDesktop.exe is not reachable through the link.
    set "RC=%E_LINKFAIL%"
    goto Finish
)

echo.
echo &#91; Result ]
echo    + Path : %LINK%
echo    + Target: !CURTGT!
echo    + Verified: GitHubDesktop.exe is reachable through the link
set "RC=0"
goto Finish

rem ===========================================================================
rem  Finish: single exit point, pause only when double-clicked
rem ===========================================================================
:Finish
echo.
if "%RC%"=="0"   echo &#91;OK] Done, the link points at the effective version.
if "%RC%"=="1"   echo &#91;ERR] Script error, see the details above.
if "%RC%"=="2"   echo &#91;ERR] GitHub Desktop install not found, nothing changed.
if "%RC%"=="3"   echo &#91;ERR] Refused on safety grounds, see the reason above.
if "%RC%"=="4"   echo &#91;ERR] Link creation failed, see the reason above.
rem CMDCMDLINE holds this file name when double-clicked, and does not when
rem invoked from an existing prompt - that is how we tell the two apart.
echo %CMDCMDLINE% | find /i "%~nx0" >nul &amp;&amp; pause
endlocal &amp; exit /b %RC%

rem ===========================================================================
rem  :ContainsAppDash -- set WARN=1 when LINKNAME contains "app-"
rem ===========================================================================
:ContainsAppDash
set "WARN=0"
set "WCOPY=!LINKNAME!"
set "WFOUND="
:ContainsLoop
if "!WCOPY!"=="" goto ContainsDone
if not defined WFOUND (
    if "!WCOPY:~0,4!"=="app-" set "WFOUND=1"
)
set "WCOPY=!WCOPY:~1!"
goto ContainsLoop
:ContainsDone
if defined WFOUND set "WARN=1"
exit /b 0

rem ===========================================================================
rem  :CheckGuard -- set E_GUARD=1 when LINKNAME looks like app-&lt;digit>...
rem ===========================================================================
:CheckGuard
set "E_GUARD=0"
set "GHEAD=!LINKNAME:~0,4!"
if not "!GHEAD!"=="app-" exit /b 0
set "GTAIL=!LINKNAME:~4!"
if "!GTAIL!"=="" exit /b 0
rem the character right after app- must be a digit
set "GONE=!GTAIL:~0,1!"
if "!GONE!"=="0" set "E_GUARD=1" &amp; exit /b 0
if "!GONE!"=="1" set "E_GUARD=1" &amp; exit /b 0
if "!GONE!"=="2" set "E_GUARD=1" &amp; exit /b 0
if "!GONE!"=="3" set "E_GUARD=1" &amp; exit /b 0
if "!GONE!"=="4" set "E_GUARD=1" &amp; exit /b 0
if "!GONE!"=="5" set "E_GUARD=1" &amp; exit /b 0
if "!GONE!"=="6" set "E_GUARD=1" &amp; exit /b 0
if "!GONE!"=="7" set "E_GUARD=1" &amp; exit /b 0
if "!GONE!"=="8" set "E_GUARD=1" &amp; exit /b 0
if "!GONE!"=="9" set "E_GUARD=1" &amp; exit /b 0
exit /b 0

rem ===========================================================================
rem  :ConsiderDir "&lt;dirname>" -- keep it in CAND if it is a usable version dir
rem
rem  This is a subroutine on purpose. It used to be a label INSIDE the
rem  "for /f ... do ( ... )" block, with "goto SkipD" as the skip path. That
rem  is the classic batch trap: goto into a block label leaves the parser in
rem  the wrong context, so the block's closing ")" is reported as
rem  ") was unexpected at this time.". A goto that jumps OUT of a block is
rem  fine; jumping INTO one is not. Using "call" keeps every goto outside.
rem
rem  Same reason it does not use setlocal: it must append to the caller's
rem  CAND list.
rem
rem  NOTE: the argument is held in DNAME, never in CD. "%CD%" is a cmd dynamic
rem  variable that expands to the current working directory.
rem
rem  The name test uses string substitution, NOT findstr and NOT a regex.
rem  Debug output proved that inside cmd a double quoted "/c:^app-&#91;0-9]&#91;0-9.]*$"
rem  never matches, whatever findstr receives is not what we think it gets
rem  (caret and dollar are cmd metacharacters on that command line).
rem  Substitution has no such ambiguity:
rem      1. must start with app-
rem      2. strip app-, the remainder must not be empty
rem      3. the remainder must consist only of digits and dots
rem  Verified to accept exactly the same set as the old regex on real data,
rem  plus it additionally rejects mixed case like APP-1.2.
rem ===========================================================================
:ConsiderDir
set "DNAME=%~1"

rem --- name test by substitution, see the note above ---
rem must begin with app-
if "!DNAME:~0,4!" neq "app-" exit /b 0
rem strip the app- prefix, the remainder must not be empty
set "TAIL=!DNAME:~4!"
if "!TAIL!"=="" exit /b 0
rem delete every digit and dot; whatever survives must be empty.
rem Written out longhand instead of a "for %%d in (...)" loop: this routine is
rem reached through CALL, and loop-variable expansion inside a called
rem subroutine is one of the places where it is easy to get the wrong value.
set "REST=!TAIL!"
set "REST=!REST:0=!"
set "REST=!REST:1=!"
set "REST=!REST:2=!"
set "REST=!REST:3=!"
set "REST=!REST:4=!"
set "REST=!REST:5=!"
set "REST=!REST:6=!"
set "REST=!REST:7=!"
set "REST=!REST:8=!"
set "REST=!REST:9=!"
set "REST=!REST:.=!"
if not "!REST!"=="" exit /b 0

if not exist "!ROOT!\!DNAME!\GitHubDesktop.exe" exit /b 0
if exist "!ROOT!\!DNAME!\.dead" exit /b 0
if exist "!ROOT!\!DNAME!\.not-finished" exit /b 0
set "CAND=!CAND! !DNAME!"
echo     !DNAME!
exit /b 0

rem ===========================================================================
rem  :ReadTarget -- read the reparse point target of %LINK% into CURTGT
rem
rem  fsutil prints:
rem      Substitute Name:       \??\C:\path\to\app-3.6.6
rem  tokens=2,* skips the two words "Substitute" and "Name:", so %%B is the
rem  path. The "Print Name" line is unusable, its length is 0 for junctions.
rem  Do NOT use setlocal here, CURTGT could not reach the caller.
rem ===========================================================================
:ReadTarget
set "CURTGT="
set "SUB="
for /f "tokens=2,*" %%A in ('fsutil reparsepoint query "%LINK%" 2^>nul ^| findstr /i /c:"Substitute Name:"') do set "SUB=%%B"
if not defined SUB exit /b 0
rem strip the \??\ reparse prefix to get a normal drive path.
rem Delayed expansion is required here: with "%SUB:...=%" the percent form is
rem expanded while the line is read, so it would pick up the value from the
rem PREVIOUS call rather than the one just assigned on the line above.
set "CURTGT=!SUB:\??\=!"
rem %%~fP makes it absolute and drops the trailing backslash, so that the
rem caller can compare it with plain string equality
if defined CURTGT for %%P in ("!CURTGT!") do set "CURTGT=%%~fP"
exit /b 0

rem ===========================================================================
rem  :PickBest "&lt;dirname>" -- compare version numbers, replace BEST if larger
rem
rem  CALL is used to reuse code, so every routine works on global variables and
rem  must NOT use setlocal (values would not travel back to the caller).
rem  Comparison uses set /a and deliberately no zero padding, because set /a
rem  reads a leading zero as octal and fails on 08.
rem
rem  NOTE: the candidate is held in DNAME, never in CD - "%CD%" is a cmd
rem  dynamic variable holding the current working directory.
rem ===========================================================================
:PickBest
set "DNAME=%~1"
rem String substitution in the delayed-expansion family uses "!" at BOTH ends:
rem   !VAR:find=replace!
rem Writing "%" at the end (as this used to: !DNAME:app-=%) makes cmd treat
rem the trailing "%" as the start of a percent expansion, the substitution
rem never happens, and CV ends up holding the literal text "DNAME:app-=".
rem The debug output showed exactly that, which is why every version compared
rem equal and the first candidate always won.
set "CV=!DNAME:app-=!"

if not defined BEST (
    set "BEST=!DNAME!"
    goto PBEnd
)

rem split the current BEST version into B1..B4
rem Each variable is assigned from its own explicit token index. An earlier
rem version used "if not defined B1 set "B1=%%x"" and relied on the token
rem variables rotating as the for loop iterated; that mixes parse-time
rem (%%x) expansion with run-time ("if not defined") state and is fragile
rem inside a CALLed routine.
rem NOTE: every option of "for /f" must live inside ONE quoted string. Writing
rem for /f "tokens=1" delims=. %%x splits it into two parts and cmd reports
rem "delims was unexpected at this time."
set "BV=!BEST:app-=!"
set "B1=0" &amp; set "B2=0" &amp; set "B3=0" &amp; set "B4=0"
for /f "tokens=1 delims=." %%x in ("!BV!") do set "B1=%%x"
for /f "tokens=2 delims=." %%x in ("!BV!") do set "B2=%%x"
for /f "tokens=3 delims=." %%x in ("!BV!") do set "B3=%%x"
for /f "tokens=4 delims=." %%x in ("!BV!") do set "B4=%%x"

rem split the candidate version into C1..C4
set "C1=0" &amp; set "C2=0" &amp; set "C3=0" &amp; set "C4=0"
for /f "tokens=1 delims=." %%x in ("!CV!") do set "C1=%%x"
for /f "tokens=2 delims=." %%x in ("!CV!") do set "C2=%%x"
for /f "tokens=3 delims=." %%x in ("!CV!") do set "C3=%%x"
for /f "tokens=4 delims=." %%x in ("!CV!") do set "C4=%%x"

rem Segment by segment compare. Written flat on purpose: a chain of nested
rem if-blocks with goto in between is easy to break, and jumping into or
rem across block labels is exactly what caused ") was unexpected at this
rem time." earlier. Here every goto stays at the top level of the routine.
call :Seg !C1! !B1!
if "!CMP!"=="LT" goto PBEnd
if "!CMP!"=="GT" goto PBSet

call :Seg !C2! !B2!
if "!CMP!"=="LT" goto PBEnd
if "!CMP!"=="GT" goto PBSet

call :Seg !C3! !B3!
if "!CMP!"=="LT" goto PBEnd
if "!CMP!"=="GT" goto PBSet

call :Seg !C4! !B4!
if "!CMP!"=="GT" goto PBSet

rem All four segments are equal: keep the earlier one. Two directories with
rem the same version would be abnormal, and not overwriting is the safer
rem choice.
goto PBEnd

:PBSet
set "BEST=!DNAME!"
:PBEnd
exit /b 0

rem ===========================================================================
rem  :Seg &lt;A> &lt;B> -- numeric compare, result in CMP: GT / LT / EQ
rem
rem  The operands arrive as command line arguments. "%~1" is the correct way
rem  to read them: the tilde modifier belongs to the percent family only, so
rem  "!~1!" expands to nothing at all. That silently made both operands 0,
rem  every comparison returned EQ, and BEST stayed on the first candidate
rem  (it picked app-3.6.5 over app-3.6.6).
rem ===========================================================================
:Seg
set "SBA=%~1"
set "SBB=%~2"
if "!SBA!"=="" set "SBA=0"
if "!SBB!"=="" set "SBB=0"
rem "set /a X=X+0" normalises any stray leading zero, which set /a would
rem otherwise read as octal.
set /a "SBA=SBA+0" 2>nul
set /a "SBB=SBB+0" 2>nul
if !SBA! GTR !SBB! (set "CMP=GT") else if !SBA! LSS !SBB! (set "CMP=LT") else (set "CMP=EQ")
exit /b 0</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2026/10/13/github-desktop-%e5%9b%a2%e9%98%9f%ef%bc%8c%e4%b9%9f%e6%98%af%e8%8d%89%e5%8f%b0%e7%8f%ad%e5%ad%90/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>草台班子新证据</title>
		<link>https://www.somedoc.net/2026/07/16/%e8%8d%89%e5%8f%b0%e7%8f%ad%e5%ad%90%e6%96%b0%e8%af%81%e6%8d%ae/</link>
					<comments>https://www.somedoc.net/2026/07/16/%e8%8d%89%e5%8f%b0%e7%8f%ad%e5%ad%90%e6%96%b0%e8%af%81%e6%8d%ae/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Thu, 16 Jul 2026 10:35:04 +0000</pubDate>
				<category><![CDATA[软件]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6682</guid>

					<description><![CDATA[前两年的时候，想了解一下各个国产厂商的系统里，主题包是怎么个 <a href="https://www.somedoc.net/2026/07/16/%e8%8d%89%e5%8f%b0%e7%8f%ad%e5%ad%90%e6%96%b0%e8%af%81%e6%8d%ae/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">前两年的时候，想了解一下各个国产厂商的系统里，主题包是怎么个结构，就到网上找了它们各家的主题制作工具安装了一下，好像华为的叫 Theme Studio，OPPO 的叫 Theme Editor（全称是 HeyTapThemeEditor）。</p>



<p class="wp-block-paragraph">前两天的时候，Windows 突然报系统盘空间吃紧，也许是跟最近颇为安装了几个 AI 工具有关，要知道现在的这些 AI 工具大部分都是把 Electron 作为核心技术栈来使用，打出来的包安装之后随随便便就得一两个 GB，空间消耗吓人。于是找来磁盘占用分析工具帮助分析，看哪些可以卸载掉。</p>



<p class="wp-block-paragraph">当然了，HeyTapThemeEditor 就在清退之列。到系统设置里卸载它，可是每次执行过程刚刚开始，它就会报一个错，说“HeyTapThemeEditor 正在运行”，而且也不能够自动关闭，卸载无法完成。</p>



<p class="wp-block-paragraph">无论是拿进程管理工具查看，还是拿窗口遍历工具查看，都没有发现其它与 HeyTapThemeEditor 相关的处于运行状态的进程。只好怀疑它很愚蠢地把它自己当成了问题所在。由于它的报错信息里清晰地包含了“HeyTapThemeEditor”字样，而含有“HeyTapThemeEditor”字样的窗口被俺第一时间认为是它的检测目标，于是请 AI 临时手搓了个脚本，遍历所有窗口，把标题里含有的“HeyTapThemeEditor”去掉。去掉是成功了，但是错误没有消除，依然不能卸载。</p>



<p class="wp-block-paragraph">只好再次仔细检查卸载程序的进程树。<code>Uninstall HeyTapThemeEditor.exe</code> 是安装路径下首先被执行起来的，它会在系统的临时目录里创建一个名为 <code>Un_A.exe</code> 的可执行程序然后将之启动，若半途退出卸载过程，则临时目录里的内容也被清除。看上去很标准的手法，也没什么问题。</p>



<p class="wp-block-paragraph">不过等等，似乎不大对。通常说来，临时目录里的卸载程序启动的时候，安装目录里的内容就不该再有保持活着（执行中）的了，因为要为删除操作创造条件。而当前的状况是，安装目录下的 Uninstall 似乎把 Un_A 加载起来之后一直在等待中，会不会就是这样产生了互相锁死的问题？</p>



<p class="wp-block-paragraph">于是在报错之际，将临时目录里的 Un_A 复制了一份备用，然后把报错的卸载过程退出。再直接去运行复制出的那个 Un_A，果然，卸载得以顺利完成（见下图）。</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><a href="https://www.somedoc.net/wp-content/uploads/2026/07/屏幕截图-2026-07-14-002832.png" class="highslide-image" onclick="return hs.expand(this);"><img fetchpriority="high" decoding="async" width="499" height="360" src="https://www.somedoc.net/wp-content/uploads/2026/07/屏幕截图-2026-07-14-002832.png" alt="" class="wp-image-6685" srcset="https://www.somedoc.net/wp-content/uploads/2026/07/屏幕截图-2026-07-14-002832.png 499w, https://www.somedoc.net/wp-content/uploads/2026/07/屏幕截图-2026-07-14-002832-300x216.png 300w" sizes="(max-width: 499px) 100vw, 499px" /></a></figure>
</div>


<p class="wp-block-paragraph">此问题在 OPPO 官网提供的最新版的主题设计器中仍然存在。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2026/07/16/%e8%8d%89%e5%8f%b0%e7%8f%ad%e5%ad%90%e6%96%b0%e8%af%81%e6%8d%ae/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Latitude 5431 的曙光</title>
		<link>https://www.somedoc.net/2026/04/10/latitude-5431-%e7%9a%84%e6%9b%99%e5%85%89/</link>
					<comments>https://www.somedoc.net/2026/04/10/latitude-5431-%e7%9a%84%e6%9b%99%e5%85%89/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Fri, 10 Apr 2026 08:32:26 +0000</pubDate>
				<category><![CDATA[电脑]]></category>
		<category><![CDATA[硬件]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6676</guid>

					<description><![CDATA[之前试过在 Latitude 5431 的 WWAN 插槽里 <a href="https://www.somedoc.net/2026/04/10/latitude-5431-%e7%9a%84%e6%9b%99%e5%85%89/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">之前试过在 Latitude 5431 的 WWAN 插槽里插入 SSD，结果不会被认，刚刚在网上溜达，看到一个帖子，又似乎有点可能性的苗头：<a href="https://www.reddit.com/r/Dell/comments/186yed9/second_ssd_in_wwan_slot_working/">第二个 SSD，插在 WWAN 插槽——正常工作！： r/Dell &#8212; Second SSD in WWAN slot &#8211; working! : r/Dell</a>。</p>



<p class="wp-block-paragraph">其实根本就在于作者提到该插槽需要配置一下才能启用：</p>



<figure class="wp-block-image size-large"><a href="https://www.somedoc.net/wp-content/uploads/2026/04/image.png" class="highslide-image" onclick="return hs.expand(this);"><img decoding="async" width="1024" height="768" src="https://www.somedoc.net/wp-content/uploads/2026/04/image-1024x768.png" alt="" class="wp-image-6677" srcset="https://www.somedoc.net/wp-content/uploads/2026/04/image-1024x768.png 1024w, https://www.somedoc.net/wp-content/uploads/2026/04/image-300x225.png 300w, https://www.somedoc.net/wp-content/uploads/2026/04/image-768x576.png 768w, https://www.somedoc.net/wp-content/uploads/2026/04/image-175x131.png 175w, https://www.somedoc.net/wp-content/uploads/2026/04/image-667x500.png 667w, https://www.somedoc.net/wp-content/uploads/2026/04/image.png 1080w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p class="wp-block-paragraph">改天有空了可以试试。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2026/04/10/latitude-5431-%e7%9a%84%e6%9b%99%e5%85%89/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>黑苹果的转战</title>
		<link>https://www.somedoc.net/2026/04/05/%e9%bb%91%e8%8b%b9%e6%9e%9c%e7%9a%84%e8%bd%ac%e6%88%98/</link>
					<comments>https://www.somedoc.net/2026/04/05/%e9%bb%91%e8%8b%b9%e6%9e%9c%e7%9a%84%e8%bd%ac%e6%88%98/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Sun, 05 Apr 2026 14:59:59 +0000</pubDate>
				<category><![CDATA[macOS]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[软件]]></category>
		<category><![CDATA[问题解决]]></category>
		<category><![CDATA[黑苹果]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6673</guid>

					<description><![CDATA[家里用的主力机，原来也曾有过更换的经历，例如有一段时间就是  <a href="https://www.somedoc.net/2026/04/05/%e9%bb%91%e8%8b%b9%e6%9e%9c%e7%9a%84%e8%bd%ac%e6%88%98/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">家里用的主力机，原来也曾有过更换的经历，例如有一段时间就是 Latitude 5400，不过用过一阵之后，还是用回了 ThinkPad T450s。原因说起来特别简单，尽管 T450s 的 CPU 比较羸弱，但是在把内存升级到板载的 4GB 再加上 16GB 条子后，已经能代偿一部分。最重要的是，随着时间推移，机子里的 SSD 都升级到了 1TB，三块盘各自为政，Windows、Ubuntu、macOS 相安无事，巴适得很。</p>



<p class="wp-block-paragraph">可是逐步地，CPU 的短板还是被逐渐感知到了。更何况近些时来，大家热热闹闹地玩 AI，这个家伙不管是对 CPU 还是内存/GPU，都不会特别迁就。所以故技重施，先把办公室的干活儿主力机升了升格，从 Latitude 5401 升级到 Latitude 5431，再把退下来的 Latitude 5401 搬回家里来用（因为它也可以支持三硬盘）。不过比较讨厌的是，它已经不支持 BIOS/MBR 引导了，而且虽然没有实测，可直觉上其 m.2 插槽也不会支持 SATA 协议的 SSD 了。这就是说，现有的 T450s 上的三个系统，每一个都得换盘，不仅如此，还要视情况进行针对性的外科手术式迁移适配。</p>



<p class="wp-block-paragraph">最先，也最顺利的是 Windows 10，毕竟咱有积累的<a href="https://www.somedoc.net/2026/03/12/bios-mbr-%e5%bc%95%e5%af%bc%e7%9a%84-windows-10-%e5%b0%b1%e5%9c%b0%e8%bd%ac%e6%8d%a2%e4%b8%ba-uefi-gpt-%e5%bc%95%e5%af%bc%e5%ae%9e%e6%93%8d/" data-type="link" data-id="https://www.somedoc.net/2026/03/12/bios-mbr-%e5%bc%95%e5%af%bc%e7%9a%84-windows-10-%e5%b0%b1%e5%9c%b0%e8%bd%ac%e6%8d%a2%e4%b8%ba-uefi-gpt-%e5%bc%95%e5%af%bc%e5%ae%9e%e6%93%8d/">经验</a>打底。Ubuntu 的使用频度目前在家里不算高，因此暂时搁置。比较不好搞的是那份黑苹果。从以往的历篇博文也可以看出，在 T450s 上表现完美的Monterey 时至今日也已略显过时，而升级到 Sequoia 之后，本身在 T450s 上还算不得完美。历尽艰难，才把升级后遇到的无法登录的问题解决掉，而进入桌面后，陆续又发现一些使用上的毛刺。一是触控板驱动的不太对头，想触发点击操作，需要在触控板上连点两次；二是显示屏的亮度无法调节，这个情况应该是和显卡有关，理由是当我把这款盘从双显卡的 T450s 上平移到另一台只有核显的 T450s 上之后，调节亮度的控件就会正常出现；第三个问题也比较古怪，那就是如果把外置电池接上的话，系统引导的时候会卡死。基于这些，心想正好把黑苹果也干脆迁到 Latitude 5401 上就好了。</p>



<p class="wp-block-paragraph">找了一块闲置的 m.2 的 SSD，用 DiskGenius 的磁盘克隆功能成功把原来 NGFF SSD 上的内容克隆了过来。需要说明的是，这份黑苹果目前面临着两个问题：1. 如何在 Latitude 5401 上才能引导起来；2. 它自身的状态与 Latitude 5401 是否能兼容。第一个问题的解决方法很标准，那就是在网络上找是否有现成的 EFI 被人分享了出来，幸运的很，确实有，在<a href="https://github.com/TeksuSiK/hackintosh-5401">这里</a>。不过在看作者的文档时，发现了一个之前少有的要求：需要使用技术手段微调这台本子的固件界面里不曾体现的选项。途径则是在 OpenCore 引导时，按空格键调出额外的引导项，进入到 OpenShell 控制台，按需执行 setup_var.efi 命令。要调整项的偏移和值文档中有现成的，但作者又提醒说，各人应该自行确认一下，以免失误导致设备变砖。于是三太爷不得不阅读 <a href="https://github.com/datasone/setup_var.efi">setup_var.efi 的文档</a>，按步骤操作。首先去下载了 Intel 的 <a href="https://github.com/CE1CECL/IntelCSTools" data-type="link" data-id="https://github.com/CE1CECL/IntelCSTools">CSME System Tools 工具集</a>中的 Flash Programming Tool（FPTW64.exe），执行 <code>FPTW64.exe -bios -d bios-dump.bin</code> 把设备当前的 BIOS 转储，然后使用 <a href="https://github.com/LongSoft/UEFITool">UEFITool</a> 把所需的 <code>Setup/*</code> 节抽取出来，再用 <a href="https://github.com/LongSoft/IFRExtractor-RS">IFRExtractor RS</a> 把配置信息转为文本格式。最后打开转换后的文本文件，一一核对提及的需要修改的配置项的偏移，顺便把当前值记录下来，以便将来可能需要恢复回去。还好，所有的偏移均与 EFI 的作者列出的一致。</p>



<p class="wp-block-paragraph">作为一个好奇宝宝，显然在修改之前必须尝试一次引导，以观察不修改的话会有什么后果。后果其实很直接：不能引导成功，会重启。老老实实修改后，引导进入到了 macOS 的安装器界面。令人痛苦的是，界面的文字非中非英，一眼看去像是俄文。为了后续的步骤可以看明白，总得把这个界面显示问题解决掉。问了 AI 若干次，也按照它的回复对 OpenCore 的 config.plist 调整了若干次，统统无效（例如在 boot-args 里加上 -apple-language en_US，并指定 SystemLanguage 等）。最后没辙儿了，拿出手机打开 Google 翻译，照着屏幕一看，翻译 app 判定显示语言为匈牙利语。靠着它，才发现第二菜单下的唯一下拉菜单项其实就是语言切换的入口，这样一来，引导问题算是基本搞定。</p>



<p class="wp-block-paragraph">接下来就要尝试用这份 EFI 引导一下前面克隆完成的 SSD 上的 macOS Sequoia 了。把 SSD 插入插槽，用带着定制了的 EFI 的 macOS 安装引导 U 盘启动，结果是令人意想不到的失望，一而再再而三地重启，根本不能像之前那样进入到安装界面。后来不禁想，不会是 U 盘自己输了什么问题了吧，决定验证下。把 SSD 取下，用最简单的环境来测试，结果这次好了，又能进入安装界面（也是 macOS 的恢复界面）了。这下恍然大悟，看来是 SSD 在作祟。赶忙到搜索引擎里查询，果然<a href="https://zhuanlan.zhihu.com/p/698148497">有人早就警示</a>：若干多的 SSD 与黑苹果相克，而老夫所用的海力士 BC711 赫然在列。好像说西部数据的好一些。</p>



<p class="wp-block-paragraph">于是到闲鱼上查访，发现 SSD 这半年涨价的实在离谱，西数的 SN520，512GB 的竟然要 350 元上下（这还不算更离谱的标价）。要知道去年十月份，1TB 的 SSD 也就这个价。唉，黑苹果的迁移大业，先停停吧。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2026/04/05/%e9%bb%91%e8%8b%b9%e6%9e%9c%e7%9a%84%e8%bd%ac%e6%88%98/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ThinkPad T450s 黑苹果升级（再续）</title>
		<link>https://www.somedoc.net/2026/03/29/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7%ef%bc%88%e5%86%8d%e7%bb%ad%ef%bc%89/</link>
					<comments>https://www.somedoc.net/2026/03/29/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7%ef%bc%88%e5%86%8d%e7%bb%ad%ef%bc%89/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Sat, 28 Mar 2026 17:11:40 +0000</pubDate>
				<category><![CDATA[macOS]]></category>
		<category><![CDATA[电脑]]></category>
		<category><![CDATA[问题解决]]></category>
		<category><![CDATA[黑苹果]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6670</guid>

					<description><![CDATA[前两天的《ThinkPad T450s 黑苹果升级（后续）》 <a href="https://www.somedoc.net/2026/03/29/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7%ef%bc%88%e5%86%8d%e7%bb%ad%ef%bc%89/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">前两天的《<a href="https://www.somedoc.net/2026/03/25/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7%ef%bc%88%e5%90%8e%e7%bb%ad%ef%bc%89/" data-type="link" data-id="https://www.somedoc.net/2026/03/25/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7%ef%bc%88%e5%90%8e%e7%bb%ad%ef%bc%89/">ThinkPad T450s 黑苹果升级（后续）</a>》中，初步解决了登录进入系统的问题，不过仍有使用上的不便需要进一步处理。第一，就是图形界面响应极为迟钝，很显然，显卡驱动方面确实尚不到位；第二就是那个无法登录的用户，是否以及如何才能抢救回来。</p>



<p class="wp-block-paragraph">为了叙述方便，我把一开始升级成功时采用的 <a href="https://github.com/CLAY-BIOS/Lenovo-ThinkPad-T450s-Hackintosh-OpenCore/issues/137" data-type="link" data-id="https://github.com/CLAY-BIOS/Lenovo-ThinkPad-T450s-Hackintosh-OpenCore/issues/137">EFI 称为第一版本</a>，简称 E1，后面找到的、测试离线新建用户成功的 <a href="https://github.com/ad1107/T450s-Sequoia-Hackintosh" data-type="link" data-id="https://github.com/ad1107/T450s-Sequoia-Hackintosh">EFI 称为第二版本</a>，即 E2。当然，前文里其实也已经透露出，最后发现无论是 E1 还是 E2，都不能使得升级前已经存在的用户登录进入桌面，而离线新建的用户无论是用 E1 引导还是 E2 引导，也都能进入桌面。</p>



<p class="wp-block-paragraph">区别在于，E1 的相关资料、讨论中，均未有进一步处理图形性能的内容，而 E2 的说明里则非常清晰地包括了此部分的内容。简而言之，其核心症结以及相应的处理手法如下：</p>



<ul class="wp-block-list">
<li>为了能够顺利安装 macOS Sequoia，需要在 OpenCore 的 <code>config.plist</code> 中将 T450s 伪装为一台 MacBookPro16,3 设备；</li>



<li>安装成功后，图形处理方面是不完善的；此不完善之处，需要借用 <a href="https://github.com/dortania/OpenCore-Legacy-Patcher">OpenCore Legacy Patcher</a>（也即 OCLP）来进行完善；</li>



<li>而由于 OCLP 运行时，是自动检测当前设备以进行修补，那么就不能让它把 T450s 识别为伪装的 MacBookPro16,3 设备，而要设别为真正与 T450s 匹配（接近）的 MacBookPro12,1，否则修补就会错位；</li>



<li>上述矛盾，需要借用 macOS Sequoia 的 USB 安装盘来解决：把安装盘上 EFI 里的伪装设备从 MacBookPro16,3 改为 MacBookPro12,1，并使用它把安装好的 macOS Sequoia 引导起来，这时执行 OCLP；</li>



<li>执行完毕后，把安装盘的 EFI 同步到硬盘上的 EPS 分区里，应该就告功成了；</li>
</ul>



<p class="wp-block-paragraph">再按照上述理解进行操作后，macOS 从硬盘引导起来就完全正常了，而且之前不能登录的那个用户自动就复活了。不过在过程中，也遇到了两个问题，记录于下。</p>



<p class="wp-block-paragraph">第一就是出于已经形成的认知，认为 E1 和 E2 版本的 EFI 尽管不一样，但其功效应该无甚差别，因此在 USB 安装盘的 EFI 是 E1 的情况下，按照对 E2 文档的理解，直接把伪装设备的型号（以及序列号等）从 MacBookPro16,3 改为 MacBookPro12,1，但这样改后用 E1 引导硬盘上的 macOS 会失败。把 USB 安装盘上的 EFI 从 E1 替换为 E2 并设置型号等信息后，再次引导硬盘上的 macOS 则成功了。</p>



<p class="wp-block-paragraph">第二是在引导成功后，执行 OCLP 时发生了错误，报 <code>Unable to resolve dependencies: 'com.rim.driver.BlackBerryUSBDriverInt' names a dependency on 'com.apple.iokit.IOUSBMassStorageClass', which was not found.</code>。在网络上搜索到<a href="https://github.com/vulgo/ga-h97n-wifi-hackintosh/issues/26">此处</a>，根据一名为 Rochmind 的<a href="https://github.com/vulgo/ga-h97n-wifi-hackintosh/issues/26#issuecomment-1325977919">回答</a>，到 <code>/Library/Extensions</code> 下，把两个以 <code>RIMBB</code> 开头的内核扩展模块移走了，再尝试 Post-Install Patch 就顺利成功了。 </p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2026/03/29/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7%ef%bc%88%e5%86%8d%e7%bb%ad%ef%bc%89/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ThinkPad T450s 黑苹果升级（后续）</title>
		<link>https://www.somedoc.net/2026/03/25/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7%ef%bc%88%e5%90%8e%e7%bb%ad%ef%bc%89/</link>
					<comments>https://www.somedoc.net/2026/03/25/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7%ef%bc%88%e5%90%8e%e7%bb%ad%ef%bc%89/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Wed, 25 Mar 2026 04:09:24 +0000</pubDate>
				<category><![CDATA[macOS]]></category>
		<category><![CDATA[问题解决]]></category>
		<category><![CDATA[黑苹果]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6665</guid>

					<description><![CDATA[前情请参阅：《ThinkPad T450s 黑苹果升级》。  <a href="https://www.somedoc.net/2026/03/25/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7%ef%bc%88%e5%90%8e%e7%bb%ad%ef%bc%89/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">前情请参阅：《<a href="https://www.somedoc.net/2025/11/16/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7/" data-type="link" data-id="https://www.somedoc.net/2025/11/16/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7/">ThinkPad T450s 黑苹果升级</a>》。</p>



<p class="wp-block-paragraph">为了解决登录不进桌面的问题，着实思索过不少可能的原因以及验证或者解决的路子，可惜得很，都没有奏效。从体感上，既然已经成功启动到了登录界面，EFI 其实是应该没什么问题的，但询问 AI 后，AI 的回答又让人不敢完全确定。AI 是这么说的，在登录界面时可能系统使用的是较为基础的显卡驱动程序，而当进入到用户桌面时，它会尝试加载可能更加匹配的高性能的显卡驱动，对应于本案例，则正好是发生卡死的时机。你看，这说法也相当成立。</p>



<p class="wp-block-paragraph">昨晚又想尝试一次，是因为发现了另一个人在 GitHub 上公开新的 EFI。这个人看上去是个新手，写的说明文档是从制作安装盘开始的，但三太爷作为一个普通用户，在之前使用的 EFI 仓库 issue 帖下发表请教而无人问津长达数月，把新出现的 EFI 当作稻草救救命也应该无可厚非。过程相对顺利，结果如出一辙。问题还是那个问题，输入用户名和密码，有错误还则罢了，有视觉动效略作提醒，停留在本界面等待重试，而如若没有错误，则等候动画旋转不止，就此无限下去。</p>



<p class="wp-block-paragraph">这次，三太爷感觉应该和 EFI 关联性不大了，毕竟这个新的 EFI 看上去和先前的 EFI 相关性极小，并非肉眼可见的衍生物。如果是这样的话，那么问题可能出在要登录的那个用户的软件环境上。要想验证这个想法，可行的方法有一个：为这个无法登录当前唯一用户的系统新建一个用户。经过查阅资料，发现大部分都要在恢复模式下进行操作，而这个黑苹果，如何进入恢复模式本身就是个讨厌差事。再度进行探求，发现了 <code>dscl</code> 命令的进阶用法，原来它是可以针对一份本机当前系统之外的系统进行操作的。</p>



<p class="wp-block-paragraph">这个命令的相关信息不算多，如果你在打 AI 的主意的话，三太爷特别提醒，要非常小心，它的知识库很可能不足够准确，尤其是对各个参数的形式要求。历经在命令行下的种种组合尝试，三太爷终于在 MacBook Pro 2015 无头骑士上，把 ThinkPad T450s 所用的黑苹果的系统盘用 USB 外接起来，成功创建了一个新的管理员账户。</p>



<p class="wp-block-paragraph">为了让可能的同路人少走弯路，此处将添加新用户的脚本给出。Monterey 是目标系统所在的宗卷，最后一行的 merge 是将新建用户指定为管理员。执行时会提示为新建用户输入密码，别不赘述。</p>



<pre class="crayon-plain-tag">export TARGET="/Volumes/Monterey"
export USERNAME="oldman"
export USERID="1976"

sudo dscl -f "$TARGET/var/db/dslocal/nodes/Default" localonly create ./Local/Default/Users/$USERNAME
sudo dscl -f "$TARGET/var/db/dslocal/nodes/Default" localonly create ./Local/Default/Users/$USERNAME UserShell /bin/zsh
sudo dscl -f "$TARGET/var/db/dslocal/nodes/Default" localonly create ./Local/Default/Users/$USERNAME RealName "Oldman"
sudo dscl -f "$TARGET/var/db/dslocal/nodes/Default" localonly create ./Local/Default/Users/$USERNAME UniqueID "$USERID"
sudo dscl -f "$TARGET/var/db/dslocal/nodes/Default" localonly create ./Local/Default/Users/$USERNAME PrimaryGroupID 20
sudo dscl -f "$TARGET/var/db/dslocal/nodes/Default" localonly create ./Local/Default/Users/$USERNAME NFSHomeDirectory /Users/$USERNAME
sudo dscl -f "$TARGET/var/db/dslocal/nodes/Default" localonly passwd ./Local/Default/Users/$USERNAME
sudo dscl -f "$TARGET/var/db/dslocal/nodes/Default" localonly merge ./Local/Default/Groups/admin GroupMembership $USERNAME</pre>



<p class="wp-block-paragraph">把黑苹果从 USB 上断开，放回到 T450s 重启，登录界面如愿出现了新创建的用户，并且在输入密码后成功登录。在界面里执行操作感觉很卡，不过这次老夫倾向于是 EFI 的锅了，因为这份新的 EFI 的安装指令中本就要求系统安装完成后还要使用 OpenCore Legacy Patcher 做一些善后事宜。但老夫的直觉想法是换回之前用的那份 EFI，:)。</p>



<p class="wp-block-paragraph">附录。AI 其实还提出过一个思路，它说如果你把一份 macOS 系统中存放于 <code>/var/db</code> 下的 <code>.AppleSetupDone</code> 文件删除的话，重启系统会引发新装系统后的设置助理执行起来。然而事实是老夫多次尝试，并没有任何效果，且该文件会被重新创建出来（macOS Sequoia）。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2026/03/25/thinkpad-t450s-%e9%bb%91%e8%8b%b9%e6%9e%9c%e5%8d%87%e7%ba%a7%ef%bc%88%e5%90%8e%e7%bb%ad%ef%bc%89/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>光速回旋</title>
		<link>https://www.somedoc.net/2026/03/14/%e5%85%89%e9%80%9f%e5%9b%9e%e6%97%8b/</link>
					<comments>https://www.somedoc.net/2026/03/14/%e5%85%89%e9%80%9f%e5%9b%9e%e6%97%8b/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Sat, 14 Mar 2026 10:02:00 +0000</pubDate>
				<category><![CDATA[生活]]></category>
		<category><![CDATA[电脑]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6660</guid>

					<description><![CDATA[前天刚刚写了一篇博文，记录了把一个 SATA 硬盘上的 BI <a href="https://www.somedoc.net/2026/03/14/%e5%85%89%e9%80%9f%e5%9b%9e%e6%97%8b/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">前天刚刚写了一篇<a href="https://www.somedoc.net/2026/03/12/bios-mbr-%e5%bc%95%e5%af%bc%e7%9a%84-windows-10-%e5%b0%b1%e5%9c%b0%e8%bd%ac%e6%8d%a2%e4%b8%ba-uefi-gpt-%e5%bc%95%e5%af%bc%e5%ae%9e%e6%93%8d/" data-type="link" data-id="https://www.somedoc.net/2026/03/12/bios-mbr-%e5%bc%95%e5%af%bc%e7%9a%84-windows-10-%e5%b0%b1%e5%9c%b0%e8%bd%ac%e6%8d%a2%e4%b8%ba-uefi-gpt-%e5%bc%95%e5%af%bc%e5%ae%9e%e6%93%8d/">博文</a>，记录了把一个 SATA 硬盘上的 BIOS+MBR 方式引导的 Windows 10 系统克隆到了 NVMe SSD 上，并成功修改为 UEFI+GPT 方式引导的过程。谁能想到，昨天不得已就反向操作了回去。</p>



<p class="wp-block-paragraph">起因其实特别简单：不管是源盘还是目的盘，三太爷计划卖掉一个回血。在记录文中，把源盘称为 SATA 硬盘是为了不被细节纠缠，事实上，它是一块 SanDisk A110 型号的 2260 规格的 PCIe SSD。由于这种类型的磁盘生产以及销售时间较短，总量较少，因而后续的电脑尤其是笔记本对其支持往往不甚理想，大多处于薛定谔的猫式的不确定状态。而三太爷手中已经将与之原配的老电脑 HP ZBook 15 G2 脱手了，所以优先把它挂到了网上。</p>



<p class="wp-block-paragraph">介绍完了源盘，又轮到了目的盘。这也不是省油灯，是一块从华为的网络设备上拆出来的 SSD，型号 P46。P46 这盘之前没用过，但众所周知华为一贯遥遥领先，想必也该受人追捧吧。好奇之下，把 P46 也挂到了闲鱼上，价格比同容量的 A110 还略贵。</p>



<p class="wp-block-paragraph">两盘挂出一段时间，均无人问津。三太爷强行给自己加戏，想着 A110 便宜一点，恐怕会被人优先拍下。为了把它上面的系统保留下来，以免售出时起急，故有前述迁移操作，并在迁移之后，好心的把 A110 全盘清空了事。</p>



<p class="wp-block-paragraph">事与愿违，这迁移刚刚结束，P46 被人买了，发货前只好再次把系统迁回去。只是对于 A110 来说，这一去一回，尽管其中的内容一直保持着没变，但磁盘分区的格式以及操作系统的引导方式已经改变了。但要命的地方恰在于此：把它插到笔记本里尝试引导的时候蓝屏了，报 INACCESSIBLE_BOOT_DEVICE 错误。原因很简单，操作系统上次记录的本引导介质应该是一块 NVMe 的 SSD，谁能想到它偷偷降级了呢，而且降到了一个少见的标准上。</p>



<p class="wp-block-paragraph">三太爷应对这种局面虽说不是烂熟于胸，也算得上有所经验。感觉调整硬盘驱动可能会是一个可能的可行方向。于是询问 AI，NVMe 和 SATA 对应的驱动在系统中通常是哪个，AI 告知是 stornvme.sys 和 storahci.sys，但同时也强烈警告，自己随便调整其启用或者禁用状态的话可能导致系统无法引导。好在咱目前已经是无法引导了，也就不怕雪上加霜了。用 PE 优盘引导，把 SSD 上的 SYSTEM 注册表单元加载起来，到 ControlSet001 下找到上述两个驱动的注册表项看了一眼，把 stornvme 的 start 从自动加载 0 改为禁用 4，而 storahci 保持 0 未动。关机，拔掉 PE 优盘尝试从 SSD 正常启动。可惜的很，仍旧一再蓝屏。</p>



<p class="wp-block-paragraph">但是！！！在不记得第三次还是四次的自动重启过程中，三太爷一直用手按 F8，某个屏幕一闪而过之后，系统竟然引导成功了！也不知道是灵异事件，还是老夫的手动修改确实生效了。仅此记录。</p>



<p class="wp-block-paragraph">附：今天有人问 A110 的详情了，看来还得找个地方迁移……</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2026/03/14/%e5%85%89%e9%80%9f%e5%9b%9e%e6%97%8b/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>BIOS/MBR 引导的 Windows 10 就地转换为 UEFI/GPT 引导实操</title>
		<link>https://www.somedoc.net/2026/03/12/bios-mbr-%e5%bc%95%e5%af%bc%e7%9a%84-windows-10-%e5%b0%b1%e5%9c%b0%e8%bd%ac%e6%8d%a2%e4%b8%ba-uefi-gpt-%e5%bc%95%e5%af%bc%e5%ae%9e%e6%93%8d/</link>
					<comments>https://www.somedoc.net/2026/03/12/bios-mbr-%e5%bc%95%e5%af%bc%e7%9a%84-windows-10-%e5%b0%b1%e5%9c%b0%e8%bd%ac%e6%8d%a2%e4%b8%ba-uefi-gpt-%e5%bc%95%e5%af%bc%e5%ae%9e%e6%93%8d/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Thu, 12 Mar 2026 05:11:08 +0000</pubDate>
				<category><![CDATA[Windows]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[电脑]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6657</guid>

					<description><![CDATA[有一块祖传的 SATA SSD 上，有一份 Windows  <a href="https://www.somedoc.net/2026/03/12/bios-mbr-%e5%bc%95%e5%af%bc%e7%9a%84-windows-10-%e5%b0%b1%e5%9c%b0%e8%bd%ac%e6%8d%a2%e4%b8%ba-uefi-gpt-%e5%bc%95%e5%af%bc%e5%ae%9e%e6%93%8d/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">有一块祖传的 SATA SSD 上，有一份 Windows 10，引导模式还停留在 BIOS/MBR 状态。想把它接续着用起来，但是新机器已经放不下 SATA 盘了，而且也不支持传统引导方式了。正好有一块同容量的 NVMe SSD，于是产生把系统克隆过来，改造后再使用的想法。</p>



<p class="wp-block-paragraph">在 Windows 10 下，把源盘（SATA 盘）和目标盘（NVMe 盘）都用 USB 方式连接好。</p>



<p class="wp-block-paragraph">第一步是先用 DiskGenius 磁盘克隆，此后源盘抛开，不再参与后续步骤。成功克隆目标盘后的第二步是用 DiskGenius 的分区表类型转换功能把其 MBR 分区表格式转换为 GPT 分区表格式。</p>



<p class="wp-block-paragraph">第三步有少许精微，其目的是要把 GPT 引导所需的 ESP 分区和 Windows 系统所需的 MSR 分区设定好。一个常见的 GPT 分区布局是，ESP 分区位于磁盘首，而 MSR 稍显随意，相邻地位于系统所在分区之前或者之后（暂不考虑 Windows 的恢复分区）。现实情况是在 MBR 布局下已经存在于系统所在分区之后，因此仅需将其分区类型在 DiskGenius 的编辑分区参数界面中重新调整为 MSR 类型即可。ESP 分区需要单独处理，主要原因是当前磁盘上没有空余空间可以使用，因此需要先用 DiskGenius 调整系统所在分区的大小，在其之前缩出 500MB 即可。此时即可通过 DiskGenius 的“建立ESP/MSR分区”菜单完成 ESP 分区的创建。</p>



<p class="wp-block-paragraph">接下来还有两步路走。</p>



<p class="wp-block-paragraph">首先是把刚刚创建好的 ESP 分区上置入引导文件。正好当前运行的主机也是 Windows 10，因此可以在管理员权限的命令行下执行 
			<span id="crayon-6adf78e934c60695385803" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-i">bcdboot</span><span class="crayon-h"> </span><span class="crayon-v">C</span><span class="crayon-o">:</span><span class="crayon-sy">\</span><span class="crayon-v">Windows</span><span class="crayon-h"> </span><span class="crayon-o">/</span><span class="crayon-i">s</span><span class="crayon-h"> </span><span class="crayon-v">S</span><span class="crayon-o">:</span><span class="crayon-h"> </span><span class="crayon-o">/</span><span class="crayon-i">f</span><span class="crayon-h"> </span><span class="crayon-v">UEFI</span><span class="crayon-h"> </span><span class="crayon-o">/</span><span class="crayon-i">l</span><span class="crayon-h"> </span><span class="crayon-v">zh</span><span class="crayon-o">-</span><span class="crayon-v">cn</span></span></span> 来完成。其中的 
			<span id="crayon-6adf78e934c66730516150" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">S</span><span class="crayon-o">:</span></span></span> 是给 ESP 分区分配的盘符，这个工作也可以在 DiskGenius 里做。</p>



<p class="wp-block-paragraph">第二步则是确认上一步创建出来的 BCD 文件是否可用，最佳工具首推 Bootice。使用它打开 <code>S:\EFI\Microsoft\Boot\BCD</code> 文件，在高级编辑模式下，把左侧栏中的对象树里的必要节点仔细检查一遍（主要是 <code>Windows Boot Manager</code> 节点以及 <code>Application Objects</code> 和 <code>Windows Resume Objects</code> 分支下的节点，把带有 <code>Device</code> 字样的 key（一般是 <code>ApplicationDevice</code> 或者 <code>OSDevice</code>）都要仔细审视一番，把对应的磁盘和分区（要特别注意：<strong>引导分区和系统分区是不一样的！</strong>）选择正确后保存。理论上，这块磁盘应该准备就绪了。</p>



<p class="wp-block-paragraph">在实践中，上述操作一次性成功。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2026/03/12/bios-mbr-%e5%bc%95%e5%af%bc%e7%9a%84-windows-10-%e5%b0%b1%e5%9c%b0%e8%bd%ac%e6%8d%a2%e4%b8%ba-uefi-gpt-%e5%bc%95%e5%af%bc%e5%ae%9e%e6%93%8d/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>路由器上跑 Debian？</title>
		<link>https://www.somedoc.net/2026/02/15/%e8%b7%af%e7%94%b1%e5%99%a8%e4%b8%8a%e8%b7%91-debian%ef%bc%9f/</link>
					<comments>https://www.somedoc.net/2026/02/15/%e8%b7%af%e7%94%b1%e5%99%a8%e4%b8%8a%e8%b7%91-debian%ef%bc%9f/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Sun, 15 Feb 2026 04:04:09 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[硬件]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6654</guid>

					<description><![CDATA[开年以来，眼看着身边的杂物越来越多，左抬脚右迈步，一碰一个纸 <a href="https://www.somedoc.net/2026/02/15/%e8%b7%af%e7%94%b1%e5%99%a8%e4%b8%8a%e8%b7%91-debian%ef%bc%9f/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">开年以来，眼看着身边的杂物越来越多，左抬脚右迈步，一碰一个纸箱子，遂决定今年以“出”为基本指导方针，“入”则需要慎之又慎。</p>



<p class="wp-block-paragraph">收拾，隔三岔五上架到小黄鱼上。一下子就从灰堆里扒拉出来几个路由器。内存 512MB，即使放在今天，也不能算小。之前是在 TF 卡上烧的 OpenWrt，昨晚又想到，有没可能给它上面跑个 Debian？</p>



<p class="wp-block-paragraph">搜了一下，竟然搜到一个博主，其博客地址是 <a href="https://blog.hackpascal.net/">HackPascal @ Home</a>，在数年前写过一个系列文章，就是在讲这个目标。大体看了一下，还挺复杂的，真要搞需要投入相当多的时间和精力，暂备于此。</p>



<ul class="wp-block-list">
<li><a href="https://blog.hackpascal.net/2019/09/debian-for-mt7621-%e6%8a%98%e8%85%be%e8%ae%b0-1-%e5%ae%9e%e7%8e%b0%e5%9f%ba%e7%a1%80%e7%b3%bb%e7%bb%9f%e6%ad%a3%e5%b8%b8%e5%90%af%e5%8a%a8/">Debian for MT7621 折腾记 (1) – 实现基础系统正常启动 | HackPascal @ Home</a></li>



<li><a href="https://blog.hackpascal.net/2019/10/debian-for-mt7621-%e6%8a%98%e8%85%be%e8%ae%b0-2-%e5%ae%9e%e7%8e%b0-mt7530-dsa/">Debian for MT7621 折腾记 (2) – 实现 MT7530 DSA | HackPascal @ Home</a></li>



<li><a href="https://blog.hackpascal.net/2019/10/debian-for-mt7621-%e6%8a%98%e8%85%be%e8%ae%b0-3-%e7%bc%96%e8%af%91-debian-kernel-package/">Debian for MT7621 折腾记 (3) – 编译 Debian Kernel Package | HackPascal @ Home</a></li>
</ul>



<p class="wp-block-paragraph">顺便提一下，在查找阅读过程中发现，该博主似乎是所谓的不死引导器 Breed 的作者。未予追证，后续再说。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2026/02/15/%e8%b7%af%e7%94%b1%e5%99%a8%e4%b8%8a%e8%b7%91-debian%ef%bc%9f/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>老树孕苞待春回</title>
		<link>https://www.somedoc.net/2026/02/09/%e8%80%81%e6%a0%91%e5%ad%95%e8%8b%9e%e5%be%85%e6%98%a5%e5%9b%9e/</link>
					<comments>https://www.somedoc.net/2026/02/09/%e8%80%81%e6%a0%91%e5%ad%95%e8%8b%9e%e5%be%85%e6%98%a5%e5%9b%9e/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Sun, 08 Feb 2026 16:54:03 +0000</pubDate>
				<category><![CDATA[Windows]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[软件]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6651</guid>

					<description><![CDATA[最近 AI 的世界里，agent 作为明星是非常耀眼的存在。 <a href="https://www.somedoc.net/2026/02/09/%e8%80%81%e6%a0%91%e5%ad%95%e8%8b%9e%e5%be%85%e6%98%a5%e5%9b%9e/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">最近 AI 的世界里，agent 作为明星是非常耀眼的存在。微软公司不甘寂寞，也推出了自己的 agent 相关的贡献。作为一名上了岁数的 Windows 程序员，当再次看到 Microsoft Agent 这个词组时，却不由自主地想到了多年前的另一个同名技术。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Microsoft Agent 是 Microsoft 开发的一项技术，允许用户与应用程序和网页内的动画角色进行交互。它使用文本到语音（TTS）引擎来允许动画角色与最终用户对话。这些动画角色被称为“Microsoft Agent”。从 Windows 98 开始，它就预装在 Windows 中。Microsoft Agent 持续与所有 Windows 版本一起发布，直到 2009 年的 Windows 7，此后 Microsoft 停止提供 Microsoft Agent。Microsoft Agent 的最新版本是 1998 年 10 月 12 日发布的 2.0 版。</p>
</blockquote>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Microsoft Agent 允许软件用户通过 Microsoft Agent 角色编辑器创建角色。微软在原始页面上制作了四个默认角色，包括精灵、梅林、皮迪和罗比。其他一些值得注意的角色是 1997 年至 2003 年 Microsoft Office 套件中出现的角色，包括 Clippy、Dot 或 Office Logo。Windows XP 搜索助手也是 Microsoft Agent，包括 Rover。另一个值得一提的 Microsoft Agent 是 Bonzi，一只紫色大猩猩，它与 BonziBUDDY 捆绑在一起，直到其停产。Bonzi 角色后来因其多个 meme 视频而成为 meme，随后受到了广泛关注，最后又因一名主播直播了 XP 损毁而导致 Bonzi 角色的受欢迎程度上升。</p>
</blockquote>



<p class="wp-block-paragraph">上述引言来自于一个围绕 Microsoft Agent 技术而创建的<a href="https://tmafe.com/" data-type="link" data-id="https://tmafe.com/">社区</a>页面，比较全面地介绍了 Microsoft Agent 是什么这一命题。</p>



<p class="wp-block-paragraph">对于国人来讲，最知名的一个 Microsoft Agent 其实应该是瑞星杀毒软件中的小狮子，尽管在后期，由于各种原因，把小狮子的展示技术迁移到了 Adobe Flash。由于小狮子的形象备受人民群众的喜爱，甚至有爱好者自行制作了不包含瑞星杀毒软件本体的独立的小狮子安装包，如果你想体验的话，可以到这里下载：<a href="https://m.crsky.com/mip/soft/94024.html">瑞星小狮子独立版下载 &#8211; 非凡手机软件</a>。</p>



<p class="wp-block-paragraph">当对技术的追思到二这个份儿上的时候，老夫突然想把非 SWF 版本的原始小狮子找到再看看。这个工作稍微绕了点，因为首先就是要找一个更老版本的瑞星杀毒软件的安装包。当然功夫不负有心人，还是被俺找到一份：<a href="https://mydown.yesky.com/pcsoft/204802.html">瑞星杀毒软件 2004 官方 PC 版免费下载-天极下载</a>。下载解包后看到里面确实存在 RsAgent.acs，这就和记忆对上了。</p>



<p class="wp-block-paragraph">acs 文件是微软的角色构建工具（可以在<a href="https://agentpedia.tmafe.com/wiki/Microsoft%20Agent%20Character%20Editor" data-type="link" data-id="https://agentpedia.tmafe.com/wiki/Microsoft%20Agent%20Character%20Editor">这里</a>下载）生成的，而制作的原始素材会用到 bmp 位图文件以及 wav 音频文件。如果你想把现成的 acs 文件逆向恢复成素材集，最妥善的方法当然是去了解 acs 的文件格式，尽管微软官方并未公开，好在前人栽过这棵树的，可以参看这个<a href="http://www.lebeausoftware.org/download.aspx?ID=25001fc7-18e9-49a4-90dc-21e8ff46aa1d" data-type="link" data-id="http://www.lebeausoftware.org/download.aspx?ID=25001fc7-18e9-49a4-90dc-21e8ff46aa1d">链接</a>。文档作者也写好了一个现成的工具，在<a href="http://www.lebeausoftware.org/download.aspx?ID=83b03bd5-18dc-467d-9778-e941536897e4">这儿</a>下载。</p>



<p class="wp-block-paragraph">微软官方文档站里目前还有相关内存，可以在<a href="https://learn.microsoft.com/en-us/windows/win32/lwef/microsoft-agent">这儿</a>参阅；一个简单的入门介绍文档可以参阅<a href="https://github.com/Planet-Source-Code/mahangu-the-complete-guide-to-ms-agent__1-13181">这儿</a>。</p>



<p class="wp-block-paragraph">有了这些，俺又不免想到，怎么在这新时代里，旧物利用一下儿呢？</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2026/02/09/%e8%80%81%e6%a0%91%e5%ad%95%e8%8b%9e%e5%be%85%e6%98%a5%e5%9b%9e/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
