kowala's home

kowala's home
這裡是我的學習筆記,陸續增加中。
http://kowala21.blogspot.com

2011-06-29

揚言停售APP 北市重罰谷歌

這幾天的新聞,內容節錄如下

揚言停售APP 北市重罰谷歌


(中央社記者孫承武台北27日電)台北市政府今天表示,由於Google表明拒絕依消費者保護法的規定,給予手機應用軟體(APP)消費者7日鑑賞期,並揚言暫時停止對台灣消費者銷售付費APP,決開罰新台幣100萬元。

市府主任消保官陳碧珠指出,Google AndroidMarket的服務條款中載明,消費者只能在下載手機應用軟體的15分鐘內退費,明確違反消保法「郵購買賣」必須准許消費者在7日內退費的規定,並經市政府限期於上週四(23日)前改善在案,Google上週五上午要求市府暫不開罰,今天再做最後決定。

但她說,Google上午由律師代表到市府,並由美方人員致電市府,明確表示拒絕遵循消保法的強制規定,並且表示停止付費應用軟體的銷售,市府對此表示遺憾,並決定依法開罰。

...

此次向蘋果以及Google兩家手機應用軟體業者宣導,蘋果也接受市府的規範,只有Google無視法律的規定,要求不被規範的特權,市府無法接受。

葉慶元指出,手機銷售平台業者如Google及蘋果在軟體出售後,可透過手機的連線更新功能,查知軟體是否持續存在於使用者的手機上,所以不至於發生消費者退費後持續使用相關手機應用軟體的問題。


相關新聞

北市鐵腕罰谷歌 手機族嗆「幫倒忙」

使用Android軟體的智慧型手機族,這幾天已經找不到付費軟體,北市府對Google開鍘重罰百萬,Google態度也很強硬,付費應用程式提供全球131個國家,可不能只有台灣的遊戲規則不一樣。

智慧型手機族:「不會用到付費軟體,除非軟體有版權,才會使用付費軟體,不能升級,權益上會有一點受損,它沒有辦法進行更新。」

有人大聲叫好,認為捍衛消費者的權益,但也有人認為,數位軟體鑑賞期延長成7天,不利保護數位內容,北市府開鍘於法有據,卻也意外引發討論!


業者:線上電影 哪能鑑賞7天?  2011/0702

針對台北市政府要求Google Android應 用程式應有七天鑑賞期,引發Google決定暫停在台灣付費應用程式服務,影音娛樂平台業者威望國際(CatchPlay)總經理張心望昨天表示,希望台 北市政府顧及消費者權益的同時,也應多考量產業發展及不同產品的性質。否則,消費者看完線上電影後還能再退錢,業者就不用做了。
...



APP付費軟體下架 消費者憂無法更新

自由 更新日期:"2011/07/06 04:11"

〔自由時報記者蔡偉祺/台北報導〕Google拒絕提供付費APP軟體七天鑑賞期,被台北市政府重罰百萬元,Google上月底已將Android Market上付費APP軟體全數下架,有消費者擔心先前購買的付費軟體後續無法順利更新。法規會主委葉慶元指出,若Google下架影響先前購買軟體民眾權益,將要求Google依消保法相關規定進行改善。
...

也有人認為手機軟體是智慧財產商品,易於拷貝、複製,應該排除消保法適用,才能保障開發者;甚至有民眾質疑,先前蘋果有長達三十天的退費期,比市府要求七天鑑賞期還要長。
...

蘋果APP退費 台灣全球首例  2011/07/15

(中央社記者孫承武、許雅筑台北14日電)台北市政府消保官要求蘋果及Google,給予付費APP7天鑑賞期和退費機制。法規會今天表示,蘋果善意回應,同意修正定型化契約,在7天內可無條件退費,創下全球首例。
 ...
 法規會表示,蘋果上午在網路上公布修正其定型化契約,「您得自產品收受之日起7日內,取消對產品之購買。在您通知iTunes您已刪除產品所有備份之前提下,iTunes將會退還您已支付之價款。自您取消購買時起,您不再被授權繼續使用該產品,此項權利不可拋棄。
...

 持續觀察...

2011-06-20

關於色碼表 - Alpha 通道

大家都知道的,RGB的組成是由 Red、Green、Blue 三原色組合而成,每個原色用兩個十六進位值表示,因此有16777216色的組合變化[1]。

(0, 0, 0) 是黑色
(255, 255, 255) 是白色
(255, 0, 0) 是紅色
(0, 255, 0) 是綠色
(0, 0, 255) 是藍色
(255, 255, 0) 是黃色
(0, 255, 255) 是青色
(255, 0, 255) 是粉紅

原色光混合的表現[2]


在網頁應用上,以黃色為例,十六進位表示則寫成 #FFFF00,數字排列方式為 #RRGGBB,同理,紅色是#FF0000,綠色是#00FF00,藍色是#0000FF。

有時我們常常會看見透明圖,也就是主題清楚,但背景透明的圖片,這在電腦桌面的小圖示是很常見的(各位可以用滑鼠拉著小圖示重疊到別的圖示上觀察看看),這透明又該如何在RGB系統上表示呢?
所以有一種可以表示透明度的方法,叫做RGBA,有時它也被寫成ARGB,A 稱為不透明度叫做 Alpha 通道,值在0%和100%之間,alpha通道值可以用百分比、整數或者像RGB參數那樣用0到1的實數表示。
比如,0x80FFFF00是50%透明的黃色,因為所有的參數都在0到255的範圍內表示。0x80是128,大約是255的一半。[3]

RGBA範例


Android 上的做法
其實很簡單,我們可以自己做一個色碼表,然後就如正常引用資源般的使用,並無不同。

 ck1 = (CheckBox)findViewById(R.id.checkBox1);
 ck2 = (CheckBox)findViewById(R.id.checkBox2);
...
 ck1.setTextColor(0xFFFF0000); //加上 Alpha 通道
 ck2.setTextColor(Color.GREEN);//一般用法




官網上的說明



參考
[1]. 0-255 = 256 種,又 256*256*256 = 16777216。
[2]. http://zh.wikipedia.org/wiki/RGB
[3]. http://zh.wikipedia.org/wiki/RGBA

2011-06-19

Android How to play video in R.raw.* using VideoView.

有時,我們會在軟體撥放前秀一段影片,而且是包含在軟體內的影片,這時就不適用SDCARD 或是 net download ,而是希望是在  R.raw.* 之中。

經過實測發現,也並不是那麼容易,關鍵在於支援的檔案格式。
官網上說的是 *.3gp 及 *.mp4

我把檔案轉成 3gp 後,放在專案的 res\raw 目錄下
檔名是 home.3gp ,結果成功撥放出來了。

home.3gp 你們可以下載回去試試。


程式碼如下

        Uri uri = Uri.parse("android.resource://"+this.getPackageName()+"/"+R.raw.home);
        VideoView v=new VideoView(this);
        setContentView(v);
        v.setVideoURI(uri);
        v.setMediaController(new MediaController(this));
        v.requestFocus();
        v.start();

2011-06-18

要如何將 AdSense 放到網誌中...

不曉得是我比較笨還是怎樣的,廣告都弄不出來 = ="
欄位也留了,廣告的程式碼也貼了,就是看不見廣告的影子...

搞了半天,終於弄出來了 ^^

照官方的說法是沒用的啦~
http://www.google.com/support/blogger/bin/answer.py?hl=zh-Hant&answer=50288

必須先按"收益"下的 "申請AdSense ",這很奇怪,我已經都有申請了,卻還要一直申請...



接著會出現廣告的配置,就用預設的。

 繼續按下一頁


看到這裡,代表已經成功了,你再回去檢視時就會看到廣告了。


就像這樣,廣告出現了,OH   YA!


 這個時候,你才可以按照官方說的步驟去做 = =

  http://www.google.com/support/blogger/bin/answer.py?hl=zh-Hant&answer=50288


 設定看看會怎樣,這時版面就會出現 AdSense 的區塊,可以按旁邊的編輯去改廣告的樣式。


 費了千辛萬苦,終於把它弄出來了阿~
 
好了,不玩了,回去繼續寫程式了...

2011-06-16

要如何製作能通過 Google AdSense 檢查的 Blog (VI) - 取得資格

經過漫長的努力,終於通過 AdSense 的審查,此刻的你,想必是疲憊不堪了,這讓我想到一部電影,127小時 127 Hours,主角 Alan 被困在夾縫中,進退不得,用盡一切辦法,最後只能斷臂求生...

這裡最快樂,宛如仙境。



被困在藍約翰峽谷石縫中,呼救無門!

斷臂重生

終於獲救


好看的電影。。。


好了,休息夠了,回頭看看這個,找到通知函,點那圈起來的連結,會引導到一份合約。



合約內容節錄

說明
請檢閱並接受以下條款及細則。
Google AdSense TM 線上標準條款及細則
您在註冊 GOOGLE ADSENSE 線上計畫前,請先詳閱本條款內容與常見問題。加入 GOOGLE ADSENSE 線上計畫即表示您接受本合約條款。如果您不接受本合約條款,請勿註冊或加入 GOOGLE ADSENSE 線上計畫。
---以下略---

請按下下方的確定同意,之後會出現這頁,在右上角,可以找到您的 ID,把它 Copy 下來備用。


回到您最初申請開發者的文件,當您繳費並完成確認後,會出現這個頁面,要求您要有AdSense 的 ID。。。
現在可以把它貼上去,並按下右邊的按鈕了。


恭喜您,已經完成所有的步驟了,可以上傳您的程式到 Android Market 了。


關於Blog 命運,以後,也可以把 apk 的使用說明擺這兒,也可以在這跟 USER 互動。

要如何製作能通過 Google AdSense 檢查的 Blog (V) - 實作篇 - 發表文章了

真是媳婦熬成婆,到了發表文章時間了。。。
先讓我喝杯水,喘口氣!

發表文章數個原則

1.自己寫
2.引用註明出處
3.敘事流暢,使讀者看的懂
4.穿插圖表強化表達概念


說明:

1.自己寫
發表文章,就是把自己對某事或物的觀點說出來,文筆不用出色,只要真誠都可以感動人的。

2.引用註明出處
老話一句:太陽底下沒有新鮮事,沒有甚麼事是我們所能 "發明" 的,我們只是發現而已,為了把觀點說清楚,常常需要套用前輩們的知識或成果,站在巨人肩膀,能使我們看得更遠,事無大小,貢獻的產生更是長遠,註明出處只是使人明白,前輩們已經證明過這些,反之則落的抄襲之嫌。

3.敘事流暢
目的是使讀者看的懂,白話要比文言文好,口語要比咬文嚼字好,因為文章只是工具,要傳達某些東西,讀者越省力閱讀越好,當然,有些 "眾所皆知" 的詞句,就不需要硬翻成 "大家早就知道的事" ,"闖紅燈" 就不用寫成 "紅燈直直走,叫也叫不停 XD",這樣也就太過了,分寸要拿捏好。

4. 穿插圖表強化表達概念
人畢竟是視覺動物,對圖像的記憶遠超過文字,也可以輕易的辨認出幾十年不見的臉孔,所以要多善用圖像來表達一些概念,可以收到事半功倍的效果。

實作:

承前例,登入後按下右上角的"設計",會進入到設計頁面,頁面上方有一排摺頁,按下"文章",先確認步驟1. 2.是否正確,然後會看到這個畫面,3.是標題,4.是本文,5.是插入圖片,值得一提的是,您無須管圖片存在哪,只要按下5.就可以上傳圖片了,如下圖所示。


承上圖,按下5. 之後會出現這個畫面,如下圖所示。


1.是按下瀏覽本機圖檔所在位置,然後按確定後會自動上傳,完成後會出現2.,可以一次把多張圖片上傳,然後在慢慢地插入文件中。圖檔插入文件中,點選該圖片,下方會出現大小及對齊的選項,如下圖,可以視需要調整。文章完成後,再按下左下橘色的 "發布文章",這樣就完成了一份文件。



上傳的圖檔會被自動保存在您的 Picasa 資料夾中,您可以參考 "我的帳戶\Picasa 網路相簿"中的設定,如下圖所示。


如果文件要修改的話,只需按下 1. 然後再點選 2. 就進入剛剛的編輯畫面了,我都是一邊看文件,一邊編輯,這樣比較方便。


 最後,google blogspot 功能真的很不錯,很好使用的。


要如何製作能通過 Google AdSense 檢查的 Blog (VI) - 取得資格
要如何製作能通過 Google AdSense 檢查的 Blog (V) - 實作篇 - 發表文章了...
要如何製作能通過 Google AdSense 檢查的 Blog (IV) - 實作篇 - 版面配置...
要如何製作能通過 Google AdSense 檢查的 Blog (III) - 實作篇 - 背景
要如何製作能通過 Google AdSense 檢查的 Blog (II) - 規劃篇
要如何製作能通過 Google AdSense 檢查的 Blog (I)

2011-06-15

要如何製作能通過 Google AdSense 檢查的 Blog (IV) - 實作篇 - 版面配置

版面配置,在第二篇中提到了一個 Blog 所需的要素,以下將帶各位實際操作。
在右上角按下設計按鈕後會來到這個頁面,如果畫面跟下圖不一樣,先檢查步驟 1. 及 2.



步驟 3. 是設定標頭的風格色系,按下後出現如下畫面,選擇一個喜歡的之後按儲存。


步驟 4.設定標題,按下後出現編輯視窗如下


 1.是標題 2. 是標題說明,它與主頁對應如下圖




 如果要加上圖片的話,可以用 3. 來上傳,也可以自己寫個圖片連結加在 2. 之中,

<img src="http://1.bp.blogspot.com/-nHJdqy3ezy4/TfO9CSbSyQI/AAAAAAAAAAg/TmeF4vrWU1o/s220/1.jpg" width="80" height="63">
<br>看在我打字很辛苦的面子上,轉載請註明網址,拜託!拜託!
<br><a href="http://kowala21.blogspot.com">
<b>http://kowala21.blogspot.com</b>
</a>

效果如下圖


步驟 5. 新增小工具,這裡的工具很多,可以視需要增減,有許多好玩的東東,以後再慢慢玩,現在只選擇幾個像樣的就好,按下後如下圖所示。


左邊是選單,現在位置是在 "基本",紅色圈圈是我已經選取的功能。


第二個選單是 "精選",把它換個名字會比較好理解,就叫它 "配合廠商" 吧。


這裡有餵魚,殺殺時間也不錯 ^^

步驟 6. 現在到了頁尾,通常這個位置都是喊喊口號,像是著作權之類的道德勸說,或是公司電話等等,總之,隨便你要擺甚麼都好,但要符合慣例,否則會被人當怪腳  XD



倒吃甘蔗 - 猜一句成語 ?

版面已介紹完了,接下來是發表文章了 ^^



要如何製作能通過 Google AdSense 檢查的 Blog (VI) - 取得資格
要如何製作能通過 Google AdSense 檢查的 Blog (V) - 實作篇 - 發表文章了...
要如何製作能通過 Google AdSense 檢查的 Blog (IV) - 實作篇 - 版面配置...
要如何製作能通過 Google AdSense 檢查的 Blog (III) - 實作篇 - 背景
要如何製作能通過 Google AdSense 檢查的 Blog (II) - 規劃篇
要如何製作能通過 Google AdSense 檢查的 Blog (I)

要如何製作能通過 Google AdSense 檢查的 Blog (III) - 實作篇 - 背景

在進入版面配置之前,先把背景加上去,這是 Blog 的精神所在,它決定了你的整體風格及方向,圖片及色系通常就能表現出你的風格。
這個範本背景是自己拍的,以前去清境農場玩的時候拍的,我喜歡大自然,所以就挑它了。

加入桌面背景

這裡有多種方法可以使用,一是直接套用 Google 提供的模組,二是跟我一樣,用一張照片來當底圖。

 先登入 Google,登入後右上角會顯示您的帳號資訊,按下設計

 

進入設計畫面後,再按範本設計工具

 


 一、套用模組

在範本設計工具中,選取任一範本,下方分割頁會顯示預覽頁面,確認後,在最右邊有一套用至網誌按鈕,按下即可。


二、自訂圖片

按下最左邊有個背景折頁按鈕,在按下2.的上傳圖片標示,上傳完成就會如下圖所示,再把背景圖固定住,把3.所示的勾勾取消,最右邊有一套用至網誌按鈕,按下即可。

 

到這裡背景就算完成了,當桌面背景一上去,感覺就不會太空洞,紮實許多,雖然還沒有內容,^^

接著是版面配置...


要如何製作能通過 Google AdSense 檢查的 Blog (VI) - 取得資格
要如何製作能通過 Google AdSense 檢查的 Blog (V) - 實作篇 - 發表文章了...
要如何製作能通過 Google AdSense 檢查的 Blog (IV) - 實作篇 - 版面配置...
要如何製作能通過 Google AdSense 檢查的 Blog (III) - 實作篇 - 背景
要如何製作能通過 Google AdSense 檢查的 Blog (II) - 規劃篇
要如何製作能通過 Google AdSense 檢查的 Blog (I)

要如何製作能通過 Google AdSense 檢查的 Blog (II) - 規劃篇

這是第二篇,準備來說說如何規劃一個 Blog,
凡事豫則立,不豫則廢,說明了計劃的重要性。
這句是故意寫的,老外用翻譯的一定看不懂,呵呵~

首先來看看完整的 Blog 架構,黃色的是標出組成元素。

[註] 本文是採用 Google 它家的部落格 http://www.blogspot.com/為對象,
沒有的就去申請一個吧,建議別用別牌的,例如 Y 牌的,免得商家互相眼紅而拒絕你的申請阿~~~
真是社會黑暗阿~ 故意寫小一點,就是不給你看 ^^


上圖黃色序號說明如下

1. 標題
這就是您 Blog 的名稱,隨你高興,隨便取一個,就好像衣服有標籤,人有名字一樣。

2.標題說明
我們買東西,總要看看成分,不然喝飲料,喝了一堆塑化劑 DEHP ,聽說該廠生產幾十年了,這可是製造汽車保險桿材料,反正我們都喝了汽車保險桿幾十年了,偶而逛到一個傷眼的 Blog 也沒什大不了了,是吧!客官們就將就忍耐看下去吧~

3.本文內容
這是文章的本體,原則上是要自己寫,千萬不要直接把別人的拿來貼,如有需要,必須註明文章出處,我會在後面補充一篇寫作的技巧及注意事項。

4.輕鬆一下
點點滑鼠,手指運動一下,這是我在 Google 設計版面中看到的,還不錯玩,就放上來給跟我一樣無聊的人玩玩,這是可有可無的,當然,這也是預備來放 AdSense 廣告的預留位置,好像要擺一個廣告供人點擊用,不然它要靠甚麼賺錢呢。

5.目錄結構,或是俗稱的導覽圖
這是我覺得最重要的東西,不可少,裏頭有甚麼內容,一目了然,好比書有目錄索引般,可以節省讀者的時間,換句話說,沒有目錄,那將是一個盲目的世界啊!

6.熱門文章
這是可有可無的,主要是讓人預覽一下,但是多了就顯得凌亂,建議 Blog 初期可以用,湊版面用,才不會顯得空洞,等內容扎實後,就可以拿掉了。

7.總瀏覽量
計數器是也,這可以看出 Blog 受歡迎的程度,而自己也可以透過管理裏頭的統計功能,看到讀者是從那些國家連過來的,可以檢討 Blog 的寫作方向,這也是必要的。

8.追蹤者
也可以當作是好友啦,當別人覺得你的 Blog 不錯時,他們可以加入,同時也會顯示誰加入,這樣可以互相教學相長 ,也提升學習的興趣。

9.作者簡介
這裡可以自我介紹,有一個專屬的簡介頁,可以放大頭照上去,這也是不錯,讓人多了解你一些,當然小小後學我是沒甚麼好說的,就是沒事愛做做宅男。

10.版權宣告
版權宣告通常是放在最下面,太陽底下沒有新鮮事,天下文章一大抄,但是抄有巧有拙,引用時別忘記註明出處,這是對他人的尊重,也是一個好的習慣。



要如何製作能通過 Google AdSense 檢查的 Blog (VI) - 取得資格
要如何製作能通過 Google AdSense 檢查的 Blog (V) - 實作篇 - 發表文章了...
要如何製作能通過 Google AdSense 檢查的 Blog (IV) - 實作篇 - 版面配置...
要如何製作能通過 Google AdSense 檢查的 Blog (III) - 實作篇 - 背景
要如何製作能通過 Google AdSense 檢查的 Blog (II) - 規劃篇
要如何製作能通過 Google AdSense 檢查的 Blog (I)

要如何製作能通過 Google AdSense 檢查的 Blog (I)

這篇文章是給誰閱讀的呢?
這是給申請或將要申請 Android Market 開發人員的新手閱讀的文件,
本文不是以廣告賺錢為目的,旨在如何快速取得 Android Market 開發人員
所應具備的 "條件",為了幫助後面的前賢們,可以快速通過這個門檻,
所以寫這篇文章...

各位應該都是繳費了,卻還不能取得資格的 USER 吧,嘻嘻!
原以為繳了美金 $25 元加入開發者就好了說,沒想到後面還有這招 XOX
查了查網路發現,其實還有很多人都有相同的遭遇啦,那就開始著手規劃
開始製作 Blog 了,先來看看我的通過通知,

這可是花了我不少精力才得到這張(圖一)



廢話不多說,開始動手吧 (距離繳費,大概已經過了一個月了 = =! )


要如何製作能通過 Google AdSense 檢查的 Blog (VI) - 取得資格
要如何製作能通過 Google AdSense 檢查的 Blog (V) - 實作篇 - 發表文章了...
要如何製作能通過 Google AdSense 檢查的 Blog (IV) - 實作篇 - 版面配置...
要如何製作能通過 Google AdSense 檢查的 Blog (III) - 實作篇 - 背景
要如何製作能通過 Google AdSense 檢查的 Blog (II) - 規劃篇
要如何製作能通過 Google AdSense 檢查的 Blog (I)

2011-06-14

VC++ 取得系統日期時間



取得系統日期時間
這張圖是跟前例一起拍的,我們只看左邊的 textBox 就好
上面左邊的 textbox 示範如何取得系統日期時間,程式碼如下:
必須先含入  #include windows.h 這個改成全形了,記得改回來!

private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {
            
            SYSTEMTIME curTime;
            GetLocalTime(&curTime);
            
            int yy=curTime.wYear;
            int mm=curTime.wMonth;
            int dd=curTime.wDay;
            int hh=curTime.wHour;
            int mn=curTime.wMinute;
            int ss=curTime.wSecond;
            int ww=curTime.wDayOfWeek;

            array String^^ localtime={
                Int32(yy).ToString(),Int32(mm).ToString(),Int32(dd).ToString(),
                Int32(hh).ToString(),Int32(mn).ToString(),Int32(ss).ToString(),
                Int32(ww).ToString()};

            textBox1->Text += localtime[0]+"年"+localtime[1]+"月"+localtime[2]+"日  "+
                localtime[3]+"時"+localtime[4]+"分"+localtime[5]+"秒,星期"+localtime[6]+"\r\n";
         }

使用comboBox來建立天干地支60甲子年選項。

使用 comboBox 來建立天干地支選單


            array ^ tangun=gcnew array {"甲","乙","丙","丁","戊","己","庚","辛","壬","癸"};
            array ^ dize=gcnew array {"子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"};          
            int i=0,j=0,count=0;
            while(count<60){
                comboBox4->Items->Insert(count,tangun[i]+dize[j]);  
                i++;j++,count++;
                i=i%10;j=j%12;              
            }              
          
 comboBox4->SelectedIndex=0;


上面預設選項是 0


再論天干地支與民國或西元間的轉換公式
首先須做一個六十甲子表,然後來推轉換公式,配合上列程式,count 就是序號。

count =0,甲子
count =1,乙丑
count =2,丙寅
...
count =59,癸亥
count =60,甲子

先與實際的干支定位,查農民曆知民國六十年為辛亥,序號count 就是47.西元就是60+1911=1971
依據干支60年循環一次,我們得知
民國六十年 = (60+47)%60=47,count =47  查六十甲子表= 辛亥
西元1971年 = (1971-1911+47)%60=47,count =47  查六十甲子表= 辛亥

依據上述可以歸納出一個公式

西元 count = (year-1911+47)%60
民國 count = (year+47)%60

然後可以設定選單
int year=1971; //西元
count=(year-1911+47)%60;
comboBox4->SelectedIndex=count;



這樣就把它轉成今年了

解決C++中,textBox換行問題.


textBox換行問題,如果沒辦法在textBox中換行,先檢查下列屬性
Multiline=True
AcceptsReturn=True
AcceptsTab=True (\t 有時用這個比較好對齊)
然後在欲顯示文字中加入 \r\n
textBox1->Text += L"設定 dllVar = "+dllVar+"\r\n";
如果有數值,需先轉換成文字
textBox1->Text += L"讀出 dllVar = "+ System::Int32(dllVar).ToString()+"\r\n";
這樣就 ok 了 ^^


一些 Unicode 及 Ansi 碼的討論

開這個話題目的,當然也是為了多了解一些編碼知識,為了解決如何在VC++中使用中文字碼問題。

先來看看Unicode及Ansi在記憶體中的排列方式(http://www.regexlab.com/zh/encoding.htm)

在 ASCII 阶段,单字节字符串使用一个字节存放一个字符(SBCS)。比如,"Bob123" 在内存中为:
42    6F    62    31    32    33    00
-    -    -    -    -    -    -
B    o    b    1    2    3    \0

在使用 ANSI 编码支持多种语言阶段,每个字符使用一个字节或多个字节来表示(MBCS),因此,这种方式存放的字符也被称作多字节字符。比如,"中文123" 在中文 Windows 95 内存中为7个字节,每个汉字占2个字节,每个英文和数字字符占1个字节:
D6 D0    CE C4    31    32    33    00
--    --    -    -    -    -
中    文    1    2    3    \0

在 UNICODE 被采用之后,计算机存放字符串时,改为存放每个字符在 UNICODE 字符集中的序号。目前计算机一般使用 2 个字节(16 位)来存放一个序号(DBCS),因此,这种方式存放的字符也被称作宽字节字符。比如,字符串 "中文123" 在 Windows 2000 下,内存中实际存放的是 5 个序号:
2D 4E    87 65    31 00    32 00    33 00    00 00          ← 在 x86 CPU 中,低字节在前
--    --    --    --    --    --
中    文    1    2    3    \0

一共占 10 个字节。


UTF-8的編碼形式(http://www.lihuasoft.net/article/show.php?id=2795)

UTF-8标准就是Unicode(ISO10646)标准的一种变形方式,
UTF的全称是:Unicode/UCS Transformation Format,其实有两种UTF,一种是UTF-8,一种是UTF-16,
不过UTF-16使用较少,其对应关系如下:
在Unicode中编码为 0000 - 007F 的 UTF-8 中编码形式为: 0xxxxxxx
在Unicode中编码为 0080 - 07FF 的 UTF-8 中编码形式为: 110xxxxx 10xxxxxx
在Unicode中编码为 0000 - 007F 的 UTF-8 中编码形式为: 1110xxxx 10xxxxxx 10xxxxxx


C++ 中相关实现方法 (http://www.regexlab.com/zh/encoding.htm)

声明一段字符串常量:
// ANSI 字符串,内容长度 7 字节
char     sz[20] = "中文123";

// UNICODE 字符串,内容长度 5 个 wchar_t(10 字节)
wchar_t wsz[20] = L"\x4E2D\x6587\x0031\x0032\x0033";

UNICODE 字符串的 I/O 操作,字符与字节的转换操作:
// 运行时设定当前 ANSI 编码,VC 格式
setlocale(LC_ALL, ".936");

// GCC 中格式
setlocale(LC_ALL, "zh_CN.GBK");

// Visual C++ 中使用小写 %s,按照 setlocale 指定编码输出到文件
// GCC 中使用大写 %S
fwprintf(fp, L"%s\n", wsz);

// 把 UNICODE 字符串按照 setlocale 指定的编码转换成字节
wcstombs(sz, wsz, 20);
// 把字节串按照 setlocale 指定的编码转换成 UNICODE 字符串
mbstowcs(wsz, sz, 20);

在 Visual C++ 中,UNICODE 字符串常量有更简单的表示方法。如果源程序的编码与当前默认 ANSI 编码不符,则需要使用 #pragma setlocale,告诉编译器源程序使用的编码:
// 如果源程序的编码与当前默认 ANSI 编码不一致,
// 则需要此行,编译时用来指明当前源程序使用的编码
#pragma setlocale(".936")

// UNICODE 字符串常量,内容长度 10 字节
wchar_t wsz[20] = L"中文123";

以上需要注意 #pragma setlocale 与 setlocale(LC_ALL, "") 的作用是不同的,#pragma setlocale 在编译时起作用,setlocale() 在运行时起作用。

這裡有一些轉換函式 (http://stdsoft.blogbus.com/logs/56468290.html)

static wstring ANSI2Unicode(const string & strin){
                wstring strout;   
                // 预计算所需空间大小(已包含结束字符),单位wchar_t
                int dwNum = MultiByteToWideChar (CP_ACP, 0,strin.c_str(), -1, 0 , 0);
                wchar_t * pBuffer = new wchar_t[dwNum];
                if (!pBuffer) {
                    return strout;
                }
                memset(pBuffer,0,(dwNum)*sizeof(wchar_t));
                if(MultiByteToWideChar(CP_ACP, 0, strin.c_str(),-1,pBuffer,dwNum) >= 0){
                    strout = pBuffer;
                }
                delete[] pBuffer;
                return strout;
}
static string  Unicode2UTF8(const wstring & strin){
                string strout;
                //测试所需存储空间大小(已包含结束字符),单位char
                int dwNum = WideCharToMultiByte(CP_UTF8,0,strin.c_str(),-1,0,0,0,0);
                char* pBuffer = new char[dwNum];
                if (!pBuffer){
                    return strout;
                }
                memset(pBuffer,0,dwNum);             
                if(WideCharToMultiByte(CP_UTF8,0,strin.c_str(),-1,pBuffer,dwNum,0,0) >= 0){
                    strout = pBuffer;
                }
                delete[] pBuffer;
                return strout;
}
static string  ANSI2UTF8(const string & strin){
                return Unicode2UTF8(ANSI2Unicode(strin));
}
static string  Unicode2ANSI(const wstring & strin){
                string strout;
                //测试所需存储空间大小(已包含结束字符),单位char
                int dwNum = WideCharToMultiByte(CP_ACP,0,strin.c_str(),-1,0,0,0,0);
                char* pBuffer = new char[dwNum];
                if (!pBuffer){
                    return strout;
                }
                memset(pBuffer,0,dwNum);
                BOOL use_def_char = FALSE;               if(WideCharToMultiByte(CP_ACP,0,strin.c_str(),-1,pBuffer,dwNum,"?",&use_def_char) >= 0){
                    strout = pBuffer;
                }
                delete[] pBuffer;
                return strout;
}
static wstring UTF82Unicode(const string & strin) {
                wstring strout;
               // 预计算所需空间大小(已包含结束字符),单位wchar_t
                int dwNum = MultiByteToWideChar (CP_UTF8, 0,strin.c_str(), -1, 0 , 0);
                wchar_t* pBuffer = new wchar_t[dwNum];
                if (!pBuffer) {
                    return strout;
                }
                memset(pBuffer,0,dwNum*sizeof(wchar_t));
               if(MultiByteToWideChar(CP_UTF8,0,strin.c_str(),-1,pBuffer,dwNum) >= 0) {
                    strout = pBuffer;
                }
                delete[] pBuffer;
                return strout;
}
static string  UTF82ANSI(const string & strin) {
                return Unicode2ANSI(UTF82Unicode(strin));
}

這位作者的網頁也不錯
http://shukaiyang.myweb.hinet.net/courses/cpp/unicode2ascii.zhtw.htm
http://shukaiyang.myweb.hinet.net/courses/cpp/unicodestr.zhtw.htm

C++ & MySQL 中文問題III


為了解決 MySQL 使用UTF-8 的中文問題,今天尋找到另一個連接器 mysql++
下載原始碼,編譯,準備測試。。。
這個比較麻煩,要自己編譯,還要引用到 MySQL 的 .h 檔,這意味著您必須下載 MySQL Server 的 .h
來,開始玩吧!

首先,請先下載 mysql++-3.1.0.tar.gz 與 mysql-noinstall-5.1.51-win32.zip
在這 http://tangentsoft.net/mysql++ 下載 mysql++
分別解壓縮兩個檔案
先執行 mysql++ 專案

...\Desktop\mysql++-3.1.0\vc2008\mysql++.sln

後,在 mysql-noinstall-5.1.51-win32\include 中,找到下列檔案

mysql-noinstall-5.1.51-win32\include
mysql_version.h
mysql.h
mysql_com.h
mysql_time.h
my_list.h
typelib.h
my_alloc.h

mysql-noinstall-5.1.51-win32\lib\opt
libmysql.lib (這個需再專案屬性中指定所在路徑)

這些要跟您所用的MySQL版本配合,解壓縮到 mysql++-3.1.0\lib 之中,然後把 *.cpp 的符號

#include <mysql_version.h>

改成 

#include "mysql_version.h"

這樣就會找現行目錄了



編譯前先改成 Release,並指定專案屬性中 libmysql.lib 所在路徑
編譯完成的元件,會在目錄中

mysql++-3.1.0\vc2008\Release



我已經編譯好了,適用 mysql-5.1.51-win32

文件下載地址 (mysql 5.1.51.rar)

其他 MySQL 版本,請自行編譯。

改天找時間測試 UTF-8 看看。

這有一些使用的參考文章,是 mysql++ 內附的範例,剛剛在 google 找使用範例,
找了半天,才找到一兩個,突然想到去 mysql++ 裏頭找,果然有
節錄如下

/***********************************************************************
 simple1.cpp - Example showing the simplest way to get data from a MySQL
    table with MySQL++.

 Copyright (c) 1998 by Kevin Atkinson, (c) 1999-2001 by MySQL AB, and
 (c) 2004-2009 by Educational Technology Resources, Inc.  Others may
 also hold copyrights on code in this file.  See the CREDITS.txt file
 in the top directory of the distribution for details.

 This file is part of MySQL++.

 MySQL++ is free software; you can redistribute it and/or modify it
 under the terms of the GNU Lesser General Public License as published
 by the Free Software Foundation; either version 2.1 of the License, or
 (at your option) any later version.

 MySQL++ is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with MySQL++; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 USA
***********************************************************************/

#include "cmdline.h"
#include "printdata.h"

#include <mysql++.h>

#include <iostream>
#include <iomanip>

using namespace std;

int
main(int argc, char *argv[])
{
    // Get database access parameters from command line
    mysqlpp::examples::CommandLine cmdline(argc, argv);
    if (!cmdline) {
        return 1;
    }

    // Connect to the sample database.
    mysqlpp::Connection conn(false);
    if (conn.connect(mysqlpp::examples::db_name, cmdline.server(),
            cmdline.user(), cmdline.pass())) {
        // Retrieve a subset of the sample stock table set up by resetdb
        // and display it.
        mysqlpp::Query query = conn.query("select item from stock");
        if (mysqlpp::StoreQueryResult res = query.store()) {
            cout << "We have:" << endl;
            mysqlpp::StoreQueryResult::const_iterator it;
            for (it = res.begin(); it != res.end(); ++it) {
                mysqlpp::Row row = *it;
                cout << '\t' << row[0] << endl;
            }
        }
        else {
            cerr << "Failed to get item list: " << query.error() << endl;
            return 1;
        }

        return 0;
    }
    else {
        cerr << "DB connection failed: " << conn.error() << endl;
        return 1;
    }
}

/***********************************************************************
 simple2.cpp - Retrieves the entire contents of the sample stock table
    using a "store" query, and prints it out.

 Copyright (c) 1998 by Kevin Atkinson, (c) 1999-2001 by MySQL AB, and
 (c) 2004-2009 by Educational Technology Resources, Inc.  Others may
 also hold copyrights on code in this file.  See the CREDITS.txt file
 in the top directory of the distribution for details.

 This file is part of MySQL++.

 MySQL++ is free software; you can redistribute it and/or modify it
 under the terms of the GNU Lesser General Public License as published
 by the Free Software Foundation; either version 2.1 of the License, or
 (at your option) any later version.

 MySQL++ is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with MySQL++; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 USA
***********************************************************************/

#include "cmdline.h"
#include "printdata.h"

#include <mysql++.h>

#include <iostream>
#include <iomanip>

using namespace std;

int
main(int argc, char *argv[])
{
    // Get database access parameters from command line
    mysqlpp::examples::CommandLine cmdline(argc, argv);
    if (!cmdline) {
        return 1;
    }

    // Connect to the sample database.
    mysqlpp::Connection conn(false);
    if (conn.connect(mysqlpp::examples::db_name, cmdline.server(),
            cmdline.user(), cmdline.pass())) {
        // Retrieve the sample stock table set up by resetdb
        mysqlpp::Query query = conn.query("select * from stock");
        mysqlpp::StoreQueryResult res = query.store();

        // Display results
        if (res) {
            // Display header
            cout.setf(ios::left);
            cout << setw(31) << "Item" <<
                    setw(10) << "Num" <<
                    setw(10) << "Weight" <<
                    setw(10) << "Price" <<
                    "Date" << endl << endl;

            // Get each row in result set, and print its contents
            for (size_t i = 0; i < res.num_rows(); ++i) {
                cout << setw(30) << res[i]["item"] << ' ' <<
                        setw(9) << res[i]["num"] << ' ' <<
                        setw(9) << res[i]["weight"] << ' ' <<
                        setw(9) << res[i]["price"] << ' ' <<
                        setw(9) << res[i]["sdate"] <<
                        endl;
            }
        }
        else {
            cerr << "Failed to get stock table: " << query.error() << endl;
            return 1;
        }

        return 0;
    }
    else {
        cerr << "DB connection failed: " << conn.error() << endl;
        return 1;
    }
}


/***********************************************************************
 simple3.cpp - Example showing how to use the 'use' method of retrieving
    a table, as opposed to the more common 'store' method illustrated
    by the simple2 example.

 Copyright (c) 2005-2009 by Educational Technology Resources, Inc.
 Others may also hold copyrights on code in this file.  See the CREDITS
 file in the top directory of the distribution for details.

 This file is part of MySQL++.

 MySQL++ is free software; you can redistribute it and/or modify it
 under the terms of the GNU Lesser General Public License as published
 by the Free Software Foundation; either version 2.1 of the License, or
 (at your option) any later version.

 MySQL++ is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with MySQL++; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 USA
***********************************************************************/

#include "cmdline.h"
#include "printdata.h"

#include <mysql++.h>

#include <iostream>
#include <iomanip>

using namespace std;

int
main(int argc, char *argv[])
{
    // Get database access parameters from command line
    mysqlpp::examples::CommandLine cmdline(argc, argv);
    if (!cmdline) {
        return 1;
    }

    // Connect to the sample database.
    mysqlpp::Connection conn(false);
    if (conn.connect(mysqlpp::examples::db_name, cmdline.server(),
            cmdline.user(), cmdline.pass())) {
        // Ask for all rows from the sample stock table and display
        // them.  Unlike simple2 example, we retreive each row one at
        // a time instead of storing the entire result set in memory
        // and then iterating over it.
        mysqlpp::Query query = conn.query("select * from stock");
        if (mysqlpp::UseQueryResult res = query.use()) {
            // Display header
            cout.setf(ios::left);
            cout << setw(31) << "Item" <<
                    setw(10) << "Num" <<
                    setw(10) << "Weight" <<
                    setw(10) << "Price" <<
                    "Date" << endl << endl;

            // Get each row in result set, and print its contents
            while (mysqlpp::Row row = res.fetch_row()) {
                cout << setw(30) << row["item"] << ' ' <<
                        setw(9) << row["num"] << ' ' <<
                        setw(9) << row["weight"] << ' ' <<
                        setw(9) << row["price"] << ' ' <<
                        setw(9) << row["sdate"] <<
                        endl;
            }

            // Check for error: can't distinguish "end of results" and
            // error cases in return from fetch_row() otherwise.
            if (conn.errnum()) {
                cerr << "Error received in fetching a row: " <<
                        conn.error() << endl;
                return 1;
            }
            return 0;
        }
        else {
            cerr << "Failed to get stock item: " << query.error() << endl;
            return 1;
        }
    }
    else {
        cerr << "DB connection failed: " << conn.error() << endl;
        return 1;
    }
}