2026/06/29

What Remains When the Technology Is Gone?

By Eden Wu

 

 

 


 

 

I spent twenty years chasing a technology.
Only recently did I realize I was actually chasing an idea. 

 

 

When I was younger, I believed I was investing my career in a framework.

Over the years, that framework gradually disappeared from the industry.

For a long time, I thought that meant twenty years of experience had become obsolete.

I was wrong.

It took me two decades to realize that I hadn't really learned the framework.

I had learned the design behind it.

 

 

 

Every technology eventually reaches the end of its life.

COBOL.

PowerBuilder.

Silverlight.

Flash.

AngularJS.

Windows Forms.

One day, even React, Flutter, or whatever we currently call modern may join that list.

The technology changes.

The real question is:

What remains when the technology is gone?

For me, the answer was not DataSnap.

It was something much smaller.

A feature called Delta.

At first glance, Delta looked like an implementation detail.

It was not.

It was a design philosophy.

Delta was not really about ClientDataSet.

It was not really about DataSetProvider.

It was not really about MIDAS.

It was not even really about Delphi.

It was about representing change.

Instead of sending an entire dataset back to the server, Delta captured what had actually changed:

  • what was inserted,
  • what was modified,
  • what was deleted,
  • and what state each record carried.

That idea was elegant.

It separated data from intent.

It allowed the client to work with data locally, then send only the meaningful changes back to the middle tier.

Looking back now, I realize that this was the part I had been chasing for twenty years.

Not the component.

Not the IDE.

Not the framework.

The idea.

In the MIDAS and DataSnap world, this idea was wrapped inside Borland's architecture.

For a long time, I thought I needed that architecture in order to keep the idea alive.

But AI changed that.

When AI became capable of helping me design and rebuild software systems, I expected it to help me find a DataSnap replacement.

Instead, it helped me understand that I did not need a replacement at all.

I needed a new place where the same idea could live.

Node.js could express it.

PHP could express it.

React could express it.

A modern data grid could express it.

Even a browser could participate in it.

The framework had changed.

The idea had not.

That realization changed how I look at my own experience.

For years, I thought my value came from knowing a specific technology deeply.

Now I think my real value came from understanding why that technology worked.

DataSnap may disappear.

ClientDataSet may become legacy.

The tools may be pushed aside by newer platforms, newer frameworks, and newer development models.

But the architectural lesson remains.

Track changes.

Preserve intent.

Separate the client state from the persistence layer.

Let the middle tier decide how those changes should be applied.

That is not a Delphi idea.

That is a software design idea.

And once I understood that, I no longer felt trapped by the tool.

I could finally carry the idea forward.

This is what twenty years of experience really gave me.

Not mastery of a product.

Not loyalty to a framework.

Not nostalgia for a disappearing ecosystem.

It taught me how to recognize a design principle that could survive the death of the tool that introduced it to me.

Maybe that is what engineering really is.

Not memorizing frameworks.

Not defending tools forever.

But learning how to extract principles that outlive them.

When the tool is gone, what did you actually learn?

For me, the answer is finally clear.

I did not spend twenty years learning DataSnap.

I spent twenty years learning Delta.

And Delta is still alive.

2026/06/25

我花了二十年,才知道自己真正學會的是什麼

 作者:吳祐賓

 


 


 

我年輕時學了一套技術。

後來市場淘汰了它。

我花了二十年,一直以為自己是在追那套技術。

直到最近才發現,我真正追的是它背後的思想。

現在,我終於能把那個思想帶到新的技術上。

 

 

這不是一篇關於 DataSnap 的文章,這是一篇關於「如何把一項即將消失的技術,提煉成一輩子都帶得走的能力」的文章



 「如果有一天不能再用 DataSnap,我真正想保留的是什麼?」

 

 

【DataSnap 死了,但 Delta 還活著】

—— 我花了二十年,才知道自己真正學會的是什麼

 

從 MIDAS 到 AI 時代的一場 30 年追逐


DataSnap 在漫長的 MIDAS 發展史上,最關鍵的就是 DataSetProvider 這個元件


在分散式架構當時還在萌芽階段, Borland 在 1995~1997 年間做了兩件事:


1. 買下 CORBA 技術。併購了 Visigenic (VisiBroker, CORBA) 公司
2. Delphi 3 加入 MIDAS (Multi-Tier Distributed Application Services)


(和 2015 年 Idera/Embarcadero 公司併購 AnyDAC 歷史有夠像)

 

當時 MIDAS 架構的 Delta 異動機制簡直是劃時代的創舉,超越資料庫 Trigger 的架構,和後來 ADO.NET DataRowState 謎樣相似框架 (Anders Hejlsberg 被挖角和後來 Delphi 8 要去吃這塊的黑歷史有機會再說)

 

放到 2026 年的今天,這個架構仍然是先進且優雅的,在我個人執業生涯中:


.PHP 沒這東西
.Database Trigger 深不見底的難維護
.dot NET 的 OOOOO (一堆複雜的 Object 等著你背)


... 不是學習門檻過高,就是難以維護


沒一個能像 DataSnap 這麼能打的 (DataSnap 表示:還有誰!)


但在那時羸弱的網路環境:


1. 32K, 56K 數據撥接
2. 10Mbps 同軸電纜網路線
3. 10Mbps RJ-45 網路線


MIDAS 走在時代的尖端卻成為死在沙灘的第一人


不知道怪誰,就怪環境吧


2006 年,我首次接觸到 dbExpress 資料庫程式開發,我就覺得這架構實在太有趣了,當時的技術書籍除了陳惟彬老師的 C++ Builder資料庫程式設計-人事薪資系統 / Delphi 進銷存(會計)系統
這三本書有提到 Delta 更新的魔法外,再無其他書籍提到


案子一個接一個來,BDE Paradox 轉到 DBX 工作也不下 N 次,突然有一天,我被迫參與了網際網路戰場


當時在 Native Program 和 Web Program 之間的掙扎、談判、糾結,這講下去又可以講個好幾篇


與 AI 討論很久,才發現

 

原來我等的,不是一個 DataSnap 替代品,而是一個足夠有能力讓我重新實現 Delta 思想的前端

 

而 DevExtreme Data Grid 完美承接這個優點



那麼,中間層是否能再使用 Borland 的 DataSetProvider 或 Embarcadero 的 FdMemTable 就已不再重要


因為在 AI 的協助下,不論是 PHP 或 Node.js,都能完美地再現 DataSnap 經典的 Delta 魔法


而 DataSnap 都要被官方掃入垃圾桶了,糾結是要 ClientDataSet 還是 FireDAC 說白了就是偽議題


為人作嫁不如擴充自己,讓自己有底氣不受工具限制發展,才是工程師真正的生存之道


2026/06/18

倉頡五代升級攻略:Win 10/11 使用者的完美解決方案 (2026 加入一鍵更換、含倉頡三代升級)

作者:吳祐賓

 

 

你使用倉頡打字的時候,有沒有感受到奇怪的味道:

* 晚上的「晚」永遠都排在第二個字

* 「卜竹竹手一」打不出「産」?而「木中中女」打不出「樓」?

 

這代表你也是微軟倉頡四代的受災戶

 

很多被倉頡四代折磨的使用者,第一直覺都是改用『微軟新倉頡』(我也是)

 

但這其實只是在繞遠路——新倉頡早已年久失修,在現代應用程式 (如底層採用 WinUI 3.0 的軟體) 中完全無法運作

 

其實,你根本不需要拋棄熟悉的習慣,只要升級一下碼表,就能讓現在的微軟倉頡脫胎換骨

 

在這裡,我想邀請你,試試看這篇的解決方案,失敗了再找微軟新倉頡也不遲

 

在學習倉頡五代後,一直覺得微軟倉頡和新倉頡的倉頡四代碼表很不友善,一直到最近看到倉頡之友的【替換微軟倉頡碼表,盡享倉頡補完計劃的樂趣】,才知道原來微軟倉頡也可以更新碼表,終於能享受到輸入文字的樂趣了!

 

首先,本篇文章適用範圍:Windows 10 2004 版以上。記得,Windows 更新有時會覆蓋原來倉頡四代的碼表,記得要再回來更新 (2026/03/18 文末追加:一鍵更換 DLC)

 

 

倉頡碼表下載點

 

下載:MSCJData_Cangjie 開頭字樣檔案

例如:

 

 

給倉頡三代的使用者 (習慣打法為:「木中田女」=樓;「戈土心」=應) 

 

倉頡三代補完計劃替換碼表下載地址:https://github.com/Arthurmcarthur/Cangjie3-Plus/releases

 

 

GitHub 上 Cangjie3 倉頡三代碼表 20230909 版本的釋出與下載頁面截圖

 

 

給倉頡五代的使用者 (習慣打法為:「木中中女」=樓;「戈人土心」=應)

 

倉頡五代補完計劃替換碼表下載地址:https://github.com/Jackchows/Cangjie5/releases

 


GitHub Jackchows/Cangjie5 專案 Releases 頁面,顯示 v4.1-beta 最新版本與更新內容

 

安裝步驟

 

  1. 任務管理器裏結束兩個Microsoft IME進程。
  2. 進入 C:\Windows\System32\zh-hk 目錄,刪除
    • ChtCangjie.sdc
    • ChtCangjie.spd
    • ChtCangjieExt.lex
    三個文件(刪除前請備份),然後將此處提供的 ChtCangjieExt.lex,複製到該目錄。




  3. 打開「包含香港增補字符集字元(HKSCS)」開關。

 

 

完成上述步驟後請重新開機,接下來就可以好好體驗倉頡五代所帶來的暢快輸入感!

 

相信更多愛用倉頡的愛好者不滿足於預設的內容。不過因為我個人沒用到進階設定,想要了解更多細節的倉頡愛好者,歡迎再閱讀補完計畫說明檔。


總結

 

使用倉頡補完計畫優點

 

  • 字碼編排最貼近符合倉頡歷代改版標準。
  • 可以使用倉頡五代輸入方式。 
  • 重難(ZX)符號輸入完整呈現。
  • 還有微軟倉頡獨門快速鍵 100% 保留,例如:
    Ctrl+, ,
    Ctrl+' 、
    Ctrl+. 。
    Ctrl+; ;
    Ctrl+Shift+; :

    `+ +
    `- -
    `/ /
    `/ 向下鍵 ÷
    `* 向下鍵 ×
    `+ 向下鍵 ±

    Ctrl+[ 【 向下鍵 「...
    Ctrl+] 】 向下鍵 」...

    Ctrl+Alt+, 叫出特殊符號表 (輸入法整合器) 

 

使用倉頡補完計畫缺點


  • Z開頭的符號碼無法使用,只能使用微軟輸入法的符號快速輸入鍵輸入。 (20220904 MSCJData 版本已解決此問題)
  • 由於微軟倉頡的排序邏輯寫死在程序中,與碼表沒有關聯。補完計畫在調整此問題已將部首、筆畫、兼容區字符移除,RIME 則無此問題

 

整體來說優點大於缺點,能暢快享受輸入文字的樂趣,在這邊分享給大家。

 

2026/03/03 附記:

為什麼我執意要使用微軟輸入法引擎?

1. PIME 容易當機 (Python 不知道為什麼就閃退) 

2. 小小輸入法在 WinUI (Win App 2.0) 是不能用的,它只能在傳統應用程式下使用

3. 其它的自由倉頡、RIME 等輸入法引擎,操作習慣都要重新熟悉,新設備也需要另外安裝。但熟悉微軟輸入法引擎下,新環境我只需要忍耐字碼位置就好,比較不會換了新設備就變智缺化 (笑)

 

 

【2026/03/19 重大更新】一鍵修復 Windows 內建倉頡(三代/五代)選字排序、補完缺字!

 


 

 

上述人工步驟在每次被 Window Update 陰了後,就很懶得重複做,於是我讓 AI 寫了批次檔來做自動更新,原始碼已在 Github 開源供各位取用

 

Github 專案網址:https://github.com/Eden5Wu/Windows-Cangjie-Updater 


三大特色

  • 自動升權:因為會修改註冊檔,所以會要求使用者提供系統管理員權限 
  • 互動式選單:官方有四款需求導向目錄,為此寫了選單,預設為「台灣優先」
  • 自動備份 :腳本會自動備份舊碼表,改壞了就換回來即可


原本是寫 bat 批次檔,但倉三和倉五來源目錄皆以中文命名,剛好又是 bat 的罩門,為避免另外說明造成你我困擾,我決定改為 PowerShell (ps1) 來處理


視 bat 為本命的愛好者,也可以在上述 Github 中下載 bat 來使用,程式碼固定為「台灣優先」,有需要再自行修改

 

 

實現一鍵安裝的倉頡更新,感覺真是太好啦!

若是哪天又被天殺的微軟「更新」蓋掉,只要再對  .ps1 檔案按右鍵 ->「用 PowerShell 執行」來安裝,3 秒鐘就修復完成!

 

真是爽快!

 

常見問題

💡 Q:執行時一閃而過,或是出現「禁止執行指令碼」的紅字報錯?

A: 這是因為 Windows 預設基於安全考量鎖定了 PowerShell 腳本執行。請照以下步驟解鎖:

  1. 按下 Win + X 鍵,選擇 「終端機 (系統管理員)」。(就是Windows PowerShell)

  2. 貼上指令:Set-ExecutionPolicy RemoteSigned -Scope CurrentUser 並按 Enter。

  3. 輸入 Y 並按確認。

完成後再次對著腳本按「右鍵 -> 使用 PowerShell 執行」即可正常運作

 


 

2026/06/01

【踩坑】Delphi DBX 的底層幽靈:SQL Server 空字串如何引爆 Range Check Error?

作者:吳祐賓  

 

 


 

 

—— 可以理解為什麼 dbExpress 要被列 Deprecated,因為高階物件和低階抽象完全是兩個風景

 

在老系統現代化(Legacy Modernization)這條宛如「走出埃及」的路上,最恐怖的從來不是程式碼有多爛,而是在這條路上遇上問題時,無人能指引解惑。更恐怖的是,哪怕你選擇留在埃及,這些幽靈依舊存在

 

這次差點被搞到懷疑人生的經歷是:

 

專案從 DataSnap 轉向 Horse,資料透過 dbExpress (DBX) 底層與 Devart 驅動程式拉出來,準備轉成 JSON API

 

這本是走出埃及的日常,結果就在最簡單的一行判斷上,我被狠狠教做人了

 

if TDBXValue(Value).IsNull then  // ← 就在這裡直接噴 Range check error

 

而且只在特定資料庫、特定欄位上發生



黑夜降臨:案發現場的詭異症狀


問題欄位是老掉牙的 TEXT 型態,內容看起來明明是空字串
 

但只要碰到這欄位,所有常識判斷全部失效:

  • IsNull 直接爆炸
  • 改用 GetString、GetStream、GetBytes 也會收到 Devart 各種型別衝突的錯誤 (TDBXTypes.BLOB value type cannot be accessed as TDBXTypes.BYTES value type)

 

雖然這和埃及日常沒什麼區別,但那一刻我還真的有慌。這不是單純的 Null 判斷問題,而是某種更底層、更邪門的東西在作祟

 

因為,同樣的 SQL,使用 TSQLQuery / TSQLDataSet 時,完全沒有這問題啊! 

 

 

守衛睜眼 —— 查出真正的狼人

 

由於這和歷史經驗不同, 理所當然往資料庫挖

 

對出問題的資料表下了診斷 SQL:

 

SELECT 
    NotesField,
    DATALENGTH(NotesField) AS DataLen,
    TEXTPTR(NotesField) AS Ptr
FROM LegacyOrderTable
WHERE CategoryCode = 'BUG_TEST';

 

 

結果出來的那一刻,我整個人傻掉


這筆資料的狀態極度詭異:

  • DATALENGTH(NotesField) = 0
  • 但 TEXTPTR(NotesField) 居然是有效的 8 Bytes 指標(像 0xFBFFC67A... 這種)


 

原來,這根本不是什麼無辜的空資料,而是一頭「披著空字串外皮的狼」

 

這東西通常出現在很久以前的資料轉檔、批次匯入,或是老系統某些特殊的寫入行為

 

SQL Server 在行外(Out-of-row)幫它配置了 LOB 指標,但實際上什麼資料都沒塞進去。就像一具只剩下指標的空殼。 

 

 

預言家睜眼:不是追兵,是自己人崩潰 —— Devart 驅動程式的精神分裂

 

最要命的是 Devart 看到這頭「披著空字串外皮的狼」之後的處理方式

 

它偵測到有 TEXTPTR(長度 8),就很「聰明」地把這個欄位標記成 WideStringType,並且把 Size 設為 8

 

而 Delphi 這邊在準備讀取的時候,卻認為這是長度為 0 的空資料。於是在底層 DBXRow_GetBytes 裡,出現了類似這樣的致命操作:

 

PBuf := @Value[ValueOffset];  // Value 是長度 0 的陣列 → 直接 Range check error!

 

 

因為專案有開 {$R+}(範圍檢查),Delphi 非常乾脆地下殺手


這就是典型的「兩個黑盒子互相誤會,所引爆的災難現場」

 


女巫睜眼:三顆解藥


第一顆解藥:SQL 層釜底抽薪


乾淨利落,不讓 Devart Driver 看到那頭「披著空字串外皮的狼」

 

// 1. Alter Table
ALTER TABLE LegacyOrderTable
ALTER COLUMN NotesField NVARCHAR(MAX);

// 2. CAST
SELECT 
    CAST(NotesField AS NVARCHAR(MAX)) AS NotesField,
    CAST(AnotherTextField AS NVARCHAR(MAX)) AS AnotherTextField
FROM LegacyOrderTable

 

只要轉成 NVARCHAR(MAX),Devart 立刻恢復正常,不再亂認 Size = 8

 

這一招幾乎零成本,卻直接斬斷了問題根源 

 


第二顆解藥:程式碼硬控

 

動資料庫其實蠻傷及筋骨的,所以並不是一個好選擇

 

既然只是為了把資料轉成 JSON API,我們大可在轉換的迴圈裡加上防護罩,直接攔截這個怨靈:

 

try
  Result := Value.IsNull;
except
  Result := TJSONNull.Create;
end;

 

 

犧牲一點 except 效能與 Debug 的雜訊,至少 API 不會再半夜隨機暴斃

 

 

第三顆解藥:修改 Driver 原始碼 


很妙的是,Devart 不含原始碼的 DLL 版並沒有這方面的問題,我分析是 {+R} 在封裝 DLL 時被關掉了,同時也沒有吐出任何例外,這 Bug 就與世無爭地活了數十年


但不想依賴 DLL 過活的我,選擇的是含原始碼的版本

 

若你和我一樣有 Devart 的 Source Code 授權,而且真的很想把根治了,可以直接進到 DBXRow_GetBytes 之類的核心方法,加上防護: 

 

if (Value <> nil) and (System.Length(Value) > ValueOffset) then
  PBuf := @Value[ValueOffset]
else
  PBuf := nil;   // 神之一手,拯救無數 Range check error

 

這顆解藥很硬,但一旦改完,整個 Devart Driver 在面對 SQL Server 老 TEXT 時都會變得更穩

 

當然,這問題我已提交給 Devart,目前的版號是 9.5.0 版,未來可以關心之後新版的 History 有沒有相關的記錄出現 



白晝到來:傷害力不高,汙辱性極強


回到開頭說的:dbExpress 之所以被列為 Deprecated,不是沒有原因


更有趣的是,同樣使用 dbExpress,傳統的 TSQLQuery/TSQLDataSet 並未重現此問題;只有直接操作 TDBXReader/TDBXValue 時才踩中地雷。這表示高階 DataSet 與底層 DBX 抽象層在處理 LOB 欄位時,很可能走的是不同路徑。究竟是透過 Stream、額外緩衝層,還是其他容錯機制避開了這個缺陷,仍有待進一步驗證;但可以確定的是,當我直接面對 DBX 最底層抽象時,這頭狼人終於現出原形。 

 

所以:就算你不走出埃及,留在舒適圈裡,這些狼人依然會在正式環境半夜等著你


老系統的坑,從來不是一次就能填完的。它考驗的不是你會不會寫 Code,而是當所有常識都失效時,你願不願意把屍體挖出來解剖。

 

如果你也正在 DataSnap 轉 Horse、DBX 操作踩雷、或任何 Legacy Modernization 的路上,歡迎在留言區分享你的戰場故事

 

願我們一起在走出埃及的路上,相互扶持