顯示具有 10.1 Berlin 標籤的文章。 顯示所有文章
顯示具有 10.1 Berlin 標籤的文章。 顯示所有文章

2017年6月9日 星期五

使用 TRESTClient 與 TRESTRequest 作為 HTTP Client 之一 (POST 字串參數)

在 Delphi XE 推出以前的年代,Delphi的發展方向是筆直朝向資料庫連結Windows 應用程式這個目標不斷前進的,從Delphi 1開始,到Delphi 7,Delphi奠定了VB Killer的外號,主要依靠的就是與資料庫的連接功能超越其他開發工具,而且超越的距離不只一個世代。

在 .NET開始發展,Delphi 8, Delphi .NET 不斷延遲的時候,與資料庫連接功能的方便性,仍然讓許多ERP廠商、軟硬體廠商持續愛用 Delphi.

直到 Web 開發與 App 開發超越了 Windows 應用程式的需求,VC, VB, Delphi 也開始隨著這波潮流,漸漸不再像 1990年代那麼廣受愛戴了。

在1990到 2010年之間,Delphi的網路連線功能,主要是藉由第三方元件來提供的,其中知名度最高,全球使用人數也最多的,應該就是 Indy 這套元件了。

這套元件在 2000 年前,叫做 WinShose,從第八版之後才改名為Indy,全球投入這套元件開發的開發人員,前後超過40人,從最基礎的 TCP/IP 功能到各種協定的Client與Server 端元件,筆者從中得益非常多,也開發了當中的DNS Server元件,對通訊協定的深入了解,Indy團隊可說是我不可或缺的師長。

隨著Delphi 工具走入了多平台開發的領域,Indy的侷限性也在這兩三年凸顯了出來,主要是在各個作業系統上面對於SSL與加密功能的支援無法緊密結合到作業系統內建的功能所致。

由於這個局限性,Delphi XE6開始,REST Client系列元件漸漸開始成為 Delphi 團隊的重點開發項目之一,所以我們從 Delphi XE6, Delphi XE7 之後的版本,可以發現到,使用 TRESTClient, TRESTRequest, TRESTResponse 系列組合的應用程式越來越多了,原廠也不斷鼓勵大家使用這套元件來提供 REST API 的連線功能。

REST API 的基礎是 HTTP 協定,大多以 HTTP 的 POST 方法把 JSON 編碼形式的參數傳遞到 Server,而 Server 再以 JSON 形式的參數回傳。

有時作法也會稍有變化,例如以 POST 方法把 Web-Form 編碼形式的參數傳遞給 Server,Server 再以 JSON 形式把資料回傳。

形式不一而足,但相同的是 HTTP 協定,最常用的也是以 POST 方法把參數傳給 Server 端。

今天要跟大家分享的主題,則是如何『使用 TRESTClient 與 TRESTRequest 作為 HTTP Client』。

前面已經提到過,在沒有 TRESTClient 整組元件以前,我們通常用的是 Indy 系列的元件來提供網路傳輸的功能,而現在有了 TRESTClient 整組元件,我們在行動平台上面就可以不需要另外配置函式庫,也能夠直接使用 https 與 server 連線了,在勒索病毒氾濫的今天,使用 https 會讓使用者比較安心。

POST作業說明

在 HTTP 的 POST 作業當中,參數跟 GET 作業一樣,Client端需要以 name=value&name2=value2 這種形式進行字串連接,再傳送到 Server 端去。Get 跟 POST的差異,在於 Get 方法是把所有參數當做 URL 的一部分,發送 HTTP GET 指令的時候,參數連同 URL  一起傳送。

而 POST 作業則是發送完 POST 指令後,把所有的參數與資料隨之傳送。依照 HTTP 型定的規範,GET 作業的 URL 是無法加密的,而且長度也有限制。因此,當需要傳遞的資料比較多,或者有機敏性,透過 HTTPS 傳送,就是最直接,也最方便,更是目前最通用的資料保護方法。

透過 POST 傳遞的參數,除了字串以外,還常常包含了檔案傳遞。我們很常看到在網頁上面以按鈕提供使用者選擇要上傳的檔案,也常看見提供以拖拉的方式把檔案上傳到遠端系統,尤其網頁郵件系統最常見到這種作法。

過去以 TIdHTTP 元件的 POST 方法發送參數時,呼叫方式如下:
var
    httpClient : TIdHTTP;
    url, params, httpResultStr : string;
begin
    url := 'http://mytestURL.com/test.php';
    params := 'name=我的名字&test=測試';

    httpClient := TIdHTTP.Create(self);
    try
         httpResultStr := httpClient.Post(url, params);
         showMessage(httpResultStr);
    finally
        httpClient.Free;
    end;
end;
  
這樣就可以把 params 字串的眾參數傳到 server 去了。理論上是這樣沒錯,但事情並沒有這麼簡單,在 HTTP 協定當中要傳參數給 Server,如果這些字串包含了特殊字元,則必須要先經過編碼,而編碼,是我們一生都需要與之對抗的繁複程序。

在 HTTP GET 方法當中,所有的參數除了要以 name=value 對每一個參數做描述,以及需要用 & 來連接各組參數,所有的 value 都需要以 url encode 來擺脫 URL 保留字元的糾纏。name 是否需要編碼呢?筆者建議,name 就乖乖的用英文吧,可以省下很多問題,以及處理這些可避免的問題所需要的時間!

那麼同樣的功能,以 TRESTClient 跟 TRESTRequest 要怎麼達成呢? 也很容易,作法如下:
1. 在 form 裡面放上 TRESTClient 跟 TRESTRequest 元件各一。
2. 把要傳遞的參數加到 TRESTRequest 實體的 params 屬性裡面去,這個屬性的型別是 TArray,所以可以存放多組參數。
3. 設定 TRESTClient 要傳送參數的URL,注意,URL 是設定在TRESTClient 哦!
4. 設定 TRESTRequest 要使用的傳輸方法,要設定為POST(因為我們正在介紹的是POST方法,請按照您的需求調整)
5. 呼叫 TRESTRequest 實體的 execute 方法,就可以把資料送去 server 了。

寫成 Delphi 的程式碼,會像以下這樣:
self.RESTClient1.BaseURL :=
          'http://我的網址/acceptNewCard.php';
self.RESTRequest1.Params.Clear;
self.RESTRequest1.Method := rmPOST;

self.RESTRequest1.AddParameter('test', self.EditCardNo.Text);
self.RESTRequest1.AddParameter('name', self.EditName.Text);

是不是很容易呢?的確很容易,裡頭的問題我們等下再深入探討,先來看 server 端要怎麼接收這些個參數,我們用 PHP 當範例,需要用 C#,JSP的讀者朋友們請自行轉譯喔⋯⋯

PHP Server 端接收 POST 參數的方法
從 1994 年開始,筆者就陸續撰文說明 HTTP POST 方法如何接參數,包含了CGI 用C,perl等語言實作,也包含 ISAPI 以 Delphi 實作,近幾年比較流行的是 PHP,JSP,C#,但 PHP 程式碼讀起來比較簡潔易懂,所以我就選擇 PHP 來做範例了。

在 PHP 裡面,透過GET 跟 POST 方法傳遞的參數,會被分別存放在 $_GET 跟 $_POST 這兩個陣列變數裡面,如果要偷懶,不想區分 GET 或 POST 方法,也可以從 $_REQUEST 這個變數試著讀取,當中有些安全性考量,最好勤勞一點,把它們區分開來。

以剛剛的例子來看,我們傳了一個名為 name,以及一個名為 test 的字串,用的是 POST 方法,所以我們得用以下兩個變數來存取這兩個字串:
  • $_POST['name']  這個變數可以取得 Client 端發送出來的 name  
  • $_POST['test']  這個變數可以取得 Client 端發送出來的 test
所以在 server 端,我們可以這樣寫,來抓到這兩個資料:
$name = $_POST["name"];
$test = $_POST["test"];

這樣寫會不會出問題呢? 答案是不會!如果使用者不輸入中文的話!

中文資料的編碼處理 

Delphi的開發人員絕大多數都是英美語系的人,我推測因此對於亞洲語系的文字顯示與傳輸比較沒有辦法完整的測試,但對於我們以中文為母語的人來說,從電腦誕生的那個年代,中文的顯示在每個作業系統、每種通訊協定的設計都比英文協定來的困難。

以上面的例子來看,如果我們直接拿這個例子來測試,筆者寫的範例程式,執行傳輸資料時,Server 所抓到的文字並不是正確的中文字,如下圖所示:

可以看得出來,傳到 server 的時候,server 是讀不到資訊的。這是怎麼回事呢?筆者屬於不認輸的好奇寶寶,使出了渾身解數,終於解決了這個問題。

寫過 Web 程式的讀者們一定可以立刻推測出來,這絕對是文字編碼出問題了,然而,是什麼地方出問題?可能出問題的點我列出來跟大家分享:
  • HTTP Client 的 charset 設定錯了
  • HTTP Request 裡面的文字編碼出問題
檢查的方向也是從這兩個關鍵點出發,第一點的檢測很容易,從Object Inspector檢查一下 RESTRequest1的設定:

AcceptCharset 確定是 UTF-8,沒錯,所以設定不是問題。

接著,就要從 Client 端發出去的資料下手了。有讀者或許會問『你怎麼不懷疑Server端程式寫錯了?』這個問題很好,之所以排除了這個問題,是因為同一個 Server 端的 PHP 程式,我用了 Postman 做過比對測試,回傳的結果是正確的,因此判定是 Client 端程式的問題。

接著筆者從 TRESTRequest.AddParameter 的各種多載形式來嘗試,AddParameter 這個方法有以下幾種多載的形式:
  • procedure AddParameter(const AName, AValue: string); overload;
  • procedure AddParameter(const AName: string; AJsonObject: TJSONObject; AFreeJson: boolean = true); overload;
  • procedure AddParameter(const AName, AValue: string; const AKind: TRESTRequestParameterKind); overload;
三種形式我都測試過,從 AddParameter 的執行中 trace 進去看各個可能性,由於 TRESTRequest 的參數中,Get 跟 Post 的加入方法是混用的,在程式碼裡面編碼又會有點不同。

在 REST.Client.pas 裡面,我曾經懷疑過編碼錯誤,所以也在執行階段對各個變數都進行觀察,最後,找到了原因與解法,至於過程,就不多說了,花了我兩天咧。

原因:編碼錯誤

用HTTP傳遞中文的時候,務必用UTF-8編碼,但一定要記得,中文字在作業系統中,都是UCS32編碼,這個現象在Windows裡面如此,在Android裡面如此,在iOS跟Mac我不確定,但處理方法是一樣的。

直接以 AddParameter('name', '中文測試'); 把參數加進 TRESTRequest 的時候,REST.Client.pas 的程式碼是把 '中文測試' 這個字串直接抓 Ord 的資料來做編碼的,然而,這個作法,是錯的!!!!!!!!

在 HTTP 傳遞 UTF-8 資料的時候,我們要傳遞的是 UTF-8 文字的二進位資料,但直接把 '中文測試' 這個字串直接拿來轉成二進位? 當時編碼並不是 UTF-8 啊,當然怎麼編碼送到 server 都是錯的!!!!

解法:AddParameter之前先做 UTF-8 轉換

這個解法,筆者第一天就已經想到,只是很想像以前改 Indy 程式一樣,直接改好 REST.Client.pas 之後,回饋給原廠使用,所以花了不少時間找方法,最後發現這個方法不用動到 REST.Client.pas,又能正確處理,就直接這麼跟大家分享了,寫成 Delphi 程式碼如下:
var
     nameStr : String;
begin
   ...
   nameStr := TIdURI.ParamsEncode(nameStr, IndyTextEncoding_UTF8);
   self.RESTRequest1.AddParameter('name', nameStr,     
             TRESTRequestParameterKind.pkGETorPOST,    
             [TRESTRequestParameterOption.poDoNotEncode]);
   ...
end;
在把字串透過 AddParameter 加入參數陣列之前,我先把字串做個 UTF-8 轉換,在這裡用的是 TIdURI 的類別方法 ParamsEncode,這個方法只有兩個參數,第一個參數是字串內容,第二個參數則是要文字編碼的種類,在這裡我選擇了 UTF8,寫法就是上面範例程式的第一行。

接著,在呼叫 AddParameter 的時候,我使用了多載形式當中的第三種,要求 AddParameter 處理資料的時候不要再動我的編碼,因為我已經處理好了。

這麼修改過之後,在各個作業系統當中,執行結果都是正確的,上面兩個圖以 Windows 作業系統為例,現在我們拿 Android 截圖來做為例子:


讀者可以看到右邊的截圖裡面,server 回傳的資料已經是正確的中文字了。

最後,我把 PHP 程式碼也附上來給大家參考:
<?php
date_default_timezone_set("Asia/Taipei");
header('Content-Type: charset=utf-8');

include("public/DBClassPDO.php");
$objDBPDO = new DBClassPDO();

$cardNum = $_POST["cardNum"];
$floorIdx = $_POST["floorIdx"];
$name = $_POST["name"];
$houseNum = $_POST["houseNum"];

$params = array();
$params['name'] = $name;
$params['cardno'] = $cardNum;
$params['houseNum'] = $houseNum;
$params['floorIdx'] = $floorIdx;
$params['picFilename'] = $fileSaveName;
$params['created'] = 0;

$result["resultCode"] = "0";
$result["result"] = "成功";
$result["sqlcmd"] = $name;

$jsonStr = json_encode($result);
echo $jsonStr;
?>
這是最基本的傳遞字串,下次再給大家示範怎麼傳檔案,透過 TRESTRequest來做也是很簡單的,TRESTRequest 跟 TRESTClient 的確是取代 TIdHTTP 的好工具。

2017年5月22日 星期一

在 Android 的 Edit 中, 如何按下 Enter 即隱藏虛擬鍵盤

在 Windows 的應用程式中,我們常常為了讓使用者能夠快速輸入,在Edit元件中的onKeyUp或者 onKeyDown 事件中主動偵測使用者輸入的字元是否有換行符號 (Enter),當使用者按下了Enter,程式碼就主動把游標 Focus 帶到下個欄位,但在行動裝置中,又多了一個課題:『如果是多個欄位,就帶到下個欄位。但如果是單一欄位,或是最後一個欄位,就隱藏虛擬鍵盤』

這個課題筆者在 2014九月的筆記『Virtual Keyboard 的顯示與隱藏』裡面已經有介紹過一次,只是當時是聚焦在iOS系統上,因為當時筆者開發的平台大都聚焦在iOS,對Android系統的關切並不多。

最近在一些專案中,也把觸角伸到了Android平台,但筆者仍堅持『不到最後關頭,絕不輕言JAVA』的原則,認識筆者的朋友們也都知道,筆者會寫,但非不得已,絕不寫JAVA的原則。

加上Delphi從XE6之後,對Android的支援也很深入了,筆者就更能堅定這個立場。

言歸正傳,在TEdit元件中要在使用者按下虛擬鍵盤的Enter鍵時,隱藏虛擬鍵盤的話,要怎麼做呢?

顯然用2014九月那個作法(把focus轉移到別的元件), 對Android平台沒用,不然筆者也不用多寫這篇筆記了。

是的,熟習Android的使用者都知道,按下Android的實體Back鍵,就能隱藏虛擬鍵盤。所以這個作法也很簡單,就是『在onKeyUp事件中,把Key 改成 137 即可』

onKeyUp 事件中,有兩個 call by Reference 的參數,一個是 Key, 另一個是 KeyChar, 都讓我們可以在處理完鍵盤事件後,重新賦予新的值給系統。在Delphi Seattle, Berlin, Tokyo (前面的版本我已經移除了,無從驗證),這三個比較新的版本中,都定義有 vkHardwareBack這個值,其數值就是 137。

只需在 onKeyUp 事件中把 137 指派給 Key 這個變數即可隱藏鍵盤了,但通常我們程式碼可能會用於多種平台上,所以我的範例程式碼會加上 {$IFDEF Android} 這樣的判別式,以利多平台的程式維護:

procedure TFormMain.Edit_changeDeviceNameKeyUp(Sender: TObject; var Key: Word;
    var KeyChar: Char; Shift: TShiftState);
begin
   if Key = vkReturn then begin
      self.btn1.SetFocus;

{$IFDEF ANDROID}
      Key := vkHardwareBack
{$ENDIF}
   end;
end;
這樣應該很容易理解,對嗎?

2017年5月19日 星期五

在 iOS 10 之後, 使用手機相機與相簿應注意事項

iOS 的 SDK 每一年至少都會有一次大改版,從 2009 到 2016 年,版號已經到了第 10 版了,很輕易的就追上了 Mac OSX. 每一次的大改版都會有不少新的功能或新的規範,在 iOS 7的改版算的上是幅度最大的,把 iOS 1.0 到 iOS 6.0 以實物視覺與立體視覺的基礎來了個髮夾彎,轉向去跟 Android, Windows 一起走平面化極簡風。(不知道賈柏斯天上有靈會怎麼反應......)
  • iOS 8的改版中,要求使用地理資訊的App需要在 info.plist 當中自己寫出提示的文字
  • iOS 10 的改版中,更進一步要求使用相機、相簿的App也要自己提示。
如果沒有在 info.plist 裡面定義提示文字,App執行到該功能的時候,會直接發生閃退,這對於使用者來說很不方便,所以App開發的人員也只好為了使用者對 App 進行改版。

如果是使用 Xcode 來開發,就需要編輯 info.plist, 加入以下圖片中兩個灰色背景的設定,一個是使用相機,另一個是使用相簿。



這跟是否使用第三方的 Framework 無關喔,即使您使用了 Cocos2D, 或是 CocoaPad, 這些宣告也是跑不掉的。

如果使用的是 Delphi,則需要從專案設定選項 (Project Option)來設定,設定視窗如下圖所示:


叫出這個視窗的方法有兩個,一個是從Delphi的視窗中點選 Project 選項,選擇裡面的 Option。另一個則是用滑鼠右鍵點選視窗右邊的專案名稱,選擇 Menu 裡面的 Option 選項,都可以顯示出這個視窗。

設定好這兩個選項之後,在 Delphi 裡面呼叫 TTakePhotoFromCameraAction 或
TTakePhotoFromLibraryAction的時候,就不會閃退了.

要留意一點,透過 Delphi 取用相機或相簿的時候,OnDidFinishTaking 所取得的照片是 TBitMap 元件,我們可以先對圖片做一些簡單的處理,例如可以把圖片的 解析度弄小一點,或者改變圖片的大小,甚至是儲存成不同格式 (PNG或JPG)。

對 iOS 模擬器處理時,由於模擬器通常沒有相機,所以不用特別設定模擬器的相機使用描述。Delphi 的設定會依照不同的裝置平台做個別的設定,而且還分成 Debug/Release,所以我自己通常會直接編輯 All Configuration。

從 iOS 5S 之後,也已經沒有 32 bit 的新裝置了,所以如果您的 App 不打算提供給 32 bit 裝置使用的話,iOS Device - 32 bit platform 也可以不用理他了。



2017年5月12日 星期五

Delphi 快速使用 AES 加密的方法

從 1994 年開始,筆者就開始接觸加密與網路安全的世界,從魯立忠老師的指導當中獲益良多,後來在元智就讀研究所的時候,也以此為研究主題。

在當時,電子商務是顯學,Visa跟 Master Card還特別為了網路交易製作了厚厚三大本的商務通訊協定,命名為SET (Secure Electronic Transaction,安全電子交易),從客戶端、商店端、銀行端定義了綿綿密密的交易規範。

然而,網際網路的世界跟 Visa Master Card所熟悉的專用網路世界差的遠了,不是大狗們(Big dogs)說了算,很快的 SSL 128 被吹捧成『最安全的交易保護機制』,每年透過這『最安全的交易保護機制』成交的金額越攀越高。

破解網路而得逞的網路詐欺,始終維持在一個很低的比例,反而從商家端流出的詐欺資料年年創新高,SET也很快的成為一個歷史名詞。

但是,SET所本的一些加密基礎,並沒有就此被埋沒。X.509電子憑證、RC4, RC5, DES, 3-DES, RSA, SHA-1, SHA256, SHA-2, 還有我們這次要介紹的AES,也不斷的推陳出新,在世界上蓬勃發展。這些聽了令人打呵欠的主題跟名詞,在很多地方都會被用上,只是用了不同的面貌呈現給使用者而已。

像是在自然人憑證、健保卡裡面,都有個人電子憑證(X.509),每年五月我們都可以用這兩種憑證進行網路報稅。

或是像電子發票,當中就需要用到 AES 加密,依據財政部的『紙本電子發票二維條碼內容規範』第五頁所述:


左方二維條碼裡面,就需要用到 AES 對發票字軌10碼及隨機碼4碼以字串方式合併後使用AES加密,並採用Base64編碼轉換。

但是,在Delphi裡面好像沒有可以直接使用AES加密的單元可以使用。筆者在碩士論文的程式撰寫時,使用的是OpenSSL 0.4的函式庫,當時還叫做SSLeay呢。但是,這作法只能在Windows 平台上面順順的用,有沒有什麼方法可以讓我們在不同的作業系統下都能順利使用 AES 呢?

經過約莫兩三個小時上窮碧落下黃泉的搜尋,找到了一個在 SourceForge 上面的加密範例程式,更棒的是,它是用 Delphi + FireMonkey 寫的,不使用 DLL,而是使用純粹的Pascal 寫的 (感謝 Eldos 的 OpenSource, 但直接到 Eldos 網站的連結目前已經找不到了)。

換句話說,這是一個跨平台都可以正常運作的 Delphi 程式,不用依靠載入的 DLL 或 Lib,用這個範例來製作電子發票的驗證加密字串,就能夠很方便的達成了。

Source Forge 的範例程式可以從這個連結下載,下載之後,請看到裡面的範例專案『FlyUtilsAESCBC.dproj』,這個範例程式中,支援用字串作為AES加密金鑰(Key)對文字進行加解密,執行起來的畫面也很清楚,筆者做了一點點修改,修改後的執行畫面如下圖所示:
 

左圖是未執行加密作業前的畫面,右圖則是執行了加密作業之後的畫面。原始的範例程式中,只支援完整的字串作為 AES 金鑰,但我們常會用到二進位資訊來做金鑰,這種情形下,金鑰通常也會經過Base64編碼過。

所以筆者稍微改寫了一個function,新增了一個按鈕,就是畫面最底下的『AES加密with Byte Key』這個按鈕的event handler。

如果點選按鈕是 AES加密,則Key裡面的字串不會被做任何處理,直接會被當成AES金鑰,點選的如果是最底下的『AES加密with Byte Key』,則Key裡面的字串會先被做Base64 解碼,變成二進位資訊,AES金鑰就是這些二進位資訊了。

procedure TFormMain.Button1Click(Sender: TObject);
var
  KeyBit: TKeyBit;
  APaddingMode: TPaddingMode;
begin
   KeyBit := TKeyBit.kb256;
   APaddingMode := TPaddingMode.pmZeroPadding;
   Memo2.text := AESEncryptStrToBase64_Base64Key(Memo1.Text, Edit1.text, TEncoding.UTF8, KeyBit, '', APaddingMode, CheckBoxCBC.IsChecked,
      rlCRLF, rlCRLF, Process);
end;

這兒的 AESEncryptStrToBase64_Base64Key 是筆者照著原本 Eldos 的程式做了一點小手腳,方便大家把電子發票平台取得的金鑰直接貼上來就能用:
function AESEncryptStrToBase64_Base64Key(Value, Key: string; StrEncoding: TEncoding = nil;
  KeyBit: TKeyBit = kb128;
  InitVectorStr: string = '';
  APaddingMode: TPaddingMode = TPaddingMode.pmPKCS5or7RandomPadding; CBCMode: Boolean = True;
  ValueCRLFMode: TCRLFMode = rlCRLF;
  KeyCRLFMode: TCRLFMode = rlCRLF;
  OnProcessProc: TOnProcessProc = nil; ProcessProc: TProcessProc = nil): string;
var
   tStrm : TBytesStream;
   keyBytes: TBytes;
   IVBytes: TBytes;
   IdDecoderMIME1 : TIdDecoderMIME;
begin
   tStrm := TBytesStream.create;
   IdDecoderMIME1 := TIdDecoderMIME.Create(nil);
   try
      IdDecoderMIME1.DecodeBegin(tStrm);
      IdDecoderMIME1.Decode(Key);
      IdDecoderMIME1.DecodeEnd;

      keyBytes := tStrm.Bytes;
   finally
       tStrm.Free;
   end;

   tStrm := TBytesStream.create;
   try
      IdDecoderMIME1.DecodeBegin(tStrm);
      IdDecoderMIME1.Decode(InitVectorStr);
      IdDecoderMIME1.DecodeEnd;

      IVBytes := tStrm.Bytes;
   finally
       tStrm.Free;
   end;
   IdDecoderMIME1.Free;

  Result := EncodeBase64Bytes(AESEncryptStr_BytesKey(Value, keyBytes, IVBytes, TEncoding.UTF8, KeyBit, APaddingMode, CBCMode, ValueCRLFMode,
    KeyCRLFMode, OnProcessProc, ProcessProc));
end;

單獨使用這個 function 的話,ProcessProc 參數可以給 nil, 這是用來讓大家看到有進度列可以顯示處理進度用的,通常這些處理是在背景進行,沒有介面的時候直接給個 nil, 就可以不用管進度條了。

大家可以看到,上面的程式碼裡面,筆者使用了Indy的 Base64解碼元件『IdDecoderMIME』,因為一來它是原本 Delphi 安裝就內建的,使用上比較方便,二來筆者也熟悉這套元件,所以不另外找其他元件了。

AES 裡面除了 Key 之外,還可以指定 IV (initialization vector), 如果使用上需要使用二進位的 IV, 您可以把二進位的 IV 先做好 Base64 編碼,這個 function 也會將它先做 Base64 解碼之後,作為 IV 進行處理的。

寫到這裡,說明的差不多了,範例程式專案我也準備好了,有興趣的讀者請自行取用吧。範例在此。