Peter's Blog
2026年9月21日 星期一
2026年9月17日 星期四
ListView
//取CheckBox寬度
function GetListViewCheckboxWidth: Integer;
begin
// SM_CXMENUCHECK 傳回選單與清單控制項中 Checkbox 的寬度(像素)
Result := GetSystemMetrics(SM_CXMENUCHECK);
end;
2026年7月28日 星期二
No FTP list parsers have been registered
FTPListItems := idFTP.DirectoryListing,出現錯誤訊息
No FTP list parsers have been registered
只需要在程式的 uses 區段中加入 IdAllFTPListParsers 即可
2026年6月30日 星期二
Delphi 函式庫發佈方式(提供 Interface PAS + DCU)
Delphi 函式庫發佈方式(提供 Interface PAS + DCU)
一、目的
當 Delphi 開發完成一個 Unit,希望提供給其他開發者使用,但又不希望公開原始程式碼時,可以採用:
- 公開 Interface PAS
- 提供編譯完成的 DCU
使用者可以正常 uses 該 Unit,也能使用 IDE 的型別提示及程式碼完成(Code Insight),但無法看到真正的程式實作。
二、發佈內容
假設原始 Unit 名稱為:
MyLib.pas
發佈內容如下:
Release
│
├── MyLib.pas ← 只有 Interface 宣告
└── MyLib.dcu ← 編譯完成的程式
不提供完整原始碼。
三、原始 Unit
例如:
unit MyLib;
interface
uses
System.SysUtils;
type
TMyClass = class
public
constructor Create;
destructor Destroy; override;
function Add(A, B: Integer): Integer;
function Sub(A, B: Integer): Integer;
end;
function GetVersion: string;
implementation
constructor TMyClass.Create;
begin
inherited;
end;
destructor TMyClass.Destroy;
begin
inherited;
end;
function TMyClass.Add(A, B: Integer): Integer;
begin
Result := A + B;
end;
function TMyClass.Sub(A, B: Integer): Integer;
begin
Result := A - B;
end;
function GetVersion: string;
begin
Result := '1.0';
end;
end.
四、編譯產生 DCU
使用 Delphi 編譯後,會產生:
MyLib.dcu
真正執行的程式都在 DCU 內。
五、建立公開 PAS
建立另一份供發布使用的 MyLib.pas。
注意:不要修改原始碼,而是另外建立一份。
例如:
Project
│
├── Source
│ MyLib.pas ← 原始完整程式
│
└── Release
MyLib.pas ← 公開介面
MyLib.dcu
公開 PAS 保留 Interface:
unit MyLib;
interface
uses
System.SysUtils;
type
TMyClass = class
public
constructor Create;
destructor Destroy; override;
function Add(A, B: Integer): Integer;
function Sub(A, B: Integer): Integer;
end;
function GetVersion: string;
implementation
end.
可以看到:
- Class
- Function
- Procedure
- Property
- Event
- Record
- Enum
都需要保留。
但是:
所有 Implementation 內的程式碼全部刪除。
六、使用者如何使用
使用者只要:
uses
MyLib;
即可正常呼叫:
var
M: TMyClass;
begin
M := TMyClass.Create;
try
ShowMessage(IntToStr(M.Add(3,5)));
finally
M.Free;
end;
end;
不需要任何特殊設定。
七、IDE 功能
由於 Interface 仍存在,因此 Delphi IDE 可以提供:
- Code Insight
- Auto Complete
- Parameter Hint
- 型別檢查
- 編譯檢查
使用體驗與一般 Unit 幾乎相同。
八、可以隱藏哪些內容
可以隱藏:
- 所有演算法
- 所有商業邏輯
- SQL
- 加解密流程
- API 呼叫方式
- 所有 Function 實作
- 所有 Method 實作
仍然會看到:
- Class 名稱
- Function 名稱
- Procedure 名稱
- Property 名稱
- Record 定義
- Enum 定義
- Event 定義
如果 Interface 中宣告了 Private 欄位:
private
FData: Integer;
使用者仍然可以看到:
FData
只是無法知道如何使用。
因此,如果希望降低資訊曝光,建議不要在 Interface 中放置過多內部欄位或實作細節。
九、優點
- 不公開原始碼。
- 使用方式與一般 Unit 完全相同。
- IDE 可正常提供 Code Insight。
- 不需要 DLL。
- 執行速度與一般 Delphi 程式相同。
- 發布方便。
十、缺點
1. 無法跨 Delphi 版本
DCU 為 Delphi 編譯器產生的中間檔。
不同 Delphi 版本的 DCU 格式可能不同,因此:
- 無法保證相容
- 通常不可共用
例如:
| 編譯版本 | 使用版本 | 是否可用 |
|---|---|---|
| XE10 | XE10 | ✔ |
| XE10 | XE8 | ✘ |
| XE10 | Delphi 10.4 | ✘ |
| XE10 | Delphi 11 | ✘ |
| XE10 | Delphi 12 | ✘ |
因此,每個 Delphi 版本都需要重新編譯對應的 DCU。
2. 每個版本都需要重新發布
若要支援:
- XE10
- 10.4 Sydney
- 11 Alexandria
- 12 Athens
通常需要:
Release
│
├── XE10
│ MyLib.pas
│ MyLib.dcu
│
├── 10.4
│ MyLib.pas
│ MyLib.dcu
│
├── 11
│ MyLib.pas
│ MyLib.dcu
│
└── 12
MyLib.pas
MyLib.dcu
3. 若公開介面有修改
例如新增:
function Test: Integer;
就需要重新:
- 編譯 DCU。
- 更新公開 PAS。
- 一起發布。
兩者必須保持一致。
十一、適用情況
適合:
- 公司內部函式庫。
- 不希望公開原始碼。
- Delphi 開發團隊使用相同版本。
- 商業 Delphi 函式庫。
- 元件開發。
十二、不適合情況
若需要:
- 支援 Delphi 多個版本
- 支援 C++
- 支援 C#
- 支援 VB
- 支援其他語言
則建議使用:
- DLL
- COM
- Web API
- REST API
而不是 DCU。
十三、建議
若所有使用者皆使用相同 Delphi 版本,採用 Interface PAS + DCU 是一種簡單且成熟的封裝方式,能兼顧開發便利性與原始碼保護。
若需支援不同 Delphi 版本,則必須針對每個版本重新編譯並發布對應的 .dcu;單一 DCU 無法跨 Delphi 版本使用。如果目標是跨 Delphi 版本,建議直接提供相容的原始碼,或改以 DLL、COM、REST API 等方式封裝功能,以降低版本相依性。
2026年5月21日 星期四
Delphi Enum 轉字串
System.TypInfo;
type
TDataState = (stInquiry, stNew, stEdit, stDelete, stRecall);
function DataStateToString(AState: TDataState): string;
begin
Result := GetEnumName(TypeInfo(TDataState), Ord(AState));
end;
2026年3月23日 星期一
Delphi FireDAC
FDManager
var sParams:TStringList;sParams.Add('Server=xx.xx.xx.xx');sParams.Add('User_Name=xxxxx');sParams.Add('Password=xxxxx');sParams.Add('Database=xxxxx');sParams.Add('DriverID=MSSQL');sParams.Add('Pooled=True');
FDManager1.AddConnectionDef('MSSQL_Pool', 'MSSQL', sParams);
FDConnection (與FDManager搭配使用,可以理解是DB Session)
FDConnection1.ConnectionDefName := 'MSSQL_Pool';FDConnection1.Connected := True;
FDQuery / FDUpdateSQL
with FDQuery1 dobeginConnection := FDConnection1;CachedUpdates := True;UpdateOptions.UpdateTableName := 'Table1';UpdateOptions.KeyFields := 'Field1,Field2';
UpdateOptions.UpdateMode := upWhereKeyOnly;
UpdateObject := Self.FDUpdateSQL1;
Close;SQL.Text :=' select a.*, b.Fiedl5 '+' from Table1 a '+' left join Table2 b on b.field1=a.field1 '+' where ... ';
Open;end;
ApplyUpdates
FDQuery1.CheckBrowseMode;FDQuery1.FetchNext; //抓資料到本地快取, 確保快取內容有提供回寫的資料FDQuery1.ApplyUpdates(-1);
var column:TFDDatSColumn;column := FDQuery1.GetFieldColumn(Field);column.ActualOriginTabName // Field歸屬的Tablecolumn.ActualOriginColName // Field實體表格中的Column Name
FDConnection -> FDQuery,當FDQuery資料是分批讀取還沒完全將資料載入時,FDConnection沒辦法被其他FDQuery操作使用,會出現以下的錯誤訊息。
FDQuery.SourceEOF; //可以知道資料是否已完全載入
如果沒有閒置的 FDConnection, 就要Create一個FDConnection提供給FDQuery使用。
2026年3月19日 星期四
美元 為何又稱美金
「美元」之所以在華語世界常被稱為「美金」,主要與歷史上的貨幣制度以及黃金的關聯性有關。這可以追溯到 19 世紀到 20 世紀中葉的全球金融背景:
1. 金本位制度(Gold Standard)
在 19 世紀後半葉至 20 世紀初,世界主要經濟體實行「金本位制」。當時的美元與黃金是直接掛鉤的,面額可以隨時向銀行兌換成等值的實體黃金。
實質等同: 由於美元在當時代表著特定重量的黃金,民眾習慣將這種具有黃金價值的貨幣稱為「美金」。
2. 布列頓森林體系(Bretton Woods System)
二戰結束後(1944 年),全球建立了布列頓森林體系。該體系確立了美元與黃金掛鉤(每 35 美元兌換 1 盎司黃金),而其他國家的貨幣再與美元掛鉤。
國際儲備: 美元成為全球最主要的儲備貨幣,地位如同黃金般穩固。這進一步加深了「美元即黃金」的社會認知,「美金」這個稱呼也因此在兩岸三地及海外華人圈廣為流傳。
3. 文化與習慣
雖然 1971 年尼克森衝擊(Nixon Shock)宣告美元正式脫離金本位,美元不再能兌換黃金,但「美金」這個詞彙已經深植於語言習慣中:
辨識度: 在華語口語中,「美金」聽起來比「美元」更具備財富的象徵意義。
區分需求: 在早期外匯管制較嚴格的時期,民間習慣用「美金」來區分本地貨幣與這種強勢的國際貨幣。
補充小知識:
儘管現在美元本質上是「法定貨幣」(Fiat Money)而非金幣,但這種稱呼已成為一種文化印記。在正式的金融公文或新聞報導中,通常會使用「美元」;而在日常生活中,「美金」依然是極為普遍的用法。
Gemini
2026年2月25日 星期三
修正 Quick Report 預覽/列印的顯示比例
當調整了電腦螢幕的縮放比例後,操作程式裡的報表QuickReport,發現報表預覽的資料內容沒有隨著顯示縮放比跟著做調整,但不影響實際列印輸出的結果。
參考網上的作法,修正 QuickReport 需 QRPrntr.pas 排除縮放比的問題。
我採用網友提供的方法1來處理。
File Name : QRPrntr.pas
Procedure Name : CreateMetafileCanvas
QRPrntr.pas 修正後,重新編譯QR506RunDXE10.bpl,
預覽結果就會以符合系統縮放比做調整了。
【參考連結】
老森常譚 IT Help 《Delphi》修正 Quick Report 預覽列印的比例問題
2026年2月3日 星期二
2025年12月25日 星期四
QuickReport - 載入 Qrp 報表文件時,會預先使用預設印表機的紙張格式套用在文件上,與設計的報表格式不符...
iPageHeightPixel, iPageWidthPixel:Integer;
Meta: TMetafile;
begin
inherited;
repReport := TQuickRep.Create(nil);
repReport.PrevInitialZoom := qrZoomToWidth; //頁寬
repReport.PrevShowThumbs := False; //不顯示簡視欄
repReport.PrevShowSearch := False; //不顯示搜尋欄
repReport.PreviewInitialState := wsMaximized; //最大化
repReport.ShowProgress := True;
repReport.PreviewDefaultSaveType := stQRP;
repReport.Prepare;
repReport.QRPrinter.Load('Report.qrp'); //載入檔案
iPageWidthPixel := Meta.Width; //取得文件記錄中的尺寸 Pixel
iPageHeightPixel := Meta.Height; //取得文件記錄中的尺寸 Pixel
repReport.Page.Orientation := repReport.QRPrinter.Orientation; //報表直/橫向
repReport.Page.PaperSize := TQRPaperSize.Custom; //報表紙張格式
repReport.Units := TQRUnit.Pixels;
repReport.Page.Length := iPageHeightPixel; //設定報表長度
repReport.Page.Width := iPageWidthPixel; //設定報表寬度
repReport.PrinterSettings.PaperSize := TQRPaperSize.Custom; //報表紙張格式
repReport.PrinterSettings.ApplySettings(repReport.QRPrinter);
repReport.QRPrinter.aPrinterSettings.ApplySettings;
repReport.QRPrinter.PreviewModal; //預覽文件
end;
2025年12月18日 星期四
透過詢問專業建議,更能具體掌握需要改進細節
#CharGPT
在參與考試或競賽後,建立回饋機制對個人成長至關重要。不論最終的成績表現如何,主動向評分委員請益都是提升自我的關鍵步驟。透過詢問專業建議,參賽者能具體掌握需要改進的空間或加強的細節。這種積極的學習態度有助於發現盲點,進而在未來的挑戰中展現更優異的實力。總結而言,將賽後的檢討視為持續進步的契機,是追求卓越不可或缺的過程。
2025年12月8日 星期一
2025年6月17日 星期二
2025年6月11日 星期三
QuickReport 調整 QRShape 配合 DetailBand 展延
begin
DetailBand1.ExpandedHeight(h);
QRShape1.Size.Height := h - QRShape1.Size.Top - 1;
end;













