又到了年底,是換工作的好時機,我想,我是有資格來說說這個議題,員工的心聲,當然我是站在我的過去來談的,一個爬到經理的工程師的心聲。
.幫員工找到位置
這不是說沒辦公桌,不太可能,那什麼叫位置?很多時候工程師都很忙,忙東忙西,若問他到底在忙什麼,他會說一大堆。若問他為什麼要忙這些東西,絕大多數都是因為,要趕案子。這個議題要說的,其實是很多主管都沒考慮到的,每一個工程師在專案中所站的位置。我這樣說肯定也有很多人否認,但是,若細細把所有專案攤開來看,應該會發現,怪了,怎麼每個人在不同的專案中的位置不一樣了。
不是說不能換位置,老實講,我最近的工作一年半內換了七次辦公座位,但是我工作的角色始終沒變。始終沒變比較好呢,還是變來變去比較好?當然各有優缺,始終沒變也許正是始終處在一個不適合的位置,變來變去也許正是找不到適合的位置。
要幫你的屬下考慮,他的位置適當嗎?什麼時候該換位置了?
2009/12/04
2009/12/02
網頁上的廣告?
前面貼了一篇關於 chrome os 的個人看法,而對 google 這家公司,相當相當多人在探討,這篇要說的也正是 google 最賺錢的產業,網頁廣告。
怎麼擴大廣告的收益,相信相當傷 google 的腦筋,這邊提供一個點子,我覺得也是大有商機。
在內嵌的廣告,不能是單向的根據關鍵字顯現,若能雙向的讓廣告根據網頁內容來「美化」網頁,相信非常有商機。
這話是怎麼說的呢?一般的廣告雖然跟關鍵字有關,但是有些則是太多文字,有些則是不相關的圖片,放在網頁上總感覺有那麼點突兀,若是廣告即網頁的一部份,甚至是不可或缺的一部份,是不是更能吸引人收看與刊登廣告呢?
怎麼擴大廣告的收益,相信相當傷 google 的腦筋,這邊提供一個點子,我覺得也是大有商機。
在內嵌的廣告,不能是單向的根據關鍵字顯現,若能雙向的讓廣告根據網頁內容來「美化」網頁,相信非常有商機。
這話是怎麼說的呢?一般的廣告雖然跟關鍵字有關,但是有些則是太多文字,有些則是不相關的圖片,放在網頁上總感覺有那麼點突兀,若是廣告即網頁的一部份,甚至是不可或缺的一部份,是不是更能吸引人收看與刊登廣告呢?
Chrome OS 之我見
有幾個趨勢是大家都可以感受到的,
智慧手機....與筆電....小筆電成長率驚人
電子紙.....軟性顯示器.....微投影機....暫成顯學
這些裝置再搭配先前 Android 一推出,現在狀況是幾乎什麼裝置都在用 Android。相信有一堆人出現既生瑜何生亮之慨?Google 為什麼已經有了 Android 之後還要推 Chrome OS? Android 與 Chrome OS 孰優孰劣?
在今年七月時回答某友人的疑問時是這麼寫的:
----------------------
不管是 google 放出的訊息,我也相信一般人目前都認定 Chrome OS 與 Android 的市場區隔明顯是前者適合大尺寸,後者適合小尺寸。
不過,若讓我們看他們的本質,都是 based on internet, based on Cloud, 事實上兩者互相影響是必然的。我相信 Android 是比較高 performance, 應用程式也會發展的比較迅速。不過 Chrome OS 擅長在 Web Application 上,整合的難度非常少,所需要的技術相對也較低。
但是 Chrome OS 有一個致命的缺點,就是離線的應用程式必須搭配 google 本身在推的 Gears, 似乎推展的不是那麼順利。也就是說,Chrome OS 要離真正的大尺寸應用,關鍵點不在應用程式數量與品質,不在整合難易,而在離線應用程式的推廣面。
我一直非常看好 google, 若你要我選擇投靠哪一邊的話,我會說短期 Android, 長期 Chrome OS。一來是 Android 成熟,二來是 Android 需要用到的技術是很多人熟悉的 Eclipse + Java。而長期來看,google 要打敗微軟,勢必是在大尺寸上決勝負,因此目前有兩條路,一個是把 Android 作成適用於大尺寸,一個是把 Chrome OS 弄成熟。前者也勢必會發生,只是那樣還是離不開 Linux, Java, 而後者最大的優勢就是可以真正跟微軟開戰,因為它開發的所有應用,都可以拿到「微軟」的平台上執行,應該說,在 Chrome OS 上開發的應用程式,都可以真正的跨平台。
所以,若想跟隨 Google 的腳步,選擇 Chrome OS 絕對對未來是有利的。
-------------------------------
半年過去了,Chrome OS 也正式推出,雖然讓很多人失望,我的看法卻沒變。純就 Google 本身來說,多種選擇多管齊下是需要的,Google 也一直這麼做。google 在多種蒐購行動中,都有一個共同點,那就是致力於本務,網路搜尋與廣告。可以想像,一家公司大到世界第一之後,剩下要做的就是不斷的擴充與創造新的商機。踏入 OS 是已經現在進行式,那麼把 OS 的版圖持續擴大也是必然的。
Android 能取代微軟嗎?我覺得不是行不行的問題。如果說 Google 能打敗微軟的利器,就是在雲端運算上把眾多的網路應用帶入人間的話,Chrome OS 有一個最大最大的優勢,那就是硬體成本可以比 Android 還便宜。
經歷過多次的金融風暴之後,成本將會是繼熱消耗之後的最大考量。其實成本一直是消費者購物的最大考量,問題是現有的 OS 若要美觀與功能夠多,就算是 embedded linux 也做不到真正便宜。而我,相信 Chrome OS 能比 Android 生產出更便宜的裝置。我相信不久的將來,使用者將會把整個網路運算帶在身邊,那時候一個 chrome OS 可能是你的手錶,可能在眼鏡上,也可能在搖控器上,更別說 1000 元手機上。
智慧手機....與筆電....小筆電成長率驚人
電子紙.....軟性顯示器.....微投影機....暫成顯學
這些裝置再搭配先前 Android 一推出,現在狀況是幾乎什麼裝置都在用 Android。相信有一堆人出現既生瑜何生亮之慨?Google 為什麼已經有了 Android 之後還要推 Chrome OS? Android 與 Chrome OS 孰優孰劣?
在今年七月時回答某友人的疑問時是這麼寫的:
----------------------
不管是 google 放出的訊息,我也相信一般人目前都認定 Chrome OS 與 Android 的市場區隔明顯是前者適合大尺寸,後者適合小尺寸。
不過,若讓我們看他們的本質,都是 based on internet, based on Cloud, 事實上兩者互相影響是必然的。我相信 Android 是比較高 performance, 應用程式也會發展的比較迅速。不過 Chrome OS 擅長在 Web Application 上,整合的難度非常少,所需要的技術相對也較低。
但是 Chrome OS 有一個致命的缺點,就是離線的應用程式必須搭配 google 本身在推的 Gears, 似乎推展的不是那麼順利。也就是說,Chrome OS 要離真正的大尺寸應用,關鍵點不在應用程式數量與品質,不在整合難易,而在離線應用程式的推廣面。
我一直非常看好 google, 若你要我選擇投靠哪一邊的話,我會說短期 Android, 長期 Chrome OS。一來是 Android 成熟,二來是 Android 需要用到的技術是很多人熟悉的 Eclipse + Java。而長期來看,google 要打敗微軟,勢必是在大尺寸上決勝負,因此目前有兩條路,一個是把 Android 作成適用於大尺寸,一個是把 Chrome OS 弄成熟。前者也勢必會發生,只是那樣還是離不開 Linux, Java, 而後者最大的優勢就是可以真正跟微軟開戰,因為它開發的所有應用,都可以拿到「微軟」的平台上執行,應該說,在 Chrome OS 上開發的應用程式,都可以真正的跨平台。
所以,若想跟隨 Google 的腳步,選擇 Chrome OS 絕對對未來是有利的。
-------------------------------
半年過去了,Chrome OS 也正式推出,雖然讓很多人失望,我的看法卻沒變。純就 Google 本身來說,多種選擇多管齊下是需要的,Google 也一直這麼做。google 在多種蒐購行動中,都有一個共同點,那就是致力於本務,網路搜尋與廣告。可以想像,一家公司大到世界第一之後,剩下要做的就是不斷的擴充與創造新的商機。踏入 OS 是已經現在進行式,那麼把 OS 的版圖持續擴大也是必然的。
Android 能取代微軟嗎?我覺得不是行不行的問題。如果說 Google 能打敗微軟的利器,就是在雲端運算上把眾多的網路應用帶入人間的話,Chrome OS 有一個最大最大的優勢,那就是硬體成本可以比 Android 還便宜。
經歷過多次的金融風暴之後,成本將會是繼熱消耗之後的最大考量。其實成本一直是消費者購物的最大考量,問題是現有的 OS 若要美觀與功能夠多,就算是 embedded linux 也做不到真正便宜。而我,相信 Chrome OS 能比 Android 生產出更便宜的裝置。我相信不久的將來,使用者將會把整個網路運算帶在身邊,那時候一個 chrome OS 可能是你的手錶,可能在眼鏡上,也可能在搖控器上,更別說 1000 元手機上。
2009/11/24
引用文件時的版權比較 CC / FDL
請見FDL 與創用 CC 授權條款之細部差異比較一文。下面僅列出原文中提到的幾條差異,文中有對兩項版權聲明的進一步說明。
一、FDL 明文規定,授權條款可視需要產生翻譯版本,但當兩者不一致時,以英文原文版本為準。而 CC 的 International 計畫,推動創用 CC 六種核心授權條款在各司法管轄領域的本地化,以我國情形為例,即是將尚未本地化 (unported) 創用 CC 授權條款翻譯成華文外,並配合我國著作權及相關法規作最小範圍的必要調整(註四)。
二、FDL 原是補足適用於自由軟體的 GNU General Public License 而來,使自由軟體相關的說明文件有「文件」適用的授權條款,但並不限於此,任何文字性作品都可適用,但是建議使用於操作說明或參考目的之作品。創用 CC 授權條款的適用客體則相對較 FDL 廣泛,任何文學或藝術創作都可適用,書籍、小手冊、甚至繪畫、攝影著作、戲劇或舞蹈著作都可依據創用 CC 授權條款提供大眾利用。
三、FDL 允許被授權人重製、散佈、改作、再授權、為商業目的使用採 FDL 授權的文件(以下簡稱本文件),另外也允許出租和公開展示(publicly display)本文件的重製物(註五)。創用 CC 「姓名標示-相同方式分享」授權條款,允許被授權人重製、散佈、修改、為商業目的使用採創用 CC 授權的著作(以下簡稱本著作)外,還允許公開演播 (publicly perform) 本著作或基於本著作而來的改用作品 (Adaptation),但禁止對本著作再授權(註六)。
此外,FDL 與創用 CC 授權條款都是全球性、免授權金、非專屬及在著作權存續期間內永久地授權。
四、FDL 規定本文件得包含免責聲明,且當有此免責聲明,該聲明將被視為FDL之一部,除此之外,FDL 無更細節的免責聲明規定。創用 CC 授權條款中則特別言明,授權人是以現狀之基礎(AS-IS)提供本著作,且未提供關於本著作之任何保證,也不對使用授權條款或本著作產生之損害負責。
五、FDL 要求被授權人不得以科技措施妨礙或控制本文件複本被閱讀或再重製。創用 CC 授權條款對被授權人使用科技措施規定則更嚴格,禁止被授權人使用任何科技措施於本著作/改用作品,而限制從其取得本著作/改用作品的接受者,行使創用 CC 授權條款所賦與他的一切權利。
六、FDL 對本文件印刷版超過 100 份的大量重製,有別於少量重製,另外要求當本文件授權聲明中要求封皮文字時,則本文件複本亦必須附上封面及封底,且清楚標示這些複本的發行散佈者 (publisher)(註七)。此外,當大量發行或散佈本文件複本超過 100 份時,FDL 允許不透明的重製格式 (0paque),但必須同時附上機器可辨讀的透明格式 (Transparent) 複本,或提供電腦網址讓公眾可下載透明格式的本文件;當選擇提供網址時,須確保此網址自被授權人最後一次散佈該不透明格式的本文件複本時起算一年是有效的。創用 CC 授權則未特別區別本著作少量或大量份數的重製。
七、FDL 允許被授權人重製及散佈修改作品的前提是,修改作品必須採用和原作品一模一樣的授權條款。創用 CC 「姓名標示-相同方式分享」 3.0 授權條款要求則相對較寬鬆,允許改用作品可採用與原著作相同的授權條款(或其後續版本),或與該授權條款具有相同授權要素的(3.0 或後續版本的)尚未本地化或任一司法管轄領域的授權條款。
八、不論是原作品複本或由原作品而來的修改作品的散佈, FDL 皆要求需標明誰是該特定版本的發行散佈者。相對地,創用 CC 授權條款則不著重於著作複本散佈者的區別及標示。
九、FDL 要求在修改作品中,除非獲得原作品作者的免除,否則須列出至少五位原作品的主要作者,且應列明該修改作品的作者。除此姓名標示目的外,原作品作者並未授權被授權人得使用其姓名作為對修改作品的背書。創用 CC 授權條款則要求,當散佈本著作、改用作品或彙編 (Collection),除非收到授權人的移除表彰的通知,否則必須表彰本著作創作者(即原始著作人)的姓名或筆名,且若原始著作人或本著作的授權人指定第三人(例如贊助機構、期刊)為姓名標示對象,則應提供之。創用 CC 授權並未如 FDL,對最低應標明之原作者人數有所要求。
十、FDL 要求修改作品中必須刪除原作品「背書」 (Endorsements) 標題的章節,但可增加對於修改作品的背書,例如該修改作品已經同儕審閱 (peer review) 的聲明。創用 CC 授權條款則規定,除非事先取得原始著作人、授權人或第三人之姓名標示對象的同意,否則被授權人不得暗示或主張,其本人或其對原著作的使用,與原始著作人、授權人或第三人之姓名標示對象間存在任何關連或有贊助或背書關係。
十一、FDL 規定,當被授權人非依授權條款所定方式使用本文件時,所獲授權將自動終止。 1.3 版新增授權恢復的規定,只要被授權人停止所有違反 FDL 規定的行為,則自某一特定著作權人得到的授權,可重新恢復。授權的恢復又分為暫時性恢復及永久恢復,只要停止違反 FDL 規定的行為,則授權即可暫時性恢復,除非被授權人所利用本文件的著作權人明確、且終局的終止對該被授權人的授權;若著作權人自被授權人停止違反 FDL 規定行為起的六十日內,未以合理方式通知該被授權人其違反規定的事實,則該被授權人將可永久恢復 FDL 的授權。此外,若被授權人是首次收到某一特定著作權人關於違反 FDL 規定的通知,且該被授權人在收到通知後的三十日內,即矯正其行為時,則 FDL 授權亦可永久恢復。創用 CC 授權條款規定,當被授權人違反任一款項,則授權條款所授予的權利將自動終止。創用 CC 授權未若 FDL 設有授權自動恢復的機制。
十二、當作品採用 FDL 時,可指定或不指定版本。若授權人指定適用某特定版本(如 1.3 版)或其後續版本,被授權人得遵循該特定版本授權條款的規定,或選擇適用該特定授權條款的後續版本;當作品未指定適用的版本時,被授權人可選擇適用自由軟體基金會曾發行過的任一版本的 FDL,包含先前或後續版本。此外, 1.3 版增設了代理人 (proxy) 機制,授權人可選擇一代理人為其決定,其以 FDL 授權的作品,未來以哪一版本釋出。當新版創用 CC 授權條款推出,並不會自動適用於採舊版授權條款的著作上,若要一著作同時適用不同版本的創用 CC 授權條款,則可依循多重授權模式釋出。此外,創用 CC 授權條款開放改用作品得採用與原著作相同的授權條款或其後續版本外,也可選擇適用與該特定授權條款具有相同授權要素的 3.0 或後續版本的尚未本地化或任一司法管轄領域的創用 CC 授權條款,但並不允許適用之前的版本(如 2.5 或 2.0 版)。
十三、對於一作品如何使用 FDL ,於授權條款最後設有附錄 (addendum) 說明之,且有提供著作權及授權聲明用語。創用 CC 授權條款對於標示方式則未另以附錄提供授權聲明參考用語,但在授權條款最後有「注意事項」,特別提到被授權人對於 Creative Commons 商標或任何與 Creative Commons 相關的商標或表徵的使用,只能是為了表明一著作依循創用 CC 授權之目的,且必須遵循 CC 當時的商標使用方針。
一、FDL 明文規定,授權條款可視需要產生翻譯版本,但當兩者不一致時,以英文原文版本為準。而 CC 的 International 計畫,推動創用 CC 六種核心授權條款在各司法管轄領域的本地化,以我國情形為例,即是將尚未本地化 (unported) 創用 CC 授權條款翻譯成華文外,並配合我國著作權及相關法規作最小範圍的必要調整(註四)。
二、FDL 原是補足適用於自由軟體的 GNU General Public License 而來,使自由軟體相關的說明文件有「文件」適用的授權條款,但並不限於此,任何文字性作品都可適用,但是建議使用於操作說明或參考目的之作品。創用 CC 授權條款的適用客體則相對較 FDL 廣泛,任何文學或藝術創作都可適用,書籍、小手冊、甚至繪畫、攝影著作、戲劇或舞蹈著作都可依據創用 CC 授權條款提供大眾利用。
三、FDL 允許被授權人重製、散佈、改作、再授權、為商業目的使用採 FDL 授權的文件(以下簡稱本文件),另外也允許出租和公開展示(publicly display)本文件的重製物(註五)。創用 CC 「姓名標示-相同方式分享」授權條款,允許被授權人重製、散佈、修改、為商業目的使用採創用 CC 授權的著作(以下簡稱本著作)外,還允許公開演播 (publicly perform) 本著作或基於本著作而來的改用作品 (Adaptation),但禁止對本著作再授權(註六)。
此外,FDL 與創用 CC 授權條款都是全球性、免授權金、非專屬及在著作權存續期間內永久地授權。
四、FDL 規定本文件得包含免責聲明,且當有此免責聲明,該聲明將被視為FDL之一部,除此之外,FDL 無更細節的免責聲明規定。創用 CC 授權條款中則特別言明,授權人是以現狀之基礎(AS-IS)提供本著作,且未提供關於本著作之任何保證,也不對使用授權條款或本著作產生之損害負責。
五、FDL 要求被授權人不得以科技措施妨礙或控制本文件複本被閱讀或再重製。創用 CC 授權條款對被授權人使用科技措施規定則更嚴格,禁止被授權人使用任何科技措施於本著作/改用作品,而限制從其取得本著作/改用作品的接受者,行使創用 CC 授權條款所賦與他的一切權利。
六、FDL 對本文件印刷版超過 100 份的大量重製,有別於少量重製,另外要求當本文件授權聲明中要求封皮文字時,則本文件複本亦必須附上封面及封底,且清楚標示這些複本的發行散佈者 (publisher)(註七)。此外,當大量發行或散佈本文件複本超過 100 份時,FDL 允許不透明的重製格式 (0paque),但必須同時附上機器可辨讀的透明格式 (Transparent) 複本,或提供電腦網址讓公眾可下載透明格式的本文件;當選擇提供網址時,須確保此網址自被授權人最後一次散佈該不透明格式的本文件複本時起算一年是有效的。創用 CC 授權則未特別區別本著作少量或大量份數的重製。
七、FDL 允許被授權人重製及散佈修改作品的前提是,修改作品必須採用和原作品一模一樣的授權條款。創用 CC 「姓名標示-相同方式分享」 3.0 授權條款要求則相對較寬鬆,允許改用作品可採用與原著作相同的授權條款(或其後續版本),或與該授權條款具有相同授權要素的(3.0 或後續版本的)尚未本地化或任一司法管轄領域的授權條款。
八、不論是原作品複本或由原作品而來的修改作品的散佈, FDL 皆要求需標明誰是該特定版本的發行散佈者。相對地,創用 CC 授權條款則不著重於著作複本散佈者的區別及標示。
九、FDL 要求在修改作品中,除非獲得原作品作者的免除,否則須列出至少五位原作品的主要作者,且應列明該修改作品的作者。除此姓名標示目的外,原作品作者並未授權被授權人得使用其姓名作為對修改作品的背書。創用 CC 授權條款則要求,當散佈本著作、改用作品或彙編 (Collection),除非收到授權人的移除表彰的通知,否則必須表彰本著作創作者(即原始著作人)的姓名或筆名,且若原始著作人或本著作的授權人指定第三人(例如贊助機構、期刊)為姓名標示對象,則應提供之。創用 CC 授權並未如 FDL,對最低應標明之原作者人數有所要求。
十、FDL 要求修改作品中必須刪除原作品「背書」 (Endorsements) 標題的章節,但可增加對於修改作品的背書,例如該修改作品已經同儕審閱 (peer review) 的聲明。創用 CC 授權條款則規定,除非事先取得原始著作人、授權人或第三人之姓名標示對象的同意,否則被授權人不得暗示或主張,其本人或其對原著作的使用,與原始著作人、授權人或第三人之姓名標示對象間存在任何關連或有贊助或背書關係。
十一、FDL 規定,當被授權人非依授權條款所定方式使用本文件時,所獲授權將自動終止。 1.3 版新增授權恢復的規定,只要被授權人停止所有違反 FDL 規定的行為,則自某一特定著作權人得到的授權,可重新恢復。授權的恢復又分為暫時性恢復及永久恢復,只要停止違反 FDL 規定的行為,則授權即可暫時性恢復,除非被授權人所利用本文件的著作權人明確、且終局的終止對該被授權人的授權;若著作權人自被授權人停止違反 FDL 規定行為起的六十日內,未以合理方式通知該被授權人其違反規定的事實,則該被授權人將可永久恢復 FDL 的授權。此外,若被授權人是首次收到某一特定著作權人關於違反 FDL 規定的通知,且該被授權人在收到通知後的三十日內,即矯正其行為時,則 FDL 授權亦可永久恢復。創用 CC 授權條款規定,當被授權人違反任一款項,則授權條款所授予的權利將自動終止。創用 CC 授權未若 FDL 設有授權自動恢復的機制。
十二、當作品採用 FDL 時,可指定或不指定版本。若授權人指定適用某特定版本(如 1.3 版)或其後續版本,被授權人得遵循該特定版本授權條款的規定,或選擇適用該特定授權條款的後續版本;當作品未指定適用的版本時,被授權人可選擇適用自由軟體基金會曾發行過的任一版本的 FDL,包含先前或後續版本。此外, 1.3 版增設了代理人 (proxy) 機制,授權人可選擇一代理人為其決定,其以 FDL 授權的作品,未來以哪一版本釋出。當新版創用 CC 授權條款推出,並不會自動適用於採舊版授權條款的著作上,若要一著作同時適用不同版本的創用 CC 授權條款,則可依循多重授權模式釋出。此外,創用 CC 授權條款開放改用作品得採用與原著作相同的授權條款或其後續版本外,也可選擇適用與該特定授權條款具有相同授權要素的 3.0 或後續版本的尚未本地化或任一司法管轄領域的創用 CC 授權條款,但並不允許適用之前的版本(如 2.5 或 2.0 版)。
十三、對於一作品如何使用 FDL ,於授權條款最後設有附錄 (addendum) 說明之,且有提供著作權及授權聲明用語。創用 CC 授權條款對於標示方式則未另以附錄提供授權聲明參考用語,但在授權條款最後有「注意事項」,特別提到被授權人對於 Creative Commons 商標或任何與 Creative Commons 相關的商標或表徵的使用,只能是為了表明一著作依循創用 CC 授權之目的,且必須遵循 CC 當時的商標使用方針。
2009/10/12
Google GData API
請參考Google Data API 概觀。我另外寫了一篇在 Windows .Net 上使用 GData API
很多網頁應用程式都提供 RSS, ATOM 服務來提供方便的資料查詢與處理,最常見的是 News, Blog, Youtube, 或是網路相簿等等。想像一下,你在自己的應用程式利用 blog 來寫日記,不必用瀏覽器登入到 blog 而是用專屬的應用程式,除了登入外,張貼文章、瀏覽清單、刪除文章都是必要的動作。而這個需求可以透過 GData API 來完成,是一種建立在 HTTP 通訊協定上的資料操作,例如透過 Get, Put 等等來存取資料。
也就是說,只要網站提供 RSS, ATOM 等服務,就可以透過 GData API 來存取,這樣不止可以把網路服務嵌入到你的應用程式中,也可以整合不同的服務。
GData 通訊協定是中性的編程語言;您可以利用可發佈 HTTP 要求及解析 XML 要求的任何編程語言,來編寫用戶端。這也是在說明我上面講的應用程式不限網頁,也可以是 C, C++, C#, Python....or others..
底下是清單:
也就是說,若你想透過分析 HTTP request/response 來使用 GData 的話,那大概就不受程式語言限制,若想直接使用 Google 提供的 GData 函式庫的話,底下是其支援清單:
Java, .Net, PHP, Python, Object-C, Javascript
很多網頁應用程式都提供 RSS, ATOM 服務來提供方便的資料查詢與處理,最常見的是 News, Blog, Youtube, 或是網路相簿等等。想像一下,你在自己的應用程式利用 blog 來寫日記,不必用瀏覽器登入到 blog 而是用專屬的應用程式,除了登入外,張貼文章、瀏覽清單、刪除文章都是必要的動作。而這個需求可以透過 GData API 來完成,是一種建立在 HTTP 通訊協定上的資料操作,例如透過 Get, Put 等等來存取資料。
也就是說,只要網站提供 RSS, ATOM 等服務,就可以透過 GData API 來存取,這樣不止可以把網路服務嵌入到你的應用程式中,也可以整合不同的服務。
GData 通訊協定是中性的編程語言;您可以利用可發佈 HTTP 要求及解析 XML 要求的任何編程語言,來編寫用戶端。這也是在說明我上面講的應用程式不限網頁,也可以是 C, C++, C#, Python....or others..
底下是清單:
| 功能 | GData | Atom | RSS 2.0 |
| 聯合發佈格式 | Y | Y | Y |
| 查詢 | Y | N | N |
| 更新 | Y | Y | N |
| 開放式並行處理 | Y | N | N |
| 驗證 | Y | N | N |
也就是說,若你想透過分析 HTTP request/response 來使用 GData 的話,那大概就不受程式語言限制,若想直接使用 Google 提供的 GData 函式庫的話,底下是其支援清單:
Java, .Net, PHP, Python, Object-C, Javascript
Android 應用程式架構
請參考Android Application Framework說明。
Android 為了讓開發者可以儘量重複使用相同的元件,整個開發函式庫非常詳細,從上層來看應用程式的框架包括幾個部份,這些都與系統應用程式使用相同的元件,因此開發者可以很容易開發出具有特色的應用程式來取代之:
分析所有的應用程式都包含一系列的服務與系統,包括:
* 一個豐富而且可以擴充的 Views(視覺化),用來建立應用程式的操作介面,例如清單(lists), 格子(grids), 文字區塊(text boxes), 按鈕(buttons), 甚至嵌入一個瀏覽器
* 內容供應,用來讓應用程式存取,與其他應用程式互相溝通,或是與他人分享。
* 資源管理, 例如訊息內容,多國語言,操作介面的圖片,甚至是操作介面的定義資訊
* 提示管理,讓所有應用程式可以在共用的狀態列顯示客製化的訊息
* 活動管理,這邊的活動指的就是各個應用程式所進行的工作,例如回上一頁,或是熱鍵,或是關閉等等。
Android 為了讓開發者可以儘量重複使用相同的元件,整個開發函式庫非常詳細,從上層來看應用程式的框架包括幾個部份,這些都與系統應用程式使用相同的元件,因此開發者可以很容易開發出具有特色的應用程式來取代之:
分析所有的應用程式都包含一系列的服務與系統,包括:
* 一個豐富而且可以擴充的 Views(視覺化),用來建立應用程式的操作介面,例如清單(lists), 格子(grids), 文字區塊(text boxes), 按鈕(buttons), 甚至嵌入一個瀏覽器
* 內容供應,用來讓應用程式存取,與其他應用程式互相溝通,或是與他人分享。
* 資源管理, 例如訊息內容,多國語言,操作介面的圖片,甚至是操作介面的定義資訊
* 提示管理,讓所有應用程式可以在共用的狀態列顯示客製化的訊息
* 活動管理,這邊的活動指的就是各個應用程式所進行的工作,例如回上一頁,或是熱鍵,或是關閉等等。
幾個重要的 Google APIs
有空的話,或許會把下列清單變成一篇篇的心得....先看著就好
========================================================
資料儲存與交換 ——Google Data APIs
使用者認證 ——Google Account Authentication API
多國語言 ——Google AJAX Language API
善用雲端運算 ——Google App Engine
圖表繪製元件 ——Google Chart API
視覺化設計 ——Google Visualization API
行事曆 ——Google Calendar API
試算表 ——Google Spreadsheets API
iGoogle主題設計 ——iGoogle Themes API
Google小工具開發 ——Google Gadgets API
桌面小工具 ——Google Desktop Gadget API
即時新聞 ——Google AJAX Feed API
社交 ——OpenSocial
社交圖譜 ——Google Social Graph API
好友清單 ——Google Contacts Data API
相簿 ——Picasa Web Albums Data API
影片 ——Youtube Data API
部落格 ——Blogger Data API
即時通訊 ——Google Talk XMPP
數據格式定義 ——Google Protocol Buffer API
為網站豐富AJAX內容——Google AJAX Libraries API
用戶廣告管理 ——Google Adwords API
廣告整合 ——Google AdSense API
線上付款 ——Google Checkout API
圖書搜尋 ——Google Book Search Book
嵌入Google搜尋 ——Google AJAX Search API
地圖服務 ——Google Maps API概述
地圖信息服務工具 ——Google Mapplets
地理標記語言 ——KML
靜態地圖服務 ——Google Static Maps API
讓應用支持桌面搜索——Google Desktop Search API
與線上文檔互動 ——Google Document List API
離線瀏覽 ——Google Gears
線上筆記 ——Google Notebook Data API
========================================================
資料儲存與交換 ——Google Data APIs
使用者認證 ——Google Account Authentication API
多國語言 ——Google AJAX Language API
善用雲端運算 ——Google App Engine
圖表繪製元件 ——Google Chart API
視覺化設計 ——Google Visualization API
行事曆 ——Google Calendar API
試算表 ——Google Spreadsheets API
iGoogle主題設計 ——iGoogle Themes API
Google小工具開發 ——Google Gadgets API
桌面小工具 ——Google Desktop Gadget API
即時新聞 ——Google AJAX Feed API
社交 ——OpenSocial
社交圖譜 ——Google Social Graph API
好友清單 ——Google Contacts Data API
相簿 ——Picasa Web Albums Data API
影片 ——Youtube Data API
部落格 ——Blogger Data API
即時通訊 ——Google Talk XMPP
數據格式定義 ——Google Protocol Buffer API
為網站豐富AJAX內容——Google AJAX Libraries API
用戶廣告管理 ——Google Adwords API
廣告整合 ——Google AdSense API
線上付款 ——Google Checkout API
圖書搜尋 ——Google Book Search Book
嵌入Google搜尋 ——Google AJAX Search API
地圖服務 ——Google Maps API概述
地圖信息服務工具 ——Google Mapplets
地理標記語言 ——KML
靜態地圖服務 ——Google Static Maps API
讓應用支持桌面搜索——Google Desktop Search API
與線上文檔互動 ——Google Document List API
離線瀏覽 ——Google Gears
線上筆記 ——Google Notebook Data API
2009/08/12
Class, Abstract Class, and Interface
最近在研究用 C# 寫 Plugin, 為了 plugin 寫了二篇文章,分別是Class Interface, 及 Plugins。
這邊就有個問題,一般我們在寫程式,大概都只會用到類別 "Class", 書上講的抽象類別(Abstract Class)似乎很少用到,更別提界面(interface)。不過在用 Plugin 時,一直都是用界面(Interface), 為什麼呢?若看得懂英文的,請參考Abstract Class vs Interface一文。
.抽象類別: 抽象類別其實非常像一般的類別,可以擁有 Private 的成員及方法,當然不同的地方就在於它也可以擁有 Abstract 的成員及方法。
.界面: 可以簡單說,界面是更抽象的類別,只能擁有 Public 及 Abstract 的成員及方法。要換一種說法就是,界面不必宣告,一切都當成 public && abstract,而且而且,因為是抽象的,所以都不能實作。
用下面的範例來看會比較容易明白:
文中提到一種使用時機,若繼承關係中,基礎類別有可能被用來產生實例的話,當然就只能用一般類別,但是若不會發生這種可能,用抽象類別會是比較好的。至於界面,則是這個基礎類別若有「預設行為」的話只能用抽象類別,否則它就只是個界面。
寫到這兒,也大概了解為何 Plugin 只能用界面了,因為,不可能為 Plugin 給什麼預設行為不是?
讓我們從抽象類別與界面的字面意義來思考,抽象類別畢竟是類別,只是它是抽象的,所以在繼承中需要被「overlay」,就像看到「筆」這個抽象概念時,你會知道可以寫,而且不管哪種筆的寫都「差不多」一樣。可是界面之所以是界面,就是因為你可以預期它要提供的功能是什麼,可是界面只停留在界面,怎麼實作是沒個準頭的,要實際繼承的人去實作。而之所以被稱為界面,也限定了繼承的人一定要「全部」實作。
這邊就有個問題,一般我們在寫程式,大概都只會用到類別 "Class", 書上講的抽象類別(Abstract Class)似乎很少用到,更別提界面(interface)。不過在用 Plugin 時,一直都是用界面(Interface), 為什麼呢?若看得懂英文的,請參考Abstract Class vs Interface一文。
.抽象類別: 抽象類別其實非常像一般的類別,可以擁有 Private 的成員及方法,當然不同的地方就在於它也可以擁有 Abstract 的成員及方法。
.界面: 可以簡單說,界面是更抽象的類別,只能擁有 Public 及 Abstract 的成員及方法。要換一種說法就是,界面不必宣告,一切都當成 public && abstract,而且而且,因為是抽象的,所以都不能實作。
用下面的範例來看會比較容易明白:
//Abstarct Class
public abstract class Vehicles
{
private int noOfWheel;
private string color;
public abstract string Engine
{
get;
set;
}
public abstract void Accelerator();
}
//Interface
public interface Vehicles
{
string Engine
{
get;
set;
}
void Accelerator();
}
文中提到一種使用時機,若繼承關係中,基礎類別有可能被用來產生實例的話,當然就只能用一般類別,但是若不會發生這種可能,用抽象類別會是比較好的。至於界面,則是這個基礎類別若有「預設行為」的話只能用抽象類別,否則它就只是個界面。
寫到這兒,也大概了解為何 Plugin 只能用界面了,因為,不可能為 Plugin 給什麼預設行為不是?
讓我們從抽象類別與界面的字面意義來思考,抽象類別畢竟是類別,只是它是抽象的,所以在繼承中需要被「overlay」,就像看到「筆」這個抽象概念時,你會知道可以寫,而且不管哪種筆的寫都「差不多」一樣。可是界面之所以是界面,就是因為你可以預期它要提供的功能是什麼,可是界面只停留在界面,怎麼實作是沒個準頭的,要實際繼承的人去實作。而之所以被稱為界面,也限定了繼承的人一定要「全部」實作。
2009/08/10
主開機磁區(MBR)被 grub/lilo 蓋掉了怎麼回覆?
這問題一直都存在,早期的文獻(好像我也貼過)都說用 fdisk /mbr 來修復,不過自從檔案系統用 NTFS 之後,此一工具也不見了。我的狀況是電腦裝的是 windows 7, 後來用 Linux 要將它安裝到我的隨身碟,不知道什麼時候做了蠢事,要用原來的方式從硬碟開機時發現停在不正常的 grub 上,因為此時隨身碟不見了。
好吧,至少原來用 grub 開機的方式我辦得到,也就是說,我讓系統停在 grub shell 上,然後用傳統的方式來試著開機進 Windows,因為我是用隨身碟,此時硬碟變成 (hd1,0):
root (hd1,0)
rootnoverify (hd1,0)
chainloader +1
makeactive
boot
注意哦,就算進 windows 7(我相信 vista 也一樣)也找不到 fdisk, 所以就先停在 Windows 開機選單畫面,注意看的話可以看到 "F8" 修復的字樣,就給它用力按 F8 吧。
上面的方式也許有人不知道怎樣進去那個 F8 畫面,也可以拿原版光碟來,裡面會有相同的畫面。
好了,假設你在系統的 F8 畫面,請選擇 Command Window 吧,然後用下面的命令:
bootsect /nt60 c: /mbr /force
上面的 C: 是因為我用 F8 以硬碟開機的方式進去系統,相信若是用安裝光碟的話不是 c:,請自行修改。
最後,要理解二件事:
一、前面的 grub 設定方式可以讓你從硬碟開機進之前安裝的 Windows 7/Vista, 但是卻尚未修改開機設定,因此下次若用純硬碟開機還是會停在不完全的 Grub 選單中。
二、為何要按 F8? 因為 bootsect 不在正常的 Windows 7 中,只存在 Windows 7 的修復控制台中,上面說的就是透過按 F8 或是用安裝光碟來進到修復控制台。
好吧,至少原來用 grub 開機的方式我辦得到,也就是說,我讓系統停在 grub shell 上,然後用傳統的方式來試著開機進 Windows,因為我是用隨身碟,此時硬碟變成 (hd1,0):
root (hd1,0)
rootnoverify (hd1,0)
chainloader +1
makeactive
boot
注意哦,就算進 windows 7(我相信 vista 也一樣)也找不到 fdisk, 所以就先停在 Windows 開機選單畫面,注意看的話可以看到 "F8" 修復的字樣,就給它用力按 F8 吧。
上面的方式也許有人不知道怎樣進去那個 F8 畫面,也可以拿原版光碟來,裡面會有相同的畫面。
好了,假設你在系統的 F8 畫面,請選擇 Command Window 吧,然後用下面的命令:
bootsect /nt60 c: /mbr /force
上面的 C: 是因為我用 F8 以硬碟開機的方式進去系統,相信若是用安裝光碟的話不是 c:,請自行修改。
最後,要理解二件事:
一、前面的 grub 設定方式可以讓你從硬碟開機進之前安裝的 Windows 7/Vista, 但是卻尚未修改開機設定,因此下次若用純硬碟開機還是會停在不完全的 Grub 選單中。
二、為何要按 F8? 因為 bootsect 不在正常的 Windows 7 中,只存在 Windows 7 的修復控制台中,上面說的就是透過按 F8 或是用安裝光碟來進到修復控制台。
2009/08/04
Data Binding
本文主要來自Piping Value Converters一文,有興趣的人也可以直接看MSDN Data Binding Overview。
先來看看該文範例執行時的畫面如下:

而用來 Data Binding 的資料如下:
問題:
根據所提供的資料,如何把它依某一個屬性(範例中是用 Status)轉換成不同的 UI 元素?以此例除了 Pending, Complete, Active 外,還有顏色。
說明:
資料在繫結時(Data Binding),若有設定 Converter 屬性的話會先經過 IValueConverter型別的 Converter 方法轉換,對單一的轉換就是這麼簡單,可是若要多重轉換時怎麼處理才好呢?該文撰寫了一個繼承自 IValueConverter 的 ValueConverterGroup 來達成此目標。在實作上, ValueConverterGroup 可以擁有多個 Converters, 在呼叫 Converter 時,會依序呼叫。比較特別需要注意的是,前一個 converter 的輸出會當成下一個 converter 的輸入,直到最後一個 converter 的輸出被當成 target 為止。
由 ValueConverterGroup 實例來看問題:
範例中定義下面三個 ValueConverterGroup 實例(Instance), 關於 ValueConverterGroup 的定義則見後面的說明。重溫前面的問題,我們只有一個 Status 屬性,可是要做三件事,一是將數值型態的 Status 轉換成易讀的 Pending, Complete, Active,一是不同的 Status 以不同的顏色來顯示,三是對不同的 Status 顯示不同的 Tooltip.
從上面的實例可以看到三個 Key 值分別是 statusDisplayNameGroup, statusForegroundGroup, statusDescriptionGroup,使用上可以由 <DataTemplate> 來引用,範例如下,剛好可以看到三個 Converter 分別引用了這三個 Key 值:
有興趣的人也可以分析上面的 來看最後 GUI 所呈現的每一筆資訊都由 @Name, (, @Status, ) 四個部份組成,運用上將 Status 用三個 Converter 來轉換成需要的元素即達成目的。
ValueConverterGroup 的定義:
這邊我只列出 Convert 的定義:
上面定義被標示成紅色斜線的部份,也就是上面在說明一節提到的「前一個 converter 的輸出會當成下一個 converter 的輸入」的實作方式。順帶一提的是,在 Data Binding 時對錯誤資料的處理可以透過 Binding.DoNothing 來終止繫結。
本文主要來自Piping Value Converters一文,有興趣的人也可以直接看MSDN Data Binding Overview。
先來看看該文範例執行時的畫面如下:

而用來 Data Binding 的資料如下:
<?xml version="1.0" encoding="utf-8" ?>
<Tasks>
<Task Name="Paint the living room" Status="0" />
<Task Name="Wash the floor" Status="-1" />
<Task Name="Study WPF" Status="1" />
<Task Name="Rebuild kernel" Status="-1" />
<Task Name="Kernel Install" Status="0" />
<Task Name="Rebuild Driver" Status="1" />
</Tasks>
問題:
根據所提供的資料,如何把它依某一個屬性(範例中是用 Status)轉換成不同的 UI 元素?以此例除了 Pending, Complete, Active 外,還有顏色。
說明:
資料在繫結時(Data Binding),若有設定 Converter 屬性的話會先經過 IValueConverter型別的 Converter 方法轉換,對單一的轉換就是這麼簡單,可是若要多重轉換時怎麼處理才好呢?該文撰寫了一個繼承自 IValueConverter 的 ValueConverterGroup 來達成此目標。在實作上, ValueConverterGroup 可以擁有多個 Converters, 在呼叫 Converter 時,會依序呼叫。比較特別需要注意的是,前一個 converter 的輸出會當成下一個 converter 的輸入,直到最後一個 converter 的輸出被當成 target 為止。
由 ValueConverterGroup 實例來看問題:
範例中定義下面三個 ValueConverterGroup 實例(Instance), 關於 ValueConverterGroup 的定義則見後面的說明。重溫前面的問題,我們只有一個 Status 屬性,可是要做三件事,一是將數值型態的 Status 轉換成易讀的 Pending, Complete, Active,一是不同的 Status 以不同的顏色來顯示,三是對不同的 Status 顯示不同的 Tooltip.
<!-- 第一部份: 將 Status 屬性值轉換成像 Pending, Complete, Active 這樣的字串以便顯示在 GUI 上 -->
<local:ValueConverterGroup x:Key="statusDisplayNameGroup">
<local:IntegerStringToProcessingStateConverter />
<local:EnumToDisplayNameConverter />
</local:ValueConverterGroup>
<!-- 第二部份: 根據 Status 屬性值轉換成不同的顏色,以便反應不同的狀態 -->
<local:ValueConverterGroup x:Key="statusForegroundGroup">
<local:IntegerStringToProcessingStateConverter />
<local:ProcessingStateToColorConverter />
<local:ColorToSolidColorBrushConverter />
</local:ValueConverterGroup>
<!-- 第三部份: 根據 Status 屬性值轉換成不同的 Tooltip -->
<local:ValueConverterGroup x:Key="statusDescriptionGroup">
<local:XmlAttributeToStringStateConverter />
<local:IntegerStringToProcessingStateConverter />
<local:EnumToDescriptionConverter />
</local:ValueConverterGroup>
從上面的實例可以看到三個 Key 值分別是 statusDisplayNameGroup, statusForegroundGroup, statusDescriptionGroup,使用上可以由 <DataTemplate> 來引用,範例如下,剛好可以看到三個 Converter 分別引用了這三個 Key 值:
<DataTemplate x:Key="taskItemTemplate">
<StackPanel
Margin="2"
Orientation="Horizontal"
ToolTip="{Binding XPath=@Status, Converter={StaticResource statusDescriptionGroup}}"
>
<TextBlock Text="{Binding XPath=@Name}" />
<TextBlock Text=" (" xml:space="preserve" />
<TextBlock
Text="{Binding XPath=@Status, Converter={StaticResource statusDisplayNameGroup}}"
Foreground="{Binding XPath=@Status, Converter={StaticResource statusForegroundGroup}}" />
<TextBlock Text=")" />
</StackPanel>
</DataTemplate>
有興趣的人也可以分析上面的
ValueConverterGroup 的定義:
這邊我只列出 Convert 的定義:
object IValueConverter.Convert( object value, Type targetType, object parameter, CultureInfo culture )
{
object output = value;
for( int i = 0; i < this.Converters.Count; ++i )
{
IValueConverter converter = this.Converters[i];
Type currentTargetType = this.GetTargetType( i, targetType, true );
output = converter.Convert( output, currentTargetType, parameter, culture );
// If the converter returns 'DoNothing' then the binding operation should terminate.
if( output == Binding.DoNothing )
break;
}
return output;
}
上面定義被標示成紅色斜線的部份,也就是上面在說明一節提到的「前一個 converter 的輸出會當成下一個 converter 的輸入」的實作方式。順帶一提的是,在 Data Binding 時對錯誤資料的處理可以透過 Binding.DoNothing 來終止繫結。
2009/08/03
Picasaweb Viewer
請參考Picasaweb Viewer
這個範例讓視窗的寬、高隨著內容調整大小,其作法是在 xaml 的<window> 裡面設定 SizeToContent="WidthAndHeight"
整個視窗分成左右兩部份,左邊是取自 http://picasaweb.google.com/data/feed/api/user/USER_NAME?kind=album 的RSS feed 列表,右邊則是照片列表。因為都是清單,因此需要將新專案的 <Grid> 拿掉,放兩個 <ListBox> 上去。
Resource
在繼續修改之前,可以先將 Resource 設定好,其內容如下:
<DockPanel.Resources >
<XmlNamespaceMappingCollection x:Key="mapping">
<XmlNamespaceMapping Uri="http://www.w3.org/2005/Atom" Prefix="default"/>
<XmlNamespaceMapping Uri="http://schemas.google.com/photos/2007" Prefix="gphoto"/>
</XmlNamespaceMappingCollection>
<XmlDataProvider x:Key="Picasa" XmlNamespaceManager="{StaticResource mapping}" Source="http://picasaweb.google.com/data/feed/api/user/USER_NAME?kind=album">
</XmlDataProvider>
</DockPanel.Resources>
左半部「相簿清單」
可以注意到這邊會採用 XmlDataProvider 來做 Data Binding, 而這正是左半部的 ListBox 的資料來源,因此我們來看看左半部怎麼寫的:
<ListBox DockPanel.Dock="Left" SelectionChanged="OnSelctionChanged" DataContext="{StaticResource Picasa}" ItemsSource="{Binding XPath=default:feed/default:entry/default:title}">
</ListBox>
在 DataContext 設定其值為 {StaticResource Picasa},這樣就可以把資料 Binding 好。同時請注意到 SelectionChanged 被設定成 OnSelctionChanged,如此一來就會在左邊的「項目」被點選而改變時,會呼叫 OnSelctionChanged() 來改變右邊的圖片清單。
右半部「照片清單」
至於右邊的清單寫法清微複雜,可以分成三部份來讀:
DataContext
<ListBox.DataContext>
<XmlDataProvider XmlNamespaceManager="{StaticResource mapping}">
</XmlDataProvider>
</ListBox.DataContext>
可以注意到右半部的 DataContext 與左半部有相同的 XmlNamespaceManager, 也許你一下子沒看出來,不過可以從左半部的 DataContext 使用 Picasa 這個資源就可以追出來。而最重要的 ItemSource 屬性則必須靠使用者點選左半部來改變,因此在 OnSelectionChanged() 中我們可以看到其定義,先讓我們瞧瞧整個事件的定義:
public void OnSelctionChanged(Object source, RoutedEventArgs args)
{
ListBox lb = args.Source as ListBox;
string simplestr;
XmlDataProvider provider = MyListBox.DataContext as XmlDataProvider;
if (provider != null)
{
simplestr = lb.SelectedValue.ToString().Substring(0,lb.SelectedValue.ToString().IndexOf('-'));
simplestr = simplestr.Replace(" ", "");
simplestr = simplestr.Replace(",", "");
provider.Source = new Uri(@"http://picasaweb.google.com/data/feed/api/user/wade.fs/album/" + simplestr + "?kind=photo");
}
}
透過 provider.Source 的設值來動態改變 ListBox 的 ItemSource 屬性。
ItemTemplate
那麼我們如何設定每個清單是什麼樣的元素?可以透過 ListBox 的 ItemTemplate 屬性來設定,範例如下:
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding XPath=@src}" Width="300"></Image>
</DataTemplate>
</ListBox.ItemTemplate>
而照片的網址則來自 RSS <content> 項目裡面的 "@src" 來指定,可以從 XPath 知道此項關聯。這邊又可以見到一項 Data Binding 的用法。
ItemsPanel
最後,照片清單的顯示若太多,常常會發生把視窗擠的變形到無法接受,若我們要採用比較像瀏覽器的方式的話,可以透過 ItemsPanel 來設定其 Layout, 範例如下:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
Proxy Server
若您的環境是透過 Proxy Server 的話,又該如何在 WPF 中使用呢?可以在執行檔所在地產生一個 EXECUTE_FILE.exe.config,其內容很簡單,我們讓它使用與 IE 相同的 Proxy Server 設定值,範例如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.net>
<defaultProxy useDefaultCredentials="true" enabled="true"></defaultProxy>
</system.net>
</configuration>
這個範例讓視窗的寬、高隨著內容調整大小,其作法是在 xaml 的<window> 裡面設定 SizeToContent="WidthAndHeight"
整個視窗分成左右兩部份,左邊是取自 http://picasaweb.google.com/data/feed/api/user/USER_NAME?kind=album 的RSS feed 列表,右邊則是照片列表。因為都是清單,因此需要將新專案的 <Grid> 拿掉,放兩個 <ListBox> 上去。
Resource
在繼續修改之前,可以先將 Resource 設定好,其內容如下:
<DockPanel.Resources >
<XmlNamespaceMappingCollection x:Key="mapping">
<XmlNamespaceMapping Uri="http://www.w3.org/2005/Atom" Prefix="default"/>
<XmlNamespaceMapping Uri="http://schemas.google.com/photos/2007" Prefix="gphoto"/>
</XmlNamespaceMappingCollection>
<XmlDataProvider x:Key="Picasa" XmlNamespaceManager="{StaticResource mapping}" Source="http://picasaweb.google.com/data/feed/api/user/USER_NAME?kind=album">
</XmlDataProvider>
</DockPanel.Resources>
左半部「相簿清單」
可以注意到這邊會採用 XmlDataProvider 來做 Data Binding, 而這正是左半部的 ListBox 的資料來源,因此我們來看看左半部怎麼寫的:
<ListBox DockPanel.Dock="Left" SelectionChanged="OnSelctionChanged" DataContext="{StaticResource Picasa}" ItemsSource="{Binding XPath=default:feed/default:entry/default:title}">
</ListBox>
在 DataContext 設定其值為 {StaticResource Picasa},這樣就可以把資料 Binding 好。同時請注意到 SelectionChanged 被設定成 OnSelctionChanged,如此一來就會在左邊的「項目」被點選而改變時,會呼叫 OnSelctionChanged() 來改變右邊的圖片清單。
右半部「照片清單」
至於右邊的清單寫法清微複雜,可以分成三部份來讀:
DataContext
<ListBox.DataContext>
<XmlDataProvider XmlNamespaceManager="{StaticResource mapping}">
</XmlDataProvider>
</ListBox.DataContext>
可以注意到右半部的 DataContext 與左半部有相同的 XmlNamespaceManager, 也許你一下子沒看出來,不過可以從左半部的 DataContext 使用 Picasa 這個資源就可以追出來。而最重要的 ItemSource 屬性則必須靠使用者點選左半部來改變,因此在 OnSelectionChanged() 中我們可以看到其定義,先讓我們瞧瞧整個事件的定義:
public void OnSelctionChanged(Object source, RoutedEventArgs args)
{
ListBox lb = args.Source as ListBox;
string simplestr;
XmlDataProvider provider = MyListBox.DataContext as XmlDataProvider;
if (provider != null)
{
simplestr = lb.SelectedValue.ToString().Substring(0,lb.SelectedValue.ToString().IndexOf('-'));
simplestr = simplestr.Replace(" ", "");
simplestr = simplestr.Replace(",", "");
provider.Source = new Uri(@"http://picasaweb.google.com/data/feed/api/user/wade.fs/album/" + simplestr + "?kind=photo");
}
}
透過 provider.Source 的設值來動態改變 ListBox 的 ItemSource 屬性。
ItemTemplate
那麼我們如何設定每個清單是什麼樣的元素?可以透過 ListBox 的 ItemTemplate 屬性來設定,範例如下:
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding XPath=@src}" Width="300"></Image>
</DataTemplate>
</ListBox.ItemTemplate>
而照片的網址則來自 RSS <content> 項目裡面的 "@src" 來指定,可以從 XPath 知道此項關聯。這邊又可以見到一項 Data Binding 的用法。
ItemsPanel
最後,照片清單的顯示若太多,常常會發生把視窗擠的變形到無法接受,若我們要採用比較像瀏覽器的方式的話,可以透過 ItemsPanel 來設定其 Layout, 範例如下:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
Proxy Server
若您的環境是透過 Proxy Server 的話,又該如何在 WPF 中使用呢?可以在執行檔所在地產生一個 EXECUTE_FILE.exe.config,其內容很簡單,我們讓它使用與 IE 相同的 Proxy Server 設定值,範例如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.net>
<defaultProxy useDefaultCredentials="true" enabled="true"></defaultProxy>
</system.net>
</configuration>
2009/07/31
P2P by using WCF
P2P by using WCF
這是一個用C# 寫的應用,主要是練習 P2P,使用的是 .NET 3.0 推出的 WCF,可以參考 A simple peer to peer chat application using WCF netPeerTcpBinding。若對開啟 C# 應用有興趣的人,可以參考 Button Controls。P2P 可以想成一個應用程式本身具有 Client 及 Server。
Server
在 Server 端需要額外引入三個 namespace:
完整程式碼如下:
很簡單,若真的要說程式片斷就在前面的六行:
cprs 是 CustomPeerResolverService 物件,用來當 ServiceHost 物件的輸入參數,主要會讀取一個設定檔(App.config), 並且設定連線資訊更新時間是 5ms。
host 是 ServiceHost 物件,並設定 timeout 時間為永不停止(1000000 天實在是非常久的時間)。然後....就這樣.....
CONFIG
至於設定檔,內容如下:
Client
Client 是稍微複雜了點,先貼一下原始程式碼如下,只需要用到兩個 namespace(System.ServiceModel, 及System.ServiceModel.Channels):
雖然小小長了點,可以看到 interface 中只有定義了三個動作:
在繼續看這份 souce code 之前,先來讀一下設定檔:
Config
對應到前面講的 Join, Leave, SendMessage 三個介面,需要三個事件來配合:
Event 的使用在程式中如下:
聊天室功能裡面,最重要的就是聊天,對,有點廢話,不過難就難在網路連線的維護,幸好都被 WCF 做掉了,在程式裡面是透過 DuplexChannelFactory() 物件來達成:
若我們看看幾個動作即可明白中間的關聯:
channel.Join(this.userName);
channel.SendMessage(this.userName, txtSendMessage.Text.Trim());
channel.Leave(this.userName);
channel.Close();
好了,就這樣。什麼?沒講完?對,聰明的你一定有發現,好像沒指出收訊息的機制?呵,其實是沒特別講清楚而已。
訊息收送
收: MessageSent += new UserSendMessage(ChatClient_MessageSent);
送: channel.SendMessage(this.userName, txtSendMessage.Text.Trim());
這是一個用C# 寫的應用,主要是練習 P2P,使用的是 .NET 3.0 推出的 WCF,可以參考 A simple peer to peer chat application using WCF netPeerTcpBinding。若對開啟 C# 應用有興趣的人,可以參考 Button Controls。P2P 可以想成一個應用程式本身具有 Client 及 Server。
Server
在 Server 端需要額外引入三個 namespace:
* System.ServiceModel;
* System.ServiceModel.Channels;
* System.ServiceModel.PeerResolvers;
完整程式碼如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.PeerResolvers;
namespace ChatServer
{
public partial class ChatServer : Form
{
private CustomPeerResolverService cprs;
private ServiceHost host;
public ChatServer()
{
InitializeComponent();
btnStop.Enabled = false;
}
private void btnStart_Click(object sender, EventArgs e)
{
try
{
cprs = new CustomPeerResolverService();
cprs.RefreshInterval = TimeSpan.FromSeconds(5);
host = new ServiceHost(cprs);
cprs.ControlShape = true;
cprs.Open();
host.Open(TimeSpan.FromDays(1000000));
lblMessage.Text = "Server started successfully.";
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
btnStart.Enabled = false;
btnStop.Enabled = true;
}
}
private void btnStop_Click(object sender, EventArgs e)
{
try
{
cprs.Close();
host.Close();
lblMessage.Text = "Server stopped successfully.";
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
btnStart.Enabled = true;
btnStop.Enabled = false;
}
}
}
}
很簡單,若真的要說程式片斷就在前面的六行:
cprs = new CustomPeerResolverService();
cprs.RefreshInterval = TimeSpan.FromSeconds(5);
host = new ServiceHost(cprs);
cprs.ControlShape = true;
cprs.Open();
host.Open(TimeSpan.FromDays(1000000));
cprs 是 CustomPeerResolverService 物件,用來當 ServiceHost 物件的輸入參數,主要會讀取一個設定檔(App.config), 並且設定連線資訊更新時間是 5ms。
host 是 ServiceHost 物件,並設定 timeout 時間為永不停止(1000000 天實在是非常久的時間)。然後....就這樣.....
CONFIG
至於設定檔,內容如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="System.ServiceModel.PeerResolvers.CustomPeerResolverService">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost/ChatServer"/>
</baseAddresses>
</host>
<endpoint address="net.tcp://localhost/ChatServer" binding="netTcpBinding"
bindingConfiguration="TcpConfig"
contract="System.ServiceModel.PeerResolvers.IPeerResolverContract">
</endpoint>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="TcpConfig">
<security mode="None"></security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
Client
Client 是稍微複雜了點,先貼一下原始程式碼如下,只需要用到兩個 namespace(System.ServiceModel, 及System.ServiceModel.Channels):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace ChatClient
{
[ServiceContract(CallbackContract = typeof(IChatService))]
public interface IChatService
{
[OperationContract(IsOneWay = true)]
void Join(string memberName);
[OperationContract(IsOneWay = true)]
void Leave(string memberName);
[OperationContract(IsOneWay = true)]
void SendMessage(string memberName, string message);
}
public interface IChatChannel : IChatService, IClientChannel
{
}
public partial class ChatClient : Form, IChatService
{
private delegate void UserJoined(string name);
private delegate void UserSendMessage(string name, string message);
private delegate void UserLeft(string name);
private static event UserJoined NewJoin;
private static event UserSendMessage MessageSent;
private static event UserLeft RemoveUser;
private string userName;
private IChatChannel channel;
private DuplexChannelFactoryfactory;
public ChatClient()
{
InitializeComponent();
this.AcceptButton = btnLogin;
}
public ChatClient(string userName)
{
this.userName = userName;
}
private void btnLogin_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(txtUserName.Text.Trim()))
{
try
{
NewJoin += new UserJoined(ChatClient_NewJoin);
MessageSent += new UserSendMessage(ChatClient_MessageSent);
RemoveUser += new UserLeft(ChatClient_RemoveUser);
channel = null;
this.userName = txtUserName.Text.Trim();
InstanceContext context = new InstanceContext(
new ChatClient(txtUserName.Text.Trim()));
factory =
new DuplexChannelFactory(context, "ChatEndPoint");
channel = factory.CreateChannel();
IOnlineStatus status = channel.GetProperty();
status.Offline += new EventHandler(Offline);
status.Online += new EventHandler(Online);
channel.Open();
channel.Join(this.userName);
grpMessageWindow.Enabled = true;
grpUserList.Enabled = true;
grpUserCredentials.Enabled = false;
this.AcceptButton = btnSend;
rtbMessages.AppendText("*****************************WEL-COME to Chat Application*****************************\r\n");
txtSendMessage.Select();
txtSendMessage.Focus();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
void ChatClient_RemoveUser(string name)
{
try
{
rtbMessages.AppendText("\r\n");
rtbMessages.AppendText(name + " left at " + DateTime.Now.ToString());
lstUsers.Items.Remove(name);
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}
}
void ChatClient_MessageSent(string name, string message)
{
if (!lstUsers.Items.Contains(name))
{
lstUsers.Items.Add(name);
}
rtbMessages.AppendText("\r\n");
rtbMessages.AppendText(name + " says: " + message);
}
void ChatClient_NewJoin(string name)
{
rtbMessages.AppendText("\r\n");
rtbMessages.AppendText(name + " joined at: [" + DateTime.Now.ToString() + "]");
lstUsers.Items.Add(name);
}
void Online(object sender, EventArgs e)
{
rtbMessages.AppendText("\r\nOnline: " + this.userName);
}
void Offline(object sender, EventArgs e)
{
rtbMessages.AppendText("\r\nOffline: " + this.userName);
}
#region IChatService Members
public void Join(string memberName)
{
if (NewJoin != null)
{
NewJoin(memberName);
}
}
public new void Leave(string memberName)
{
if (RemoveUser != null)
{
RemoveUser(memberName);
}
}
public void SendMessage(string memberName, string message)
{
if (MessageSent != null)
{
MessageSent(memberName, message);
}
}
#endregion
private void btnSend_Click(object sender, EventArgs e)
{
channel.SendMessage(this.userName, txtSendMessage.Text.Trim());
txtSendMessage.Clear();
txtSendMessage.Select();
txtSendMessage.Focus();
}
private void ChatClient_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
if (channel != null)
{
channel.Leave(this.userName);
channel.Close();
}
if (factory != null)
{
factory.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
雖然小小長了點,可以看到 interface 中只有定義了三個動作:
Join : 用來登入用
Leave: 用來登出用
SendMessage: 用來傳送訊息
在繼續看這份 souce code 之前,先來讀一下設定檔:
Config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint name="ChatEndPoint" address="net.p2p://chatMesh/ChatServer"
binding="netPeerTcpBinding" bindingConfiguration="PeerTcpConfig"
contract="ChatClient.IChatService"></endpoint>
</client>
<bindings>
<netPeerTcpBinding>
<binding name="PeerTcpConfig" port="0">
<security mode="None"></security>
<resolver mode="Custom">
<custom address="net.tcp://localhost/ChatServer" binding="netTcpBinding"
bindingConfiguration="TcpConfig"></custom>
</resolver>
</binding>
</netPeerTcpBinding>
<netTcpBinding>
<binding name="TcpConfig">
<security mode="None"></security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
對應到前面講的 Join, Leave, SendMessage 三個介面,需要三個事件來配合:
private static event UserJoined NewJoin;
private static event UserSendMessage MessageSent;
private static event UserLeft RemoveUser;
Event 的使用在程式中如下:
NewJoin += new UserJoined(ChatClient_NewJoin);
MessageSent += new UserSendMessage(ChatClient_MessageSent);
RemoveUser += new UserLeft(ChatClient_RemoveUser);
聊天室功能裡面,最重要的就是聊天,對,有點廢話,不過難就難在網路連線的維護,幸好都被 WCF 做掉了,在程式裡面是透過 DuplexChannelFactory() 物件來達成:
factory = new DuplexChannelFactory(context, "ChatEndPoint");
channel = factory.CreateChannel();
若我們看看幾個動作即可明白中間的關聯:
channel.Join(this.userName);
channel.SendMessage(this.userName, txtSendMessage.Text.Trim());
channel.Leave(this.userName);
channel.Close();
好了,就這樣。什麼?沒講完?對,聰明的你一定有發現,好像沒指出收訊息的機制?呵,其實是沒特別講清楚而已。
訊息收送
收: MessageSent += new UserSendMessage(ChatClient_MessageSent);
送: channel.SendMessage(this.userName, txtSendMessage.Text.Trim());
2009/07/24
在 blog 中優化顯示程式碼片段
底下的內容是 CSS, 放在 blog 設定畫面裡版面配置==>修改 HTML==>找到類似 body{ 附近(意指在它的前面或是那一段的後面, 不要插到裡面去了)插入下面兩種可以選擇之一的 CSS 碼, 一個是用 <Code> 括住,使用時的範例如下:
效果如下:
或者用有縮編的 PRE tag, 在寫 blog 時的範例如下:
效果如下:
<Code>
code {
display: block;
font-family: Courier New;
font-size: 9pt;
overflow:auto;
background: #fff5ee url(http://sites.google.com/a/hc888.com.tw/file/img/Code_BG0.gif) left top repeat-y;
border: 1px solid #ccc;
padding: 5px 5px 5px 20px;
max-height:200px;
line-height: 1.2em;
margin: 5px 0 0 15px;
}
</CODE>
效果如下:
code {
display: block;
font-family: Courier New;
font-size: 9pt;
overflow:auto;
background: #fff5ee url(http://sites.google.com/a/hc888.com.tw/file/img/Code_BG0.gif) left top repeat-y;
border: 1px solid #ccc;
padding: 5px 5px 5px 20px;
max-height:200px;
line-height: 1.2em;
margin: 5px 0 0 15px;
}
或者用有縮編的 PRE tag, 在寫 blog 時的範例如下:
<PRE>
pre {
display: block;
font-family: Courier New;
font-size: 9pt;
overflow:auto;
background: #fff5ee;
border: 1px solid #ccc;
padding: 5px 5px 5px 20px;
max-height:200px;
line-height: 1.2em;
margin: 5px 0 0 15px;
}
</PRE>
效果如下:
pre {
display: block;
font-family: Courier New;
font-size: 9pt;
overflow:auto;
background: #fff5ee;
border: 1px solid #ccc;
padding: 5px 5px 5px 20px;
max-height:200px;
line-height: 1.2em;
margin: 5px 0 0 15px;
}
2009/07/18
The value of Multi touch 的價值
要說 multi touch 的價值實在是愧不敢當,但是最近的工作剛好就是在開發多點觸控的應用程式,跟著一群年輕小夥子工作,感覺非常溫馨之餘,也讓我有機會在跟他們探討中去深思多點觸控到底價值何處?
一開始我對多點觸控的期待,就是改變對電腦的操作,當然我更想要像駭客任務那樣,不止可以觸控,最希望的是立體感那種操作,最好要更先進的思想操作。但是在接觸多點觸控一年來,發現裡面的問題不少,但是這篇不是要講問題點,而是要講價值。
大概最快想到的就是「手勢操作」。可是問題是誰來定義手勢?M$ 確實定義了些手勢操作,可是若實際去開發軟體的話就會發現其中存在不少問題,這邊指的是我們開發軟體的過程,而不是「應用」。問題在我們的功力還不夠,幸好也是因為大家都聚精會神的研究並解決問題,因而讓我更有機會去探索多點觸控的價值,而這樣的思考至少目前必須先面對 M$ 所建立起來的環境是那麼的複雜與充滿矛盾上。
第一個價值,當然希望能有更直覺式的操作,剛剛提到手勢就是這樣的產物。目前開發出來的就是 Zoom In/ Zoom Out, Scroll, PAN(Move) 這三個。事實上滑鼠也可以做到。我一直在思考,click, double click, move, drag....與 what is gesture! 困惑著我。答案或許是,請拋棄滑鼠吧。(註: 鍵盤留著)
第二個價值,是希望能讓整個螢幕上出現的框框有立體操作感。立體感早就有了,我說的是立體的操作感,否則只是多了另一隻滑鼠而已。要有立體操作感很難,要在平面表達立體已經發展多年,早期都是用滑鼠,當然相安無事,現在要在投射在平面所產生的立體物件上操作,難度非常大。幸好,我也定義了三種操作,有機會再來分享。我們定義了一些立體操作與手勢,希望能改變人類操作平面電腦的習慣,這邊講平面的意思當然是希望將來的顯示裝置操作起來能隨想隨動。
第三個價值,是......趁著這波可攜熱與觸控熱,讓觸控拉近你我彼此間的距離變得更加容易。我個人一直是在玩 Linux, 嵌入式是我的擅項,突然來搞 M$ 真是有夠突兀,不過這不妨礙,問題是我最想要的是建立一個龐大而自然的虛擬世界,能因此而實現嗎?
且拭目以待吧。
一開始我對多點觸控的期待,就是改變對電腦的操作,當然我更想要像駭客任務那樣,不止可以觸控,最希望的是立體感那種操作,最好要更先進的思想操作。但是在接觸多點觸控一年來,發現裡面的問題不少,但是這篇不是要講問題點,而是要講價值。
大概最快想到的就是「手勢操作」。可是問題是誰來定義手勢?M$ 確實定義了些手勢操作,可是若實際去開發軟體的話就會發現其中存在不少問題,這邊指的是我們開發軟體的過程,而不是「應用」。問題在我們的功力還不夠,幸好也是因為大家都聚精會神的研究並解決問題,因而讓我更有機會去探索多點觸控的價值,而這樣的思考至少目前必須先面對 M$ 所建立起來的環境是那麼的複雜與充滿矛盾上。
第一個價值,當然希望能有更直覺式的操作,剛剛提到手勢就是這樣的產物。目前開發出來的就是 Zoom In/ Zoom Out, Scroll, PAN(Move) 這三個。事實上滑鼠也可以做到。我一直在思考,click, double click, move, drag....與 what is gesture! 困惑著我。答案或許是,請拋棄滑鼠吧。(註: 鍵盤留著)
第二個價值,是希望能讓整個螢幕上出現的框框有立體操作感。立體感早就有了,我說的是立體的操作感,否則只是多了另一隻滑鼠而已。要有立體操作感很難,要在平面表達立體已經發展多年,早期都是用滑鼠,當然相安無事,現在要在投射在平面所產生的立體物件上操作,難度非常大。幸好,我也定義了三種操作,有機會再來分享。我們定義了一些立體操作與手勢,希望能改變人類操作平面電腦的習慣,這邊講平面的意思當然是希望將來的顯示裝置操作起來能隨想隨動。
第三個價值,是......趁著這波可攜熱與觸控熱,讓觸控拉近你我彼此間的距離變得更加容易。我個人一直是在玩 Linux, 嵌入式是我的擅項,突然來搞 M$ 真是有夠突兀,不過這不妨礙,問題是我最想要的是建立一個龐大而自然的虛擬世界,能因此而實現嗎?
且拭目以待吧。
2009/06/28
win7 比 vista 便宜 ... 20% off!
微軟在其網站上,正式公告售價:
昇級版售價:
* Windows 7 Home Premium (Upgrade): $119.99
* Windows 7 Professional (Upgrade): $199.99
* Windows 7 Ultimate (Upgrade): $219.99
完整版售價:
* Windows 7 Home Premium (Full): $199.99
* Windows 7 Professional (Full): $299.99
* Windows 7 Ultimate (Full): $319.99
這意味著,因應經濟不景氣,微軟大發慈悲心,大降價,以 Win7 Home Premium 為例大約降價 20%!
昇級版售價:
* Windows 7 Home Premium (Upgrade): $119.99
* Windows 7 Professional (Upgrade): $199.99
* Windows 7 Ultimate (Upgrade): $219.99
完整版售價:
* Windows 7 Home Premium (Full): $199.99
* Windows 7 Professional (Full): $299.99
* Windows 7 Ultimate (Full): $319.99
這意味著,因應經濟不景氣,微軟大發慈悲心,大降價,以 Win7 Home Premium 為例大約降價 20%!
2009/06/23
xp, vista, win7 共舞 multi-boot -2
先前寫過一篇xp, vista, win7 共舞,後來又因為不知名的原因把某一個開機選項弄不見了。真的,不是故意為之。那問題來了,怎樣把之前的開機選項搞出來?
其實這問題或許最好的答案是養成備份習慣。不過因為是實驗機,所以我也就懶得備份,再說,工作機也不需要搞這東東。
在BCDE FAQ....這文章還真怪,BCDEF...Ok Ok, 不離題,在這篇文章中提到要怎樣自己弄一個新的開機選項,請見 How to create a new Windows Vista operating system entry 這一條的說明。步驟大約如下:
1. bcdedit /copy {GuidToCopy} /d 「NewEntryDescription」
==> 我是這麼下命令的:
bcdedit /copy {current} /d "win7 enterprise"
==> 這樣一來,會把目前的開機選項複製並附在選單的最後。若是注意的話,會發現 Identifier 是新的,其餘的都是與 {current} 相同。
2. 用下列兩個命令來設定到你要的磁區,也就是「磁碟機」:
bcdedit /set {NewGuid} device partition=x:
bcdedit /set {NewGuid} osdevice partition=x:
==> 這邊的 {NewGuid} 就是上一條產生的 Identifier, 而 x: 就是你要的磁碟機
3. 用下列命令調整開機順序,不執行此命令也行
bcdedit /displayorder {NewGuid} /addlast
其實這問題或許最好的答案是養成備份習慣。不過因為是實驗機,所以我也就懶得備份,再說,工作機也不需要搞這東東。
在BCDE FAQ....這文章還真怪,BCDEF...Ok Ok, 不離題,在這篇文章中提到要怎樣自己弄一個新的開機選項,請見 How to create a new Windows Vista operating system entry 這一條的說明。步驟大約如下:
1. bcdedit /copy {GuidToCopy} /d 「NewEntryDescription」
==> 我是這麼下命令的:
bcdedit /copy {current} /d "win7 enterprise"
==> 這樣一來,會把目前的開機選項複製並附在選單的最後。若是注意的話,會發現 Identifier 是新的,其餘的都是與 {current} 相同。
2. 用下列兩個命令來設定到你要的磁區,也就是「磁碟機」:
bcdedit /set {NewGuid} device partition=x:
bcdedit /set {NewGuid} osdevice partition=x:
==> 這邊的 {NewGuid} 就是上一條產生的 Identifier, 而 x: 就是你要的磁碟機
3. 用下列命令調整開機順序,不執行此命令也行
bcdedit /displayorder {NewGuid} /addlast
2009/06/12
screen capture
我們有一個應用程式,需要抓取全螢幕的畫面,有人會截取 PrtScn Keyboard 事件來達到目的,我當然認定這個方法非常不好,就像前面講的 mouse hook 一樣,別人一樣也可以做 keyboard hook。
後來用了某種 screen capture 的方法,結果把螢幕上所有東西都抓下來,或許這結果是某些人想要的,但是我們卻不要像滑鼠或是一些 Gesture 提示的畫面。目前找到一篇Screen Captures, Window Captures and Window Icon Captures with Spy++ style Window Finder!有不錯的範例,請自行前往觀看。
後來用了某種 screen capture 的方法,結果把螢幕上所有東西都抓下來,或許這結果是某些人想要的,但是我們卻不要像滑鼠或是一些 Gesture 提示的畫面。目前找到一篇Screen Captures, Window Captures and Window Icon Captures with Spy++ style Window Finder!有不錯的範例,請自行前往觀看。
Touch 與 mouse 共舞
最近在搞 Windows 7 的 Multi Touch 應用程式,其中有幾項議題困擾很久,其中一項是 sleep(1) 已於前面說明。另一項是 Touch 發生時我們不想要有 Mouse Event, 我說的是不想要,不是不處理即可,這問題讓我煩到快抓狂。
不抱怨了,直接說答案,就是用 Global Mouse Hook, 然後把 Mouse Message 濾掉。是有不少例子在說明 mouse hook, 例如我看的最懂的是Processing Global Mouse and Keyboard Hooks in C#,卻沒有人講明白怎樣濾掉 mouse message.
事實上在該文中有答案,只是它沒明說而已。我直接寫一個範例片段來說明,當然說明直接放程式片段中:
不抱怨了,直接說答案,就是用 Global Mouse Hook, 然後把 Mouse Message 濾掉。是有不少例子在說明 mouse hook, 例如我看的最懂的是Processing Global Mouse and Keyboard Hooks in C#,卻沒有人講明白怎樣濾掉 mouse message.
事實上在該文中有答案,只是它沒明說而已。我直接寫一個範例片段來說明,當然說明直接放程式片段中:
// 註冊 global mouse hook
void TFormPaint::RegMouseHook()
{
MouseHookDelegate = gcnew HookProcDelegate(this, &TFormPaint::MouseHookProc);
MouseHookHandle = ::SetWindowsHookEx(WH_MOUSE_LL, MouseHookDelegate,
Marshal::GetHINSTANCE(System::Reflection::Assembly::GetExecutingAssembly()->GetModules()[0]),0);
}
// 我們處理的 mouse hook 的函式
int TFormPaint::MouseHookProc(int nCode, int wParam, IntPtr lParam)
{
WCHAR Buffer[30];
// 把 Mouse Hook 的訊息從 lParam 指標中取出來
MOUSEHOOKSTRUCT* mouseHookStruct=(MOUSEHOOKSTRUCT*)lParam.ToPointer();
// 只濾掉發生在本視窗的滑鼠事件,
// 所以要比對滑鼠所發生時的視窗
HWND CurHwnd = WindowFromPoint(mouseHookStruct->pt);
GetWindowText(CurHwnd, Buffer, 30);
String^ WindowTitle = gcnew String(Buffer);
// ☆重點來了,
// 當發現滑鼠事件是作用在我們的視窗上,就以
// return -1 來濾掉
if (WindowTitle->IndexOf(this->Text) != -1)
{
return -1;
}
// 下面這行非常重要,否則整個系統的滑鼠會因此壞掉
return CallNextHookEx(MouseHookHandle, nCode, wParam, lParam);
}
sleep 1 ms ?
還記得四月初時我寫的這一篇timer 與 while(1)嗎?最近在用 visual studio 開發應用程式時有用到 sleep() 函式來暫時停止程式運行,用我們的話說,就是我們要讓程式的執行頻率是 125 次/秒,也就是我們想要用 sleep(8) 來達到目的。sleep(8) 應該要能暫停 8ms, 結果實驗數據是 16ms, 甚至當我們調整成 sleep(1), 答案也一樣。
慘了,效率只有一半!這問題困擾很久,試著用 timer,答案一樣的令人沮喪,也試過透過 Windows Message 的方式,沒用。
好了,最後結果是參考timeBeginPeriod搭配 timeEndPeriod。因為這會改到系統的時間設定,因此記得要成對。
簡單寫個範例如下:
想對 Timer 有更進一步了解的人,可以自行參考Timer 介紹一文。
慘了,效率只有一半!這問題困擾很久,試著用 timer,答案一樣的令人沮喪,也試過透過 Windows Message 的方式,沒用。
好了,最後結果是參考timeBeginPeriod搭配 timeEndPeriod。因為這會改到系統的時間設定,因此記得要成對。
簡單寫個範例如下:
timeBeginPeriod(8);
sleep(8);
timeEndPeriod(8);
想對 Timer 有更進一步了解的人,可以自行參考Timer 介紹一文。
2009/06/08
xp, vista, win7 共舞 multi-boot
最近真的是離不開 M$ OS, 甚至一台電腦要裝九個作業系統,當然啦,是資源不足才需要這樣。問題來了,開機選單在 winxp 還可以透過界面修改,可是 vista, win7 全都不開放!現在研究後的心得如下,請在 vista or win7 下執行:
一、首先在 Command Prompt, 也就是 DOS command, 上用右鍵選「Run as Administrator」,這樣才有足夠的權限,若找不到的話請參考:
Start Menu->All Programs->Accessories->Command prompt(在上面點右鍵選擇"Run as administrator")
二、可以用 bcdedit /enum 列出所有開機選單,例如下面的是我的電腦的範例, # 號後面的是我加的註解:
三、在上文提到的 {current} 還有 {default} 等選擇文字,其實就在 /enum 列出的 "identifier" 欄位裡面
四、 用 bcdedit /? 取得進一步資訊
一、首先在 Command Prompt, 也就是 DOS command, 上用右鍵選「Run as Administrator」,這樣才有足夠的權限,若找不到的話請參考:
Start Menu->All Programs->Accessories->Command prompt(在上面點右鍵選擇"Run as administrator")
二、可以用 bcdedit /enum 列出所有開機選單,例如下面的是我的電腦的範例, # 號後面的是我加的註解:
Windows Boot Manager
--------------------
identifier {bootmgr}
device partition=D:
description Windows Boot Manager
locale en-US
inherit {globalsettings}
default {current}
resumeobject {42231781-52f5-11de-92a1-f650bd7a3f2b}
displayorder {ntldr}
{current}
{42231782-52f5-11de-92a1-f650bd7a3f2b}
{4223177e-52f5-11de-92a1-f650bd7a3f2b}
{b1252275-52ef-11de-ac3a-e7ceed221dbd}
toolsdisplayorder {memdiag}
timeout 30
Windows Legacy OS Loader
------------------------
identifier {ntldr}
device partition=D:
path \ntldr
description 舊版 Windows
# 可以用下列命令改掉
# bcdedit /set {ntldr} Description "XP 32/64"
Windows Boot Loader
-------------------
identifier {current}
device partition=C:
path \Windows\system32\winload.exe
description Microsoft Windows Vista
locale en-US
inherit {bootloadersettings}
osdevice partition=C:
systemroot \Windows
resumeobject {adc6e5dc-5450-11de-b61c-8253c0de8538}
nx OptIn
# 可以用下列命令將原來的"Microsoft Windows Vista" 改掉
# bcdedit /set {current} Description "Vista 32 en Enterprise"
Windows Boot Loader
-------------------
identifier {42231782-52f5-11de-92a1-f650bd7a3f2b}
device partition=I:
path \Windows\system32\winload.exe
description Windows 7
locale zh-TW
inherit {bootloadersettings}
recoverysequence {42231783-52f5-11de-92a1-f650bd7a3f2b}
recoveryenabled Yes
osdevice partition=I:
systemroot \Windows
resumeobject {42231781-52f5-11de-92a1-f650bd7a3f2b}
nx OptIn
# 可以用下列命令將原來的 "Windows 7" 改掉
# bcdedit /set {42231782-52f5-11de-92a1-f650bd7a3f2b} Description "Win7 64 Ultimate"
Windows Boot Loader
-------------------
identifier {4223177e-52f5-11de-92a1-f650bd7a3f2b}
device partition=H:
path \Windows\system32\winload.exe
description Windows 7
locale en-US
inherit {bootloadersettings}
recoverysequence {4223177f-52f5-11de-92a1-f650bd7a3f2b}
recoveryenabled Yes
osdevice partition=H:
systemroot \Windows
resumeobject {4223177d-52f5-11de-92a1-f650bd7a3f2b}
nx OptIn
# 可以用下列命令將原來的 "Windows 7" 改掉
# bcdedit /set {4223177e-52f5-11de-92a1-f650bd7a3f2b} Description "Win7 32 Ultimate"
Windows Boot Loader
-------------------
identifier {b1252275-52ef-11de-ac3a-e7ceed221dbd}
device partition=E:
path \Windows\system32\winload.exe
description Microsoft Windows Vista
locale en-US
inherit {bootloadersettings}
osdevice partition=E:
systemroot \Windows
resumeobject {b1252276-52ef-11de-ac3a-e7ceed221dbd}
nx OptIn
# 可以用下列命令將原來的 "Microsoft Windows Vista" 改掉
# bcdedit /set {b1252275-52ef-11de-ac3a-e7ceed221dbd} Description "Vista 64 Enterprise"
三、在上文提到的 {current} 還有 {default} 等選擇文字,其實就在 /enum 列出的 "identifier" 欄位裡面
四、 用 bcdedit /? 取得進一步資訊
訂閱:
文章 (Atom)