Translate

2015/12/31

風水輪流轉,這次又會轉回【Windows-base Client】了嗎?(下) ── 存亡關鍵的最後一哩

簡單的總結一下 Web 和 Apps 的戰爭結果。

*********************************************

自 HTML5 標準推出後,可以說 Web 的戰線越來越明確,在既有的瀏覽器能延伸到哪裡,Web 功能就能夠到那裡。

Apps 就如同是 Windows 的 EXE,想要和作業系統無縫接軌,也只有 Apps 能辦得到。

2015/12/27

2015/12/24

風水輪流轉,這次又會轉回【Windows-base Client】了嗎?(中) ── 開發界的潮流擂台賽

在一個地方安逸了,大概會有兩種結果:
  • 鑽石永恆久遠,一招永留傳
  • 溫水煮青蛙,等死

前從前,有個作業系統叫做 DOS。
歷久不衰的【C 大於】
據說,當 Windows 起來時,死抱著 DOS 的那批工程師,絕大多數也都抱著 DOS 退休了。

而所謂的石,就是指剩下抱著 DOS 卻還沒退休的人,至今仍有取之不盡的需求收入。不信?看一下賣場裡的收銀機吧!

現在,不管你有沒有經歷過 DOS-base 開發時期,凡是 Windows-base 的程式猿,目前也絕對都受到了第二次挑戰。

那麼,究竟 Windows-base 程式猿,會和 Windows 一起推進太平間,亦或是走向另一波猿生中的高潮?
讓我們繼續看下去


2015/12/17

風水輪流轉,這次又會轉回【Windows-base Client】了嗎?(上) ── Windows程式猿的危機

開發 Windows-base Application 久了之後,就會想要開發一點不一樣的東西。

在 Web-base 的 Thin Client 出現後,有一段時間在懷疑自己,是否要轉換跑道?

平平都是資料庫應用,不需安裝的網頁應用程式在使用者便利性就佔了上風。然而 Windows-base 應用程式在病毒的亂入下,也漸漸被貼上「不安全」的標籤。


現在誰看到【EXE 】敢直接開的!
圖片來源

2015/11/30

dbExpress MetaData 徹底攻略

使用了 dbExpress 後,一直沒有機會詳細體驗。

自從寫了【dbExpress SetSchemaInfo 取得結構的勘誤之處】這篇之後,終於有點進展了。 把可能的呼叫法全上了一遍,成果如下圖:




兩種寫法有好有壞,但效能上是相等的,所以要使用哪種方式完全是看個人習慣。


2015/11/26

Delphi Web 開發解決方案:Kitto

Kitto 是款類似 UniGUI 的 Delphi 開發 Web 應用程式的解決方案之一。
Kitto Logo
先來看看它的架構:
Kitto 大略的架構

2015/11/23

DataSnap 設計上簡直可以取代 Database Trigger 了

這一陣子一直在改良 DataSnap 雲端服務器的效能。

想說 ProviderFlags 可以自動產出 SQL 碼,但礙於必須做更新前、後的處理,所以當時還是人工刻指令。

2015/11/03

FastReport在啟動時載入過久的問題

FastReport的Runtime Design模式很有意思。

幾乎快可以取代Delphi一大部份的功能。

不過有時會遇到報表檔載入時間很久很久的情形。

除了報表檔本身可能很大之外,在FR DataModule裡所存的DataSet,不過是在Design或是Runtime開啟時,也都會先Open!

就是那個Open!把整個啟動時間拉長了。


另外還有Design下設定好的TParams無法再設定的問題:
1.與Dialog視窗綁定的物件優先權較高
2.使用Design程式碼洗掉TDataSet重置

上述兩點都能解決TParams無法再設定的問題。

2015/10/29

一名程式猿的心路歷程,一起來學寫程式吧!(結語)

看完前面十餘載的Coding前傳,不知道程式語言學習對你來說是困難還是簡單?





學程式這件事,說簡單真的是騙人的




但這一系列最終想表達的是:

2015/10/22

Delphi IDE Search 功能的 Regular Expressions


最近在修改元件的過程中,遇到許多要增加屬性的操作。

比方說操作 Excel 元件時,我們可以這樣寫:
  ExcelWorksheet1.Cells.Item[Y, 3];
  ExcelWorksheet1.Cells.Item[Y, 6];
  ExcelWorksheet1.Cells.Item[Y, 8];

這些上百行有點像又不會太像的程式,我想修改成以下的內容
  ExcelWorksheet1.Cells.Item[Y, 3].Value;
  ExcelWorksheet1.Cells.Item[Y, 6].Value;
  ExcelWorksheet1.Cells.Item[Y, 8].Value;

實除修改時,除了增加滑鼠和鍵盤磨耗率外,對技術力的增加簡單是趨近於零。

這時,有個巧妙的工具可以應用 ── Replace Text (Hot Key = Ctrl + H)。

Replace Text 畫面
我猜猜你會怎麼做:

【選擇大範圍的模糊搜尋,找到目標後,再人工修改。】

實在很苦命。

這時正則表達式(Regular expressions)就很有用了。

Regular expressions 的位置

以上述的例子來說,我可以在 Text to find 輸入
{\.Item\[.+\]}

在 Replace with 輸入
\0.Value

如此一來就變成這樣:

找到了,要修改囉
修改成功!

要注意的是,官方說明的【()】符號並沒有功用,實際應用請直接使用【{ }】符號。

參考資料:


一名程式猿的心路歷程,一起來學寫程式吧!(十二)

旅程最終回

在長久的奔波下,求職之路非常不順,這次加入到企鵝的旗下,心裡就打定主義,要在這間公司待到退休。

企鵝所管理的組織大致被分為兩大塊:
  • ERP系統維護和上線
  • 網路管理(兼電腦硬體組裝)

別無選擇,再繼續做基礎工作,兼著學學網路管理,企鵝則是這兩個單位的頭兒。

老實說,學習這件事我沒有慧根,跟著網管的主管學了好多年,許多網管技術一直都學不起來。

「以我的技術力,對他就像是用小姆指捏死一隻螞蟻一樣簡單。」這是網管第二把交椅對與我的程度比較的結論。

企鵝觀察了這個現象很久,就在某天吃飯時,語重心長地跟我說一句話:

「專科在這個社會已經吃不開了,更何況你還是汽車修護的背景。」企鵝長嘆一口氣。



「你覺得能在這間公司待到退休嗎?」

2015/10/15

一名程式猿的心路歷程,一起來學寫程式吧!(十一)

歷經幾次峰迴路轉,我總算如願踏入了IT相關的工作,在電腦補習班的工作大致上是:

軟體安裝 圖片來源
電腦組裝 圖片來源
雖然很期待會有上免費課程的旁聽,可是實際上每天都很忙,天天都有不知名的電腦掛掉,當時忙東忙西,日子也慢慢的過。直到有一天, 我發現我自己生病了,生的病是:

2015/10/08

一名程式猿的心路歷程,一起來學寫程式吧!(十)

在打這篇主題時,才發現居然已經進入第十篇了。

上回提到在夜間部念書時,在白天發生的事情。


那一定要講講晚上燈紅酒綠的學生生活。


這所二年制專科學校,校地是個從正門就可以看到後門的扁長方形。

無趣的專業科目,讓我每個寒暑假都來重修,過得十分充實。


附帶說明,這個時期的夜間部可是要念三年才能畢業的,換句話說,光是重考 + 專科我就花了和別人唸大學四年相同的時間


沒想到我居然還能上到資訊課,原本以為可以上個PowerBuilder課程和產業接軌,但沒想到,老師說:

「我們來上Visual Basic,Basic是最高階的語言,學起來最輕鬆了。」

當時,課堂上所採用的課本是:

2015/10/01

一名程式猿的心路歷程,一起來學寫程式吧!(九)

亂七八糟的學生學習寫程式的日子過去了。

光陰似箭,歲月如梭,來到了高中三年級。

本檔主題僅十三篇這樣寫下去好像沒完沒了,我決定再加快一下時間軸。

別人的高三生活可以說是力拼大學的苦讀期。

我的高三生活是為了糊口而打拼,真是一失足成千古恨。


這時,我的選擇大概只有下列幾種:

2015/09/24

一名程式猿的心路歷程,一起來學寫程式吧!(八)

太好了,終於畫出來了,那麼,應該要開始讓它動吧。

只是,要怎麼讓這張圖動呢?

以我當時的程式水平,要讓這張圖動起來似乎有很大的難度,更慘的是,老師不願意回頭再講述以前講過我語法。

「老師,之前講的語法能不能再重講一次?」

「同學,都快學期末了,範圍很大,你指的是哪一個語法?」老師苦笑著問。

「像 IF, FOR 這一類的語法能不能再重講一次?」看著筆記中殘缺的內容,我弱弱地問。

「這個要重講又要浪費一堂課,你直接問同學好了,這兩個語法很簡單,我必須顧及其它資質比較好的同學。」

老師還沒回答完,就有另一個聲音出現。

2015/09/17

一名程式猿的心路歷程,一起來學寫程式吧!(七)

因為在去年有看到QBASIC,那種漂亮的水藍色畫面。 
所以在第一次看到GW-BASIC時,其實心情蠻沮喪的,這種心情,就跟它的畫面一樣,是黑白的。

只是

聽不懂老師所教的內容更是讓我沮喪。

過了一個月又第三個禮拜,我仍然呈現在一個鴨子聽雷的狀態。
大概是這個狀態
圖片來源

這段期間,央求家人買了部電腦給我,最後也真的買了。
我永遠記得,這台電腦要價「五萬元整」,而當時的物價水平,以一部125CC的機車來說,要價是

「三萬八千八」 。

而這一學期,我所練習程式的時間,也比我在學校汽車修護的課程時數總合,還要多出非常非常地多。

當時,晚上回到家,扒了兩口飯後,就把電腦打開,努力的開始練習老師所傳授的筆記,只是看了很久,但程式怎麼也無法成功運作。


我看不懂你,你也搞不懂我。

這種心情實在很痛苦。

這種情況維持了一個多月,眼下只剩一個月的時間,我的期末作業仍然空白一片,老師課堂上照著他的進度持續講不停,抄筆記都來不及了,哪有機會電腦機上練習。

下課老師就不見,是要怎麼問問題啦!




修車子不行,連寫程式也沒辦法嗎?




面對亂七八糟、寫了又刪的程式作業,盯著電腦螢幕,我呈現在一個半放棄的狀態。



距離期末報告,還剩三個星期。


//-------------------------------------------------


資訊課的進度仍是無條件進行中,而我仍然不知道我要怎麼問老師作業該怎麼寫,因為,我連我的問題都不知道該怎麼問。


人生最難的莫過於如此吧。



這時,
突然看到老師在白板上畫了一個圓,旁邊寫了個指令內容。

又畫了一條直線,旁邊也寫了行指令內容。

接著便開始講解要怎麼使用指令來畫圖,當下仍然是消化不了,但我還是盡了我最大的努力,把老師所說的內容可能地抄下來。



以及一個神秘指令:CLS



當晚,儘管知道做了也沒有用,但我還是把電腦打開,放入磁片,把所抄的指令通通打進去。

說也奇怪,神奇的事情發生了。

畫面上的圓和線只出現一瞬間,居然就消失了!

連續操作幾次,都出現相同的結果。


都迫在眉捷了,怎麼在這時才發生電腦壞掉的事情啊!


但是想想好像也不太可能,於是

我就先把畫圓形的程式碼刪了。


嗯,沒錯,圓形沒畫出來。


再把畫線的程式碼刪了。


嗯,沒錯,線也沒畫出來。



最後,再把CLS刪了。


嗯,
完全沒有畫面
完全沒有畫面
完全沒有畫面。


















在盯著眼前的國防布黑畫面好一陣子後,我突然想到什麼了,
二話不說,接著馬上啟動「金手指」模式。

你要這麼稱呼我也行啦

 兩手的金手指飛也快地鍵出飛揚的程式碼,足足花了數個小時的時間。


我引以自豪的程式結果就要出來了!!





滿心期待地按下了RUN,終於要呈現啦!!!


















我的史上第一個作業初稿(未完成)



敲了兩百多行程式,總算是畫出一個小小的成果。

但這樣就行了嗎?
應該要再做點什麼吧。

只是

天亮了,準備要去上學了。


事實上,這個圖在當時大約花了8個晚上的時間才完成,電腦繪圖什麼的,現在想想還真可怕。




最後期末作業成品如下:



















我們下回繼續 ^_^

一起來學寫程式吧~



2015/09/10

一名程式猿的心路歷程,一起來學寫程式吧!(六)

很快的,終於到了下學期,同學們非常期待這次電腦教室的電腦會不會改密碼。

但幾乎所有人都猜出來了,就剩我一個猜不到 BIOS 密碼,但也沒差,這種無聊的遊戲完全無法提起我的興趣。

這次,不只是樓層換了,連教室裡的電腦也跟著一起換了。

辦公桌上箱型的電腦螢幕沒有改變,但原本應該當墊子的主機現在居然直立者擺放在螢幕的旁邊,而且主機上已經沒有那種需要上鎖的磁碟機,看起來就是全新的機器。

「因為上學期有八成的電腦因為BIOS密碼被改掉,所以那批電腦現在在報廢處理中。」

同學們互看一眼,噗嗤一聲笑了出來。可是我猜不到,所以真的也沒什麼好笑的。(怒)

「資訊中心的老師們決定這次調整一下課程,應該還是要讓你們學一點東西。」

哦!要學什麼呢!?是嘸蝦米嗎?
反正應該是其它的輸入法吧,完全不讓人期待。


「這個學期要教你們如何寫程式。」
「這個學期要教你們如何寫程式。」
「這個學期要教你們如何寫程式。」


2015/09/03

一名程式猿的心路歷程,一起來學寫程式吧!(五)

這是二年級梅雨季開始的第二個星期,外面天空下得雨似乎沒完沒了。

「全部到走廊集合!」

班長興奮地跑回教室前門,大聲的指揮班裡所有人。

「快點!已經二十分鐘了!」

全部的人都還搞不清楚狀況,緩慢的向走廊移動。


「是要去哪啦!」一名對上課沒興趣的同學大聲對班長叫道。

「剛剛資訊老師才決定要讓我們在資訊教室上課,所以不要讓他等太久!」班長立即吼了回去。

由於是第一次去資訊大樓,所以全班陷入一種抗奮的狀態 ── 電腦就是要拿來打電動,不然要幹嘛的心態。

 一伙人在迅速集合後,浩浩蕩蕩的往資訊大樓前進,腦袋不聽使喚的出現等下可能會出現的畫面。
圖片來源

幻想被隨之而來巨大建築物的陰影蓋住,抬起頭,移開雨傘,資訊大樓大門已在眼前。

進入大廳,只要左轉,就可以進入夢幻機臺,呃,是電腦教室,真的太興奮了!


「腳步放輕,我們要右轉上樓,上五樓後,左邊第一間教室。」班長以全班能聽到的音量提醒我們。


對,就是右轉,上五樓,電腦教室就在左邊。




咦?




不是在一樓?明明一樓就有電腦了啊,為什麼要上五樓?


算了,反正有電腦就好了,爬個五樓什麼的也不算什麼……才怪,那是幾近七到八樓的高度,爬上去真的超喘的!

到了電腦教室,和一年前所看到的電腦教室不同,陳舊泛黃的巨大電腦 ── 兩台箱狀螢幕站在橫躺的主機上,每張辦公桌都放有這樣兩台的電腦。

是因為是新科技怕被學生亂用,所以才很少開放吧。

老師已經在教室內了,在我們全就定位後,便開始說明他的上課底線和學期目標,我沒什麼興趣,便也沒認真在聽,簡單的說,這個學期大概就是要完成輸入法的訓練,除此之外沒有別的要求,因為資訊課對我們來說是蠻多餘的,把車子修好比較實在。

在老師說明的時候,已經有幾個同學偷偷開機了,開機後的嗶聲實在太大了,想裝聽不到都不行,倒是老師完全無視這些手癢的同學,仍自顧自的在說明,這老師也太穩了吧。


「好了,以上說明各位都了解了吧,現在我把磁片發下去,每人一片。」


 磁片?原來開機需要磁片啊,難怪老師可以無視學生偷跑開機的行為,老神在在的說明。

在拿到老師發的,像A5紙張大小的磁碟片後,把它插入磁碟機,並且把它鎖起來,接著按下開關,聽到嗶一聲後,主畫面就呈現在我的眼前。

怎麼跟想像中的不一樣,畫面是沒有色彩的一片黑,以及伴隨磁碟機磁頭讀取聲緩慢吐出的黃色文字。

「黃色的文字!等下應該就有不一樣的色彩吧!」我大概是興奮過頭了,心裡的話居然不小心脫口而出。

圖片來源


我想老師應該是聽到了,他是這麼說的:

 「開機後應該會進入輸入法練習程式,你們不要想試著在這間教室玩任何遊戲。」







 「因為它是黑白螢幕」
 「因為它是黑白螢幕」
 「因為它是黑白螢幕」







什麼!?黑……黑白螢幕!? 意思是
這個
這個

還有這個

 都沒有機會了嗎!?

啊啊啊啊啊啊啊啊啊啊啊啊啊啊 (抱頭吶喊)



 無視於我內心中的吶喊,老師繼續說:

「開完機的同學選擇自己喜歡的輸入法開始練習。考試只有學期末那次,目標一分鐘三十個中文字。」

「老師!只有注音和內碼!要選哪一個」

 「只要能打出中文就好,輸入法不難,我就不教了,自己在課堂上找時間練習。」


接著老師便開始授課,我們就像是被半放棄般,一邊聽著無聊的計算機概論,一邊做輸入法練習。

不過,很快的,我們找到新的樂子,也算是很快樂的過完我們上學期的資訊課,那就是:


































猜 BIOS 密碼




我們下回繼續 ^_^

一起來學寫程式吧~

2015/08/27

一名程式猿的心路歷程,一起來學寫程式吧!(四)

話說情竇初開的少年展開了尾行初體驗。
才剛進了一樓大廳,便見到一對男女手牽著手  屁顛屁顛 開心地走出來,我依稀還記得,那他們看到我先是驚訝而後鄙視的神情。


我還穿著實習生的制服呢!


而女孩的長髮不經意地又再一次拂過我的臉龐;這輕甜、強烈的香氣……


沒錯,是她。

「學長,謝謝你幫忙這次的作業!你人真好!」
「這沒什麼,這種類型的家庭作業,對我來說是小菜一碟。」
「學長謝謝你,那我要坐車了,明天見。」
「我可以騎車載妳回去。」

「我爸爸會在車站等我耶,被看到不太好。」

學長只能悻悻然望著女孩的背影離開。

那時還沒有好人卡這名詞出現。
但我心中是如此吶喊的:


2015/08/20

一名程式猿的心路歷程,一起來學寫程式吧!(三)

在開始介紹資訊課之前,還有個事件緣起要先講解。



開始囉!

//-------------------------------------------------


可惡!我就是不想唸書!
體內的頑劣分子強力大炸裂,非常用力的擺爛,我很滿意這次非暴力不合作的叛逆行為。
初到該校時的內心反應(圖片來源




我錯了。

在初次段考後,差點沒被家中二老 KO。

人要自重後而人重之,但當時人生重心並不在課業上,繼續畫漫畫、寫劇本。

內心說穿了只有三個字:

圖片來源
只是在初次段考後是收斂很多就是了。

反正,最幸運的事就是:



嗯,我還活著。


要不然咧,你們就無緣看到我在這邊話猛唬啦!

//-------------------------------------------------

這到底是什麼樣的一所學校?

每個成長歷程都是將自己推升到現在成就的一個推手,但,這並不是十分願意回憶的記憶區塊。

印象中,這是所車程非常遙遠的學校,我必須轉程兩班車才到得了學校大門,來回乘車時間約在 2 小時到 3 小時,如果再加上等車時間,還要再加個 1 小時,那時最不值錢的就是時間價值了。

幸好,學校體恤我們這些路遙有馬力的學生❪?❫,特約了好多路線的校專車,雖然是單程,也就是回家限定,但這可以大幅縮短我回家的時間,想想能早早洗洗睡,於是也就買了這善意地回家券。

只是,因為我的科別特性,必須在一天的課程後,繼續留下來接受「體能訓練」或「學徒中的學徒」課程。

結果就是,我繳了整學期的專車費卻常態性地要另外搭公車回家。

一日車資必破 100 新臺幣!

可以想見,每天光是「上」「下」學就是讓人開心不起來的事。就痛扣!

付了錢卻沒有享受到服務,就是一種「乾洗」。


我被公車公司乾洗!
我被公車公司乾洗!
我被公車公司乾洗!
...
..
.
這種羞辱的事真不願意回想 ><|||

時間價值的流失比什麼都還要可怕!

在幾番波折,總算是看到大門,在大門旁,有個方正的水泥建築,牆壁上的磁磚和圍牆都是同樣的酒紅色。

進入大門,在左手邊的是3000公尺 = 七圈半的操場,順著操場往正前方直走到底,是同樣貼滿酒紅色磁磚的行政大樓。

左轉延著行政大樓的外牆走到底,有個大中至正銅像的圓環,圓環的後方是圖書館。

轉進圓環的右邊直走到底,有棟白色油漆粉刷的商科教室大樓,隔壁淺藍色的建築物則是工科教室大樓,再過去是大禮堂。

而商、工科教室大樓和行政大樓中央間隔有個大型花圃,是學生下課時間閒逛的去處,行政大樓和花圃的右方則是工科的實習工廠。

再往大門的方向走,前面就是程式猿的啟蒙搖籃──資訊大樓

話說回來,如果說學徒的本質是老闆付錢請來的初學者技師兼雜工,那麼:

工科的學生就是所謂「學徒中的學徒」,就是家長付錢請學校來指導學生奴役打雜修佛兼學點技術。

小和尚在開始唸佛之前,必修課程就是:挑水、砍柴、打掃。

想當然爾,身為傳說中「學徒中的學徒」,碰掃把的時間肯定是要比碰車子要來得多,我自然不會是那個例外。
 
我被分配到的打掃區域就是資訊大樓和實習工廠的中間的道路,六公尺寬的道路,每天都有掃不完的落葉。

現在想想,我真的很幸運。

那是個美麗黃昏時段,一如往常的,我悶著頭,默默地幫那些老樹「擦屁股」。

一陣輕柔的微風拂過我的臉,帶著淡淡的果香。

我抬起頭,有個長髮飄逸的女孩子從我眼前的資訊大樓溜去,不一會兒便消失在轉角處,意亂神迷的我不知道是哪根神經錯亂,隨著稍逝即縱的香氣,尾隨著進入資訊大樓。





我們下回繼續 ^_^

一起來學寫程式吧~

2015/08/13

一名程式猿的心路歷程,一起來學寫程式吧!(二)

話說夢想尚未實現,
光陰似箭,又到了鳳凰花開的日子。
卻緊接著就到了選擇人生夢想的交叉路口。
如果那時有個「玩樂科」,我想我應該也考不上,話說,那時的環境大至上有這麼幾種選擇:
  • 普通高中
  • 專業科技學校
  • 職業學校
對念書沒興趣的我,卻也不知道要走哪條路下去。
嗯……這真的不是我畫的  圖片來源
對呀,不是要當漫畫家?那來去考當時最夯的復 x 美工,以我那時的基礎,一定可以穩穩的上!



























落榜了……





一輩子沒拿過水彩筆、練習過素描,維二會的只是臨摩描繪的幾個公仔,這樣的水平要上頂尖的美術學校。

作夢!(嘖嘖嘖……這所學校少了我這麼一位好學生)

充其量只能算是業餘愛好。
但這一巴掌好像還沒有打醒我,我還是決定要選個能和繪畫沾上邊的道路。

事實上,沒資格糾結的我,當時糾結得很,因為家庭環境,只賦予我讀公立學校的權利,在熱門首選:普、電、資、商全部不行滿額的情況下,我能選的,非常有限。

就在我決定要去唸 xx 海事,進軍當航海王時,老爹居然搶先一步在我的志願欄裡填上:

汽車修護科
汽車修護科
汽車修護科
.
.
.

原因只有一個,我的成績能觸到的天只有那一科。

在傳統唯有讀書高的封建思想時空背景下,技職教育可以說是條永世不得超生的不歸路。
可惜我腦袋不夠靈光,就算我人生再來一次,可能還是重複相同的道路也說不定。

但是,我這條程式猿之路肯定不會走得這麼崎嶇。

就算當時資訊不發達,也還是有圖書館這種巨大資源可以利用,至少不是只對於某個職業單純的充滿幻想吧。

正因能力不足才只能被迫選擇,那麼先把腳步站穩吧!
很明顯地,當時還不明白這個道理 圖片來源

話說,我現在有個美工實作功力非常高強的同事,公司裡大大小小的櫃子、擺設、看板等,甚至是酒窖置瓶架,全都是由這位同事一手包辦。而且,不只是做好,更是美輪美奐

在一次聊天中,他感嘆著美工界的第一志願又如何。原來三十年前他讀的是復 x 美工,現在是我們家的總務。


你不知道的是,他是我們家最有 Power 的總務!


回頭看自己懵懵懂懂的高職生涯,一直到上了資訊課,才算是個程式猿起頭。

資訊課可以說是絕大多數程式猿的起頭呢~



我要成為 Coding 王!


以為我那時就抱這樣的想法嗎?並沒有!那時的資訊課所教的內容是……




我們下回繼續 ^_^

一起來學寫程式吧~

2015/08/06

一名程式猿的心路歷程,一起來學寫程式吧!(一)

在我的人生之中,原本設定應該是在十七歲進入職場,修理汽車、管理零件庫存、精進修理技術,成為一名專業的汽車修護技師,接著晉升領班,好一點可能就自己開業,接著一路往退休之路前進。
圖片來源

然而,沒想到現在居然是以寫程式、開發軟體維生,雖然完全算不上是有成就的程式設計師,但這條路,我已經比很多人走得還遠,回顧這段跌跌撞撞的程式人生,是否能找出未來的人生目標,我想還是個未知數,但如果有人因此而想走上這條程式之道,這也是很棒的事,接下來,就要開始我的洗腦文啦。
圖片來源


當然,隔行如隔山,這中間的過程相當錯綜複雜,不亞於小紅帽找到大野狼外婆的難度。

套句台語的說法,即是「話若是講透更 目屎是揮莫離啊! 」
(如果要把話說盡,可以說整晚,眼淚也會不停的流,擦也擦不完)

真的要追本朔源,可能要從自己接觸電玩開始講起吧。

每個男人心中都有個頑劣的調皮男孩,玩自己喜歡的玩具,追求自己喜歡的夢, 進而成就自己的理想,這大概是大多數人的生命旅程。

身為不喜歡讀書的宅男,我只喜歡打電玩,最大的樂趣便是虐待遊戲裡的大魔王(真的,實際被虐的是玩家,別被騙了),那時手持電動玩具正是興盛,手上的零用錢都買拿去買電動玩具了,花費法是這樣的:買電動玩具、玩到被老師沒收、再買電動玩具、重玩至原先的進度、又被沒收......................的無盡輪迴。
 (所以千萬不要認為禁止是有用的,倒不如順天而行吧)
圖片來源
無視他人的人生,自然不被他人重視,對那時的自己除了電動玩具的記憶外,也沒有其它記憶了,和其他同學的互動嗎?嗯……還蠻慘的,沒互動,但又愛炫耀遊戲通關的「知識」,以滿足自己的成就感,若要說這人不是怪咖,還真的沒人信吧。

電玩世界那種非黑即白的世界,是不能應用在真實世界的,孩子,地球很危險的。

但我就是很愛,這種單純的人造空間居然好像還真有這有人間仙境般的世界,到底怎麼做的?

不明所以,卻樂此不疲。


「如果我也能做出這樣讓人喜歡的遊戲……」


這大概是我那時的內心寫照。接著便開始學習畫漫畫、寫劇本,現在回想起來,不就跟寫程式一樣嗎?

都是文字工作者啊!

原來,我啟蒙便是從文字工作者開始。所以那時,我的夢想便是:









漫畫家!









真的,和程式完全沒有任何關係,八竿子打不著邊啊!

至於什麼時候開始我程式設計之路呢?








我們下回繼續 ^_^

一起來學寫程式吧~

2015/07/31

Httpsrvr.dll在Apache for Windows的設定

按  HTTPSRVR with Apache 的說明:
1. Install Apache (I will assume default directory structure for 2.0.52).
2. Copy 'httpsrvr.dll' to your /apache2/cgi-bin directory.
3. In httpd.conf, change the 'Options None' (again assuming defaults for 2.0.52) in to 'Options ExecCGI'.
4. AddHandler isapi-isa .dll to your httpd.conf.
5. Restart Apache. 


補充實作的設定段:
#
# "/xampp/cgi-bin" should be changed to whatever your ScriptAliased
# CGI directory exists, if you have that configured.
# Options None

    AllowOverride None
    Options ExecCGI
    Order allow,deny
    Allow from all
 AddHandler isapi-isa .dll






2015/07/27

Delphi字串計數函式

已經忘記是什麼時候遇到需要計算重複字的次數問題
前陣子想到才上Google來找,因為關鍵字仍然不方便搜尋,所以轉錄在這裡。

uses
  StrUtils;

function Occurrences(const Substring, Text: string): Integer;
var
  offset: Integer;
begin
  result := 0;
  offset := PosEx(Substring, Text, 1);
  while offset <> 0 do
  begin
    inc(result);
    offset := PosEx(Substring, Text, offset + length(Substring));
  end;
end;

資料來源:

2015/07/23

TDataSetProvider 的一個 Bugs

追 VCL Code 才找到問題主因……
procedure TDataPacketWriter.AddIndexDefs(DataSet: TDataSet; const Info: TInfoArray);
...
        { Get the DEFAULT_ORDER }
        if not (poRetainServerOrder in Options) then
          DefIdx := (DataSet as IProviderSupport).PSGetDefaultOrder // EListError: List index out of bounds (-1)
        else
          DefIdx := nil;
...

但裡頭是組合語言,我已經沒辦法了,只能回避囉!

延伸閱讀:

2015/07/21

TByte 和 TByteDynArray 的轉換

TByteDynArray 同等 TByte

意外發現 TBytesStream 這個類別

但發現不能做 TBytesStream.Create(TByteDynArray)
不過卻可以寫成這樣:
TBytesStream.Create(TByte(TByteDynArray))

據說後來有改正這個缺點。

see also:

2015/06/16

Delphi 和 CB 的雲端技術,安全嗎?

在「都加密了,還怕什麼?」這篇有提到關於雲端技術常被探討的安全性議題。

可以知道
  • 只要採用合適的加密法以及自訂的傳輸方式,可以使雲端應用更安全。
  • 不是公開制定的協議,被駭客盯上的可能性機乎為零。

或許以 DataSnap 這種小眾市場上,稍加封裝就可以讓安全性大幅提升。

當然,Borland / EMBT 如果有開後門的話就另當別論了(笑)

2015/06/08

REST DataSnap: Server Methods Lifecycle 的差別之處

REST DataSnap 的 Server methods Lifecycle 有:Session, Invocation, Server 三種選項。


Session:
每個 IP 只會固定配置一個實體,直到 Client 離線後釋放。


Invocation:
每次連線都會配置一個新的實體,到 Client 離線後釋放。


Server:
只有一個實體,給所有 Client 使用,類似 Com-based 的 Threading Model : Apartment




其它的教學連結:
  • Delphi Labs: DataSnap XE - Server Methods Lifecycle         
  • DelphiLabsDataSnapLifecyclePart1        
  • DelphiLabsDataSnapLifecyclePart2
  • 2015/05/08

    關於ClientDataSet.Filiter的Trim補充

    Delphi in Depth: ClientDataSet 愛好者Cary Jensen在他的部落格上有回應我對Trim參數的問題。

    寫得好詳盡,在這邊存個傳送門:Using Trim in ClientDataSet Filters

    2015/05/05

    Firebird 版本選擇:Classic, SuperClassic, SuperServer

    Firebird 官方說明文件:Classic, SuperClassic or Superserver?

    Classic:

    連線方式採 1 程序(Process)對應 1 連線,單一程序崩潰時並不影響其它連線。

    SuperClassic、SuperServer:


    • 採 1 程序對應所有連線,該程序如果崩潰即所有連線都會異常。
    • 承上,崩潰後該程序會自動重啟。

    資源分配上:

    SuperServer > SuperClassic > Classic。
    (※SuperServer 還多了一個作弊緩衝區)

    只有 Classic 和 SuperClassic 能同時連線。

    SuperServer 在 Windows 下預設只採用第一個認到的核心。必須到 firebird.conf 設定 CpuAffinityMask。

    本機連線:

    只有 Classic 和 SuperClassic 提供,有安全性的問題,但搭配單機版應用程式下非常實用。

    三款都有各自的特色。
    如果還是不知道要用哪個,就下載 SuperClassic 吧。


    2015/05/06補充:
    謝謝Delphi.KTOP的leveon大提供額外的資訊,還有附圖解說,十分詳盡。
    傳送門:Firebird – SuperServer, ClassicServer or SuperClassic?

    另外,據說Firebird 3.0之後這三者會合成一個,不知道正式版發行後是不是真的會成真,期待早日釋出。

    2015/04/25

    FireDAC dbExpress 效能之戰,使用DataSnap

    打從接觸dbExpress(以下簡稱DBX)開始,對於DBX能否應用在實戰當中存在非常大的疑問。

    但是ADO和FireDAC是屬於2-Tier架構元件,要和DBX這種屬於單向唯讀的元件比較,由於立足點不同,所以直接比較是非常奇怪的事情。

    也許使用While Loop測試比較也可以;但測試結果與其說是測元件效能,說是測試電腦硬體效能應該更為貼切。

    While not Query.Eof do
    begin
      Query.FieldByName(FieldName).AsString...
      Something code...
      Query.Next();
    end;

    這樣的應用,現實設計卻很少在我們的專案中出現。
    那麼這樣的測試方法,存在有以下兩點問題:

    2015/04/23

    原來DBX4 Driver可以這樣更新

    因為DBX4之後,這框架就進入維護期,RAD Studio XE各版就是Bug的修正。

    剛拿XE7的dbxmss.dll到XE的環境下測試,居然可以正常使用。

    原本XE下的BUG也確實有得到修正,太讚了!

    只是到了XE7,還是有些許異常需要想辦法繞,使用起來還是毛毛的。


    整個DBX4專案才這麼大,這樣也出包
    怎麼繞呢?就是原本在TSQLQuery下就可以做的調整,現在要移到TClientDataSet下來做才可以。

    最後,證明3rd才是最優的解決方案!


    Devart的Dbx driver for MSSQL 就完全沒有問題。讚!



    2015/04/21

    Delphi DataSnap CORBA C/S 構築方式

    MTDORB - Delphi向けCORBA実装

    很意外的,居然還有人在使用CORBA技術,而且還有Open Source出來,可以免費使用。

    它的名字是:MTDORB -- Multi-threaded Delphi ORB

    文末的「 オープンソースを利用した 3層C/Sシステムの構築方法」是2006年的版本,在Delphi 2009後已經不知道要怎麼去建構CORBA Client……

    但至少還是個希望,有機會可以試試看讓它復活。

    參考資料:






    Delphi XE Regular Expressions 單元中的 Bug

    Report #: 87752 TRegEx frees FRegEx while TMatch et al still have a reference to it 這篇中有提到。

    在 RegularExpressions 單元有個潛在的bug。 重現的方式也很簡單:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Button1.Caption := TRegEx.Match('one two', '\w+').NextMatch.Value;
    end;
    

    會發現Button1.Caption變成空白,而不是預期的"one"。

    詳細的解說在:
    Bug in Delphi XE RegularExpressions Unit.

    解決方式如下:
    To fix this bug, delete or rename the two RegularExpressions.dcu files and copy RegularExpressions.pas into your source code folder. Make these changes to both the TMatch and TGroupCollection records in this unit:

    1. Declare FNotifier: IInterface; in the private section.
    2. Add the parameter ANotifier: IInterface; to the Create constructor.
    3. Assign FNotifier := ANotifier; in the constructor’s implementation.

    You also need to add the ANotifier: IInterface; parameter to the TMatchCollection.Create constructor.

    修改完成後,把Debug和Release後的dcu,覆蓋($BDS)\lib\debug(release)目錄同名檔案即可。

    附帶說明,在Delphi XE2以後已經修正了這個Bug,如果你有需要,請再留言索取。

    參考資料:

    2015/04/17

    dbExpress SetSchemaInfo 取得結構的勘誤之處

    dbExpress的SetSchemaInfo一直以來都很接近ADO的OpenSchema
    連取得的表格格式都很接近。
    先來看看TADOConnection.OpenSchema和TSQLConnection.SetSchemaInfo各別取得的表格:
    Get Primary Key Schema Information
    ADODBX
    TABLE_CATALOGRECNO
    TABLE_SCHEMACATALOG_NAME
    TABLE_NAMESCHEMA_NAME
    COLUMN_NAMETABLE_NAME
    COLUMN_GUIDINDEX_NAME
    COLUMN_PROPIDPKEY_NAME
    ORDINALCOLUMN_NAME
    PK_NAMECOLUMN_POSITION
    INDEX_TYPESORT_ORDERFILTER
    表1:ADO參數為"siPrimaryKeys",DBX為stIndexes

    看起來一切是如此美好。
    但沒想到在dbExpress 4之後,SetSchemaInfo做了一些調整
    dbExpress 4下使用SQLConnection.SetSchemaInfo取得的表格居然是:
    dbExpress 4: SetSchemaInfo stIndexes
    CatalogName
    SchemaName
    TableName
    IndexName
    ConstraintName
    IsPrimary
    IsUnique
    IsAscending
    表2:DBX 4實際取得的Schema表格

    結果COLUMN_NAME被取消了,如此一來,要使用DBX就無法直接使用SetSchemaInfo來取得Primary Key欄位。

    只是,每一版The Structure of Metadata Datasets卻從來沒有更新及說明替代方法,實在是很讓人傷腦筋。

    可參閱:

    2015/04/21 更新:

    2015/04/07

    FireBird測試使用的資料庫

    每每要測試資料庫時都很煩腦沒有測試使用的資料庫。

    常見的北風資料庫很不錯,但就是要安裝 SQL Server 這點就比較麻煩。

    剛剛在這裡找到Borland Interbase 6.0搭載的範例資料庫,搭配Firebird emb上很好測試。

    在這邊記錄一下連結:

    mirrors for employee.gdb

    2015/04/02

    Delphi IntraWeb 會計系統程式設計

    IntraWeb(以下簡稱IW)是Delphi裡繼WebSnap後另一個網站設計解決方案。
    許多Delphier認為IW不是個一回事。

    不過如果IW真的這麼糟,能從3.x發展到現在的XIV,直接說它不堪使用也未免太過武斷。

    今天為了證明IW實用性而找了一個專案來嘗試看看。

    就以陳惟彬老師的ASP.NET會計系統來體驗試作。
    ASP.NET會計系統程式設計 2002 陳惟彬著

    為什麼要使用這本書?

    這套系統是以ASP.NET 1.1 VB語言進行開發,有以下和IW共同的特色:
    1.頁面設計採絕對定位,和Delphi VCL Form設計方式相同。
    2.全面使用ASP.NET元件,剛好和IW元件形成對照組。

    考量Delphier的不良習慣,限縮設計的手段:
    1.全程不使用任何已知的HTML, CSS, JavaScript。
    2.付費可以省工,TMS for IntraWeb參戰。

    專案開發環境:

    • Delphi XE
    • FireBird database 1.5 embedded (.Net framework not need!)
    • IntraWeb XIV 14.0.23
    • TMS IntraWeb Component Pack 5.4.1.1

    整個專案所需要的檔案

    IntraWeb 會計系統主畫面

    新增子會科程式畫面
    與 VCL Form 設計比較不同的地方是:
    1.為了配合AJAX(Asynchronous JavaScript and XML),在IW元件中,或多或少都有提供OnAsync事件,而要使用OnAsync事件時,必須要注意搭配元件的刷新。
    以「新增子會科程式」為例,當TIWDBAdvWebGrid允許Async Edit時,就必須在.OnAsyncEdit事件寫上TIWDBAsyncNavigator元件變更處理,否則便出現 Grid 在編輯狀態,但Navigator卻沒有變化的搞笑情形。
    2.TIWDBGrid和TDBGrid很像,但編輯功能還需要綁定其它TIWDBxxx元件。
    3.新增功能:
      A.TIWDBGrid:無此功能,必須另外設計新增畫面。
      B.TIWDBAdvWebGrid:有新增功能,非常接近TDBGrid體驗。
    4.TIWDBAdvWebGrid內建就有和ASP.NET Grid相同的分頁功能,這是DBGrid沒有的功能。

    開發心得:
    在開發時間緊迫的情形下,最終還是放棄OnAsync事件的使用。
    開發過程中全都使用Delphi程式,開發習慣並沒有因為是網站開發而有所改變。
    IW元件功能和VCL元件相比,功能性相對較差,不足的地方還需要額外程式處理,一樣全面使用Delphi Code。
    TDataSet存放在Web Server上,這先天的限制使 IW 註定不能承受太大量的使用者負載,但卻能做到很複雜的商業運算。當然也可以不使用TDataSet,這樣就不需使用DB Aware元件,就像不使用DBGrid而使用StringGrid一樣,但相對的,就必須在程式上多費點苦心。
    畫面很陽春,和VCL一樣,沒有內建 OS Style 的 IW 畫面實在太復古。

    結語:
    系統本身雖尚未完成,但初期開發結果能充分使用TDataSet的功能,所以完成整套系統只是時間上的問題,不會有無法做完的情形,何時才能完成?等我空閒一點再說吧。 ^_^

    2015/03/30

    TIWDBAsyncNavigator 圖片無法顯示的問題

    IntraWeb version: V14.2.3
    TMS IntraWeb version: TMS IntraWeb Component Pack Pro Script Edition V5.4.1.1

    在使用 TIWDBAsyncNavigator 元件時,實際執行的畫面如下:
    TIWDBAsyncNavigator 的圖片無法顯示
    沒有指定按鈕圖片時,IWDBAsyncNavigator預設會載入 IWDBNavigator 的圖片。

    網頁原始碼比較:

    IWDBAsyncNavigator 圖示路徑
    IWDBNavigator 圖示路徑
    檔名相同,但 IWDBAsyncNavigator 路徑缺少根目錄符號(/)。不知道是什麼原因造成,於是進入 IWDBAsyncNavigator.pas 來檢查。

    在 LINE 1036 處修改如下:
    {$IFDEF TMSIW11}
        {$IF FALSE}
        if url = '' then
          url := '$/gfx/DBNAV_' + Action + '.gif';
        if urld = '' then
          urld := '$/gfx/DBNAV_' + Action + 'Disabled.gif';
        {$ELSE}
        if url = '' then
          url := 'gfx/DBNAV_' + Action + '.gif';
        if urld = '' then
          urld := 'gfx/DBNAV_' + Action + 'Disabled.gif';
        {$IFEND}
    {$ELSE}
        if url = '' then
          url := '/gfx/DBNAV_' + Action + '.gif';
        if urld = '' then
          urld := '/gfx/DBNAV_' + Action + 'Disabled.gif';
    {$ENDIF}
    


    IWDBAsyncNavigator  圖示終於可以正確顯示


    參考資料:IWDBAsyncNavigator images

    2015/03/28

    Firebird Embedded 連線注意的地方

    Delphi的DBX一直都無法連線到Firebird 1.5。

    老是出現「DBX Error: Driver could not be properly. Client may be misiing, not installed properly, of the wrong version, or thr driver may be misiing from the system path.」

    原來是DBX Driver不只是單認system path,連VendorLib也強迫認「fbclient.dll」。

    難怪我就算把VendorLib設為「[DirPath]\fbembed.dll」也完全連接不上Firebird Embedded。

    結論:
    1.把Firebird的fbembed.dll複製到Delphi安裝目錄\Bin。
    2.fbembed.dll改名為fbclient.dll

    連結成功!

    2015/03/10

    IntraWeb使用ClientDataSet的注意事項


    Working with ClientDataSets裡是這麼寫的:
    If you are using a ClientDataSet component and you get an Access Violation when exiting the application, you need to add the DBClient unit in the application uses clause before IWMain.
    The reason for the access violation is that if DBClient is not included in project file uses clause, it's internal interfaces are freed before all sessions are closed and when IW closes it's sessions it will try to free ClientDataSet component, and you will get the access violation.
    When DBClient is placed before IWMain IW will free sessions before DBClient interfaces are freed.
    這章節在描述TClientDataSet可能會在Session釋放前就先被清除(Freed),然後在Session要釋放時又再被釋放一次,進而發生 Access Violation。

    解決的方法就是在 IWMain 前的 uses 加入 DBClient,讓釋放的順序正確,Access Violation的問題才能獲得解決。





    Delphi XE7使用Indy 10.6.1連結Gmail SMTP

    我在KTOP中回答「xe6 使用gmail的問題」這個主題,因為程式碼編排有問題,故在這裡另外轉貼。

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdExplicitTLSClientServerBase, IdSMTP, IdSSLOpenSSL,
      IdMessage, IdAttachmentFile, Vcl.StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
        function SendEmail(sendTo: string;
                        subject: string;
                        body: string;
                        attachFiles: TStringList;
                        smtpHost: string;
                        smtpPort: Integer;
                        smtpUser: string;
                        smtpPass: string;
                        tls: TIdUseTLS): boolean;
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    { TForm1 }
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      SendEmail( 'mybuddy@example.com', // 目的地E-Mail
                        'This is the subject',
                        'This is the body of the email....',
                        nil,
                        'smtp.gmail.com',
                        587,
                        'myusername@gmail.com', // 登入帳號
                        'mypassword'{ 登入密碼 }, utUseExplicitTLS);
    end;
    
    function TForm1.SendEmail(sendTo, subject, body: string;
      attachFiles: TStringList; smtpHost: string; smtpPort: Integer; smtpUser,
      smtpPass: string; tls: TIdUseTLS): boolean;
    var
        smtp: TIdSmtp;
        ssl: TIdSSLIOHandlerSocketOpenSSL;
        msg: TIdMessage;
        i: Integer;
    begin
        smtp:=TIdSmtp.Create(nil);
        ssl:=TIdSSLIOHandlerSocketOpenSSL.Create(nil);
        msg:=TIdMessage.Create(nil);
        try
    
            try
                smtp.Host:=smtpHost;
                smtp.Port:=smtpPort;
                smtp.Username:=smtpUser;
                smtp.Password:=smtpPass;
    
                //smtp.OnConnected :=IdSMTP1Connected;
                //smtp.OnDisconnected :=IdSMTP1Disconnected;
                //smtp.OnFailedRecipient :=IdSMTP1FailedRecipient;
                //smtp.OnStatus :=IdSMTP1Status;
                //smtp.OnTLSNotAvailable :=IdSMTP1TLSNotAvailable;
                //smtp.OnWork :=IdSMTP1Work;
    
                if not (tls=utNoTLSSupport) then begin
                    ssl.Destination:=smtpHost + ':' + IntToStr(smtpPort);
                    ssl.Host:=smtpHost;
                    ssl.Port:=smtpPort;
                    ssl.SSLOptions.Method:=sslvTLSv1;
    
                    //ssl.OnStatusInfo:=IdSSLIOHandlerSocketOpenSSL1StatusInfo;
                    //ssl.OnGetPassword:=IdSSLIOHandlerSocketOpenSSL1GetPassword;
                    //ssl.OnStatus:=IdSSLIOHandlerSocketOpenSSL1Status;
    
                    smtp.IOHandler:=ssl;
                    smtp.UseTLS:=tls;
                end;
    
                msg.Recipients.EMailAddresses := sendTo;
                msg.Subject:=subject;
                msg.Body.Text:=body;
    
                if(Assigned(attachFiles)) then begin
                    for i := 0 to attachFiles.Count - 1 do begin
                       if FileExists(attachFiles[i]) then
                            TIdAttachmentFile.Create(msg.MessageParts, attachFiles[i]);
                    end;
                end;
    
                smtp.Connect;
                smtp.Send(msg);
                smtp.Disconnect;
    
                result:=true;
            finally
                msg.Free;
                ssl.Free;
                smtp.Free;
            end;
        except
           result:=false;
        end;
    end;
    
    end.
    

    結論:
    上述程式可以正確發出和收到信件,在XE7下驗證無誤。


    參考資料來源:Sending email with attachments using Delphi, Indy 10.5.5 and GMail




    2015/03/09

    ADO 是個好東西,不用嗎?

    ADO--ActiveX Data Object
    在RAD Studio裡被歸類在「ADO Express」元件盤中,RAD Studio 2006後則改名為「dbGo」。

    ADO架構
    圖片來源:MSDN Microsoft Data Development Technologies: Past, Present, and Future
    ADO的缺點在於,全世界的作業系統中,只有Windows才有具備Ole Provider。

    換言之,ADO只能運行在Windows的世界。

    然而在深入了解ADO後,發現ADO的核心概念和DataSnap幾乎一樣,透過Windows平台內建的OleDB engine,要單層開發、兩層開發,有狀態還是無狀態形式的公事包開發都不是問題,一切只需要一個ADO元件就可以完成。

    ADO是個好東西,不用嗎?

    單機,對於Access、Excel都有非常多的範例和很極佳的效能。
    兩層,對於SQL Server的連線效能極佳,又額外提供其它資料庫的Ole Provider可以擴充。

    無狀態的儲存,我們可以採用「公事包」的方式,把ADODataSet的Data另存為實體檔案,待網路順暢時再行傳輸。

    「公事包」這名詞,微軟是這麼說的:

    使用公事包同步 -- Windows 7說明
    您可以使用 [公事包] 來讓兩部不同電腦之間的檔案保持同步,即使電腦不在相同的網路上。 如果電腦不在相同的網路上,您可以使用卸除式媒體從一部電腦將檔案複製到另一部電腦,再使用另一部電腦處理那些檔案,然後使用 [公事包] 對原始電腦進行變更同步。

    ADO裡的LockType中,ltBatchOptimistic參數便是對應公事包特性。
    更淺碟一點的說法,就是「離線編輯」。

    實際的範例用法如下:
    dfm:
    object ADOConnection1: TADOConnection
      Connected = True
      ConnectionString =
        'Provider=SQLOLEDB.1;Persist Security Info=False;User ID=root;' +
        'Initial Catalog=AdventureWorks;Data Source=127.0.0.1;password=0000'
      KeepConnection = False
      LoginPrompt = False
      Mode = cmReadWrite
      Provider = 'SQLOLEDB.1'
      Left = 60
      Top = 34
    end
    object DBNavigator1: TDBNavigator
      Left = 42
      Top = 25
      Width = 240
      Height = 25
      DataSource = DataSource1
      TabOrder = 0
    end
    object ADODataSet1: TADODataSet
      Active = True
      Connection = ADOConnection1
      CursorType = ctStatic
      LockType = ltBatchOptimistic
      CommandText = 'select * from Emp'
      Parameters = <>
      Left = 60
      Top = 86
    end
    object DataSource1: TDataSource
      DataSet = ADODataSet1
      Left = 58
      Top = 136
    end
    object DBGrid1: TDBGrid
      Left = 12
      Top = 62
      Width = 379
      Height = 171
      DataSource = DataSource1
      TabOrder = 1
      TitleFont.Charset = DEFAULT_CHARSET
      TitleFont.Color = clWindowText
      TitleFont.Height = -11
      TitleFont.Name = 'Tahoma'
      TitleFont.Style = []
    end
    object TrackBar1: TTrackBar
      Left = 464
      Top = 13
      Width = 79
      Height = 32
      Max = 1
      Position = 1
      TabOrder = 2
      OnChange = TrackBar1Change
    end
    object Label1: TLabel
      Left = 406
      Top = 23
      Width = 52
      Height = 13
      Caption = 'Connected'
    end
    object CheckBox1: TCheckBox
      Left = 406
      Top = 56
      Width = 97
      Height = 17
      Caption = 'Offline Mode'
      TabOrder = 3
      OnClick = CheckBox1Click
    end
    object Button1: TButton
      Left = 410
      Top = 86
      Width = 75
      Height = 25
      Caption = 'ApplyUpdate'
      TabOrder = 4
      OnClick = Button1Click
    end
    object Button2: TButton
      Left = 410
      Top = 122
      Width = 101
      Height = 25
      Caption = 'Save Package'
      TabOrder = 5
      OnClick = Button2Click
    end
    object Button3: TButton
      Left = 410
      Top = 153
      Width = 101
      Height = 25
      Caption = 'Load Package'
      TabOrder = 6
      OnClick = Button3Click
    end

    Pas:
    unit Unit1;

    interface

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ComCtrls, Grids, DBGrids, DB, ADODB, ExtCtrls, DBCtrls;

    type
      TForm1 = class(TForm)
        ADOConnection1: TADOConnection;
        DBNavigator1: TDBNavigator;
        ADODataSet1: TADODataSet;
        DataSource1: TDataSource;
        DBGrid1: TDBGrid;
        TrackBar1: TTrackBar;
        Label1: TLabel;
        CheckBox1: TCheckBox;
        Button1: TButton;
        Button2: TButton;
        Button3: TButton;
        procedure TrackBar1Change(Sender: TObject);
        procedure CheckBox1Click(Sender: TObject);
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
        procedure Button3Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;

    var
      Form1: TForm1;

    implementation

    {$R *.dfm}

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      ADODataSet1.Connection := ADOConnection1;
      ADODataSet1.UpdateBatch;
    end;

    procedure TForm1.Button2Click(Sender: TObject);
    begin
      ADODataSet1.SaveToFile('OfflinePackage.data');
    end;

    procedure TForm1.Button3Click(Sender: TObject);
    begin
      ADODataSet1.Close();
      ADODataSet1.LoadFromFile('OfflinePackage.data');
    end;

    procedure TForm1.CheckBox1Click(Sender: TObject);
    begin
      ADODataSet1.Connection := nil;
    end;

    procedure TForm1.TrackBar1Change(Sender: TObject);
    begin
      ADOConnection1.Connected := (TrackBar1.Position = 1);
      ADODataSet1.Active       := ADOConnection1.Connected;
    end;

    end.

    成果圖:
    ADO 公事包範例

    故可從以上得知,ADO真的是很棒的架構。

    公司採用SQL Server,以及沒有跨平台需求的專案,ADO真的可以完全符合需求。

    安全性則可以透過VPN和NAT來解決。


    DataSnap好像又不是這麼必要了。是嗎?

    一山不容二虎之 EDBClient - Key violation 記事

    關聯式資料庫裡有個很基本的特性:PRIMARY KEY 條件約束。 一個資料表只能有一個 PRIMARY KEY 條件約束,並且任何加入 PRIMARY KEY 條件約束的資料行都不可接受 Null 值。因為 PRIMARY KEY 條件約束保證唯一的資料,它們通常...