2013/12/26

Convert Excel column to integer funtion with Delphi

See also: How to convert a column number (eg. 127) into an excel column (eg. AA)


Delphi code below:
function ColumnNumber(A_ColAddress: string): Integer;
var
  _Digits: array of Integer;
  _Pos: Integer;
  _Mul, _Res: Integer;
begin
  SetLength(_Digits, Length(A_ColAddress));
  for _Pos := 0 to Length(A_ColAddress)-1 do
  begin
    _Digits[_Pos] := Ord(A_ColAddress[_Pos+1]) - 64;
  end;

  _Mul := 1;
  _Res := 0;
  for _Pos := Length(A_ColAddress)-1 downto 0 do
  begin
    _Res := _Res + (_Digits[_Pos] * _Mul);
    _Mul := _Mul * 26;
  end;

  Result := _Res;
end;

2013/12/01

使用 DataSnap 開發,是否要選擇 HTTPS?

最近和我們家的網管討論 WebService 應該用 TCP/IP 還是 HTTP。

但意外的是,我們家網管推薦使用 HTTPS 開發

但我想他應該沒想到後續的域名、金鑰賞味期限製作有多麻煩

不過更重要的是數位簽章的費用啊啊啊 >"< |||

2013/11/18

分享基於 SOAP 的 3-Tier 開發範例 及 觀後心得

某個日本公司寫的一個關於 SOAP 的分散式開發專案範例
網址:3層スタイルC/Sアプリ作成術サンプルソース

目前看了下範例後有一些簡單的想法。

  • 充分利用 ClientDataSet 的 XML 特性,與 SOAP 完美搭配。
  • 一樣是 C/S ,更重要的是已經 3-Tier 化了。傳統 2-Tier 下資料庫裸露在外的問題已不復見了。
  • 開發上也不需考慮 DCOM +Socket / HttpSrvr 等搭配技術的實現,架構上顯得是相對乾淨,以及在Delphi 3 - XE n 都能夠被實現的技術。
  • 標準的無狀態 (Stateless) 的 3 層架構。(不過 SOAP 是允許有狀態 (Stateful) 實現的)
  • SOAP 是非常成熟的 Web Service ,也就表示它的問題和 DataSnap 相比下顯得較無缺失些。
  • 多種連線方式成為可能,只要重載函式 (overload) ,就能夠讓後端使用多種 DataSet ,不論是 ADO, DBX 還是未來的 FireDAC 都能夠銜接這個框架。
  • SOAP 的效率實在是很差,連接的成本太高,還沒開始傳封包呢!
  • 按照範例中的 Readme.txt 解釋,在多人連線上似乎會有衝突,還有調整的必要性,畢竟只是 DEMO 。可能還有些關鍵被隱藏起來了吧。
  •  在不考慮 Socket / HttpSrvr 的問題 (?) 下,DataSnap 開發無狀態的應用程式還是相對簡單。
不過好像沒有文獻顯示SOAP Service可以承載多少 Client 同時在線。這部份可能完全取決在 HTTP Service 上?

2013/11/04

Day11 RAD Studio XE5 iOS / Android Message Alert 的三種方法

按照 Embarcadero 公司所提供的 Demo 中,有一個 MessageAlerts 的範例。

很好奇的打開來看,居然是以前在 Win32 中的:
1. ShowMessage
2. MesssageDlg


和以前 VCL framework 的呼叫方式如出一轍

這樣學習壓力就降低很多了啊!


緊接著還有自製的訊息視窗:
3. RAD Studio XE5 -- Creating a custom MessageBox

於是便如法泡製的接在原來的 Demo 後面。






 實際呈現效果還蠻炫的!有機會可以多多嚐試看看。  ^____^

2013/11/03

Day10 RAD Studio XE5 的 DataSnap

今天原本是想寫個 DataSnap 的 Demo
可是無意間找到了這個網站:
iOS Apps 分散式系統建置興導入

於是今日一整天就在這個網站研習囉~~~ ^_^

2013/11/02

Day9 RAD Studio XE5 FireMonkey 兩種 Layout 的方法 -- Grid 和 Flow

Grid 和 Flow 兩個都是擺放多個物件對齊的好工具,這兩個容器最大的不同點在於:

Grid: 可擺放多個相同大小的物件容器。
Flow: 擺放多個,但寬度不一定相同的物件容器。

原廠的教學在此:
Tutorial: Using Grid and Flow Layouts in FireMonkey FM2

實際成果圖如下:



2013/11/01

Day8 RAD Studio XE5 為 App 加入手勢功能

承襲VCL的手勢功能
讓 TabControl 和 GestureManager 及 TAction 綁定,再決定要使用什麼樣的手勢。

實作上意外的很簡單的手勢功能。 ^___^

2013/10/31

Day7 使用 LiteDAC 仿造一個 IBLite 的 Demo

在 EMBT 的 Youtube 中有一段 Demo 是在描述 dbExpress with IBLite 的範例

看起來很是麻煩,畢竟是單向資料集,需要用很多元件才能達成存取的目的。

使用 LiteDAC 時,就像在 VCL framework 的 2-Tier 下一樣簡單,底下是範例 Code
procedure TForm1.AddButtonClick(Sender: TObject);
var
  DeptName: String;
  NewDeptNo: Integer;
begin
  try
    if InputQuery('Enter New Dept Name', 'DNAME', DeptName) and not (DeptName.Trim() = '') then
    begin
      LiteQuery1.Last();
      NewDeptNo := LiteQuery1.FieldByName('DEPTNO').AsInteger + 10;
      LiteQuery1.Append();
      LiteQuery1.FieldByName('DNAME').Text := DeptName;
      LiteQuery1.Post();
      LinkFillControlToField1.BindList.FillList();
    end;
  except
    on e: Exception do
    begin
      ShowMessage(e.Message);
    end;
  end;
end;
///
procedure TForm1.DeleteButtonClick(Sender: TObject);
begin
  try
    if LiteQuery1.Locate('DNAME', ListView1.Selected.Text, []) then
      LiteQuery1.Delete();
    LinkFillControlToField1.BindList.FillList();
  except
    on E: Exception do
      ShowMessage(E.Message);
  end;
end;



事實證明,LiteDAC 在發佈到行動裝置時完全不需要再帶任何的 Libraries,對於行動裝置的開發實在是太方便啦!

2013/10/30

Day6 FireMonkey 的幾個樣版

在新建立的專案中,Blank Application 是最常用的樣版,但 Header / Footer 和 Header / Footer with Navigation 實際上到底能不能用 Blank Application 做出來?

看起來樣版間似乎互不相通
直接看看 Header / Footer 樣版的結構吧。
H / F 樣版可是意外的簡潔
非常直白的三個元件 - Footer(TToolbar) + Header(TToolbar) + Label(TLabel)
如此就能構成一個帶有頭和尾的專案,就算用 Blank Application 先建立也不過是拉三個元件的時間。


再來看看 Header / Footer with Navigation 樣版的結構。
元件很多,但不難理解
和原先的H / F很接近,主要是多了 TTabControl 這個頁面管理元件
和 VCL 裡的 TPageControl 很接近。

嗯,所以可以參考樣版來設計屬於自己的 Blank 專案。一面加強對 FireMonkey 元件的熟悉度。

2013/10/29

Day5 LiteDAC - iOS開發的利器

當窮得只剩 PC 的時候...那就戰到不能戰為止吧!

在 iOS 應用程式部屬到設備上時有個很嚴重的限制:
就是在發佈應用程式時不能附帶任何的 libraries (*.dylib) 。

難道在資料庫的存取上就只能走 DataSnap 嗎?

當然不是,我們還可以使用 iOS 內部支援的 SQLite 當作本機資料庫使用,現在就來介紹新的工具吧 -- LiteDAC!

Devart 的 DAC 元件群(ODAC、MyDAC、PgDAC、IBDAC、UniDAC 和 LiteDAC)有個最大的特色,就是它不需要任何的 libraries (*.dylib),就可以直接存取資料庫。

今天就初步來介紹本次的主角 -- LiteDAC!

使用上非常的輕鬆,底下是原始碼:


procedure TForm1.FormCreate(Sender: TObject);
var
  _Count: Integer;
  _Temp: TLiteQuery;
begin
  _Temp := TLiteQuery.Create(Self);
  _Temp.Connection := LiteConnection1;

  LiteConnection1.Options.ForceCreateDatabase := True;
  LiteConnection1.Database := IncludeTrailingPathDelimiter(TPath.GetDocumentsPath) + 'emps.sqlite';
  LiteConnection1.Connect();

  _Temp.SQL.Text := 'SELECT count(*) FROM sqlite_master WHERE type=''table'' AND name=''DEPT'' ';
  _Temp.Open();
  _Count := _Temp.Fields[0].AsInteger;
  _Temp.Close();
  if _Count = 0 then
  begin
    _Temp.SQL.Text := ''
      +'CREATE TABLE DEPT '
      +'( '
      +'   DEPTNO INTEGER NOT NULL, '
      +'   DNAME  VARCHAR(14), '
      +'   LOC    VARCHAR(13), '
      +'   PRIMARY KEY (DEPTNO) '
      +') ';
    _Temp.ExecSQL();

    _Temp.SQL.Text := ''
      +'INSERT INTO DEPT '
      +'VALUES      (10, '
      +'             ''ACCOUNTING'', '
      +'             ''NEW YORK''); '
      +' ';
    _Temp.ExecSQL();

    _Temp.SQL.Text := ''
      +'INSERT INTO DEPT '
      +'VALUES      (20, '
      +'             ''RESEARCH'', '
      +'             ''DALLAS''); ';
    _Temp.ExecSQL();

    _Temp.SQL.Text := ''
      +'INSERT INTO DEPT '
      +'VALUES      (30, '
      +'             ''SALES'', '
      +'             ''CHICAGO''); ';
    _Temp.ExecSQL();

    _Temp.SQL.Text := ''
      +'INSERT INTO DEPT '
      +'VALUES      (40, '
      +'             ''OPERATIONS'', '
      +'             ''BOSTON''); ';
    _Temp.ExecSQL();
  end;

  LiteQuery1.Open();
end;

很快速的做出了新的成品

  LiteConnection1.Options.ForceCreateDatabase := True;
Options 的參數 ForceCreateDatabase 是在指定 LiteDAC 要檢查 iOS 上是否已有指定的資料庫存在。
  LiteConnection1.Database := IncludeTrailingPathDelimiter(TPath.GetDocumentsPath) + 'emps.sqlite';
IncludeTrailingPathDelimiter(TPath.GetDocumentsPath) 是直接取得 App 在 iOS 裝置裡的路徑。

附帶說明:LiteDAC 在 iOS 上會智能載入系統自帶的 SQLite 驅動,所以在 LiteConnection.Options.Direct 一定要為 False 。


LiteDAC 官網在這兒

2013/10/28

Day4 RAD Studio XE5 第一支快快樂樂學寫手機程式誕生

非常快速的寫了一支Demo程式
真的很快啊!

可是在Android上卻發生沒有回應的問題
嗯……這該怎麼辦呢?


Day3 RAD Studio XE5 的 C++ ... 還是先從 Delphi 開始練習好了

不知道為什麼 C++ Builder 老是出怪手,不過我也沒時間 Debug 它了
等它的正式版推出了再回過頭來測試吧。

再來看看 Delphi 的實作狀況:

果然是順多了,沒有怪怪的異常訊息,和ClientDataSet也能夠正確 Binding 。

明日再來試著 ApplyUpdate 的實作

2013/10/26

Day2 RAD Studio XE5 資料庫連線測試

因為測試時間不多
所以想快一點連線到資料庫

但…dbExpress怎麼突然都連不到資料庫了?

所以來到了 Devart 網站尋求支援了


搭配了 Firebird 2.5
底下 Devart dbExpress for Firebird 的連結畫面


好不容易裝好,卻在Compile時出現以下的錯誤

奇怪…照著手冊上的教學卻出錯了,不知道是哪裡的問題

總之,先這樣吧!

Day1 RAD Studio XE5 前置準備


首先就是準備下載 RAD Studio XE 5 ,準備進行 30 天的試用
以及準備一台 MAC 進行模擬測試

究竟 30 天可以玩出什麼東西呢?


我也不知道

總之,先做了再說吧 ^___^

FireMonkey框架核心概念是 LiveBinding 和 dot NET / Flex 所指的 Binding有異曲同工之妙

但我還是喜歡 Flex 的全時 Binding 的概念

FireMonkey / dot NET 都是一次性 Binding 的概念
如果資料異動了就需要重新 Binding …… 這和 Binding 的本意不太一樣吧 哈哈

在使用行動平台前,先用 C++ Builder 來試試看吧


2013/09/06

學習Excel XP Components的網站



以下這個內容好重要嘿!
==================
对Excel单元格的访问通常是使用Range属性,其调用格式如下(以下都正确):
// property Range[Cell1: OleVariant; Cell2: OleVariant]: ExcelRange read Get_Range;

ExcelApp.Cells.Range['A1:C2', EmptyParam].Value := 'ab';

ExcelApp.Cells.Range['A1', 'C2'].Value := 'ab';

ExcelApp.Cells.Range['A1:C2', EmptyParam].Value2 := 'ab';

ExcelApp.Cells.Range['A1', 'C2'].Value2 := 'ab';


但是奇怪:参数Cell1, Cell2不能为数字,也不能为Null,否则会报 “OLE error 800A03EC.” 的错误!

2013/08/19

Dbexpress driver is Thread Safe?

這陣子在為Dbexpress Driver for SQL Server的選擇在測試著,進行到Debug DataSetProvider.OnBeforeUpdateRecord事件時,卻意外的發現各家的Driver在對Thread使用上的處理都不盡相同,於是做了點小小的研究。

2013/08/07

DataSnap 應用中 Variant 會傻傻分不清 NULL 和 Empty String 的問題

這個問題會在 Delphi 7 ~ 2009 版本上出現,官方雖然說在 Delphi2009 的 SP4 (Database pack update) 上修正完成,但實際上卻是依然無解。


EMBT,你不是說已經搞定了嗎?
哎呀!原來是CodeGear的錯!

要還原現場也十分簡單,照著
Report #:  8482 TWidestring seems to convert empty strings to NULL
 的步驟做就可以輕鬆重現經典bug

於是我也依樣劃葫蘆地再發一次,只是角色換成了DBEXPRESS元件而已。
Report #117649 [SQL Server, NVARCHAR] TWideStringField seems to convert empty strings to NULL

想不到當天就有了回覆了,不知道是不是官方人員的回覆,但一整個感動啊!

回文的內容簡貼如下:
I tested your case with Delphi XE2 Update4 with SQL Server 2008 R2 on Windows 7. But, the error does not occur.
I will check the internal status of QC#67982 etc.. 



簡單的說,在「QC#67982」早就已經修補了這個Bug。

那QC#67982是什麼東西? 查了好久才知道原來是打包成「Delphi 2009 Update4」了。
我使用的版本早已經是 Last Version 了
 但現實是並沒有解決我的問題啊!!!
 
接著就看到了這篇:
Report #71984 ApplyUpdates with empty string fields yields null values in where clause 


看來是轉成SQL的時侯出怪手了,那到底DataSetProvider到底轉了什麼資料出來呢?
 *使用 Open Dbexpress + SQLMonitor 擷錄結果:

ISQLCommand.SetParameter: 1; value: 1 INPUT WVARCHAR: '124'
ISQLCommand.SetParameter: 2; value: 2 INPUT INTEGER: 1
ISQLCommand.SetParameter: 3; value: 3 INPUT WVARCHAR: '0001'
ISQLCommand.Execute: update "DemoData"  set
 "SubCode" = ?
where
 "DemoID" = ? and
 "DemoCode" = ? and
 "SubCode" is null


你你你…… SubCode 明明是not null、是empty string('')啊!你這個 NULL 跑出來是要鬧場的啊!

找到問題了,那就來解決吧!
照著剛剛的 Report #71984 也的確是能解決這個問題,修改Provider.pas後的擷取結果如下:

ISQLCommand.SetParameter: 1; value: 1 INPUT WVARCHAR: '124'
ISQLCommand.SetParameter: 2; value: 2 INPUT INTEGER: 1
ISQLCommand.SetParameter: 3; value: 3 INPUT WVARCHAR: '0001'
ISQLCommand.Execute: update "DemoData"  set
 "SubCode" = ?
where
 "DemoID" = ? and
 "DemoCode" = ? and

 ("SubCode" is null or "SubCode" = '''')

果真是解決了這個問題,但SQL看起來怪怪的。問題似乎不在Provider身上。

真正的原因是:
WHERE底下出現了「IS NULL」。那也就是TField.OleValue得到了應為「Empty String」的Variant 卻回傳了「NULL」詭異情形。

既然2010後已經解決,那就別理這個問題吧。


開玩笑的。


其實還好Delphi的Variants單元寫得很好,有幾個判斷的function,就直接放上Code吧。

  if VarIsStr(ClientdataSet1.Fields[0].OldValue) then
    Result := VarToStr(ClientdataSet1.Fields[0].OldValue);

如此一來問題就暫時得到解決了。
不過,能升級還是盡可能升級到XE以上的版本吧!

JSON Parse in Delphi XE (02)

在 JSON Parse in Delphi XE (01) 裡提到因為文字轉碼失敗引發的錯誤 今天就來試著解決這個問題 首先的判斷是: Indy 的 BUG? 那就來看看其它的軟體是否能被正確顯示: