2008年5月22日 星期四

文件內四種編碼方式

  ANSI:系統預設的標準文字儲存格式。ANSI是American National Standards Institute的縮寫。它成立於1918年,是一個自願性的組織,擁有超過1300個會員,包括所有大型的電腦公司。ANSI專為電腦工業建立標準,它是世界上相當重要的標準。
  Unicode:世界上所有主要指令文件的聯集,包括商業和個人電腦所使用的公用字集。當採用Unicode格式儲存文件時,可使用Unicode 控制字串輔助說明語言的文字覆蓋範圍,如阿拉伯語、希伯來語。用戶在「記事本」中輸入含有Unicode字串的文字並儲存文件時,系統會提示你必須選取「另存為」中的Unicode編碼,這些字串才不會被遺失。需要提醒大家的是,部分Windows 2000字型無法顯示所有的Unicode 串。如果發現文件中缺少了某些字串,只需將其變更為其它字型即可。
  Unicode big endian:在Big-endian處理器(如蘋果Macintosh 電腦)上建立的Unicode文件中的文字位元組(存放服務機構)排列順序,與在Intel處理器上建立的文件的文字位元組排列順序相反。最重要的位元組擁有最低的位址,且會先儲存文字中較大的一端。為使這類電腦的用戶能夠存取你的文件,可選項Unicode big-endian格式。
  UTF-8:UTF意為通用字集轉換格式(Universal Character Set Transformation Format),UTF-8是Unicode的8位元格式。如果使用只能在同類位元組內支持8個位元的重要資料一類的舊式傳輸媒體,可選項UTF-8格式

ANSI
最常見於.... BBS
你以上大學的BBS網站或new ,帖文的簽名五花八門且創意實足...
ANSI Esc控制碼
讓名字變色就要用 ANSI Esc控制碼
1.ANSI Esc控制碼 以ESC字元和 [ 開始, 這兩個字元之後的字元是參數, 還有一個設定碼字元,此字元有大.小寫的區別.
2.任一字元間不可輸入空格, ANSI Esc設定碼有兩個以上的參數時,應以分號(;)區隔. http://netlab.cse.yzu.edu.tw/~statue/arion/beginner/ansi.html
UTF-8:Unicode Translation Format-8 (UTF-8) 編碼
MS IE 瀏覽器如是簡體的目錄或檔名....
工具>網際網路選項>進階>.永遠都使用UTF-8傳送 (須重啟IE)
你可句選它,可對其下載,不會有...???檔.zip...檔情形.
什麼是 UTF-8?
首先 UCS 和 Unicode 只是分配整數給字串的編碼表. 現在存在好幾種將一串字串表示為一串字元的方法. 最顯而易見的兩種方法是將 Unicode 文本存儲為 2 個 或 4 個字元序列的串. 這兩種方法的正式名稱分別為 UCS-2 和 UCS-4. 除非另外指定, 否則大多數的字元都是這樣的(Bigendian convention). 將一個 ASCII 或 Latin-1 的文件轉換成 UCS-2 只需簡單地在每個 ASCII 字元前插入 0x00. 如果要轉換成 UCS-4, 則必須在每個 ASCII 字元前插入三個 0x00.
在 Unix 下使用 UCS-2 (或 UCS-4) 會導致非常嚴重的問題. 用這些編碼的字串串會包含一些特殊的字串, 比如 '\0' 或 '/', 它們在 檔案名和其他 C 庫函數參數里都有特別的含義. 另外, 大多數使用 ASCII 文件的 UNIX 下的工具, 如果不進行重大修改是無法讀取 16 位的字串的. 關於這些原因, 在檔案名, 文本文件, 環境變量等地方, UCS-2 不適合作為 Unicode 的外部編碼.
在 ISO 10646-1 Annex R 和 RFC 2279 裡定義的 UTF-8 編碼沒有這些問題. 它是在 Unix 風格的操作系統下使用 Unicode 的明顯的方法.

UTF-8 有一下特性:
UCS 字串 U+0000 到 U+007F (ASCII) 被編碼為字元 0x00 到 0x7F (ASCII 相容). 這意味著只包含 7 位 ASCII 字串的文件在 ASCII 和 UTF-8 兩種編碼方式下是一樣的.
所有 >U+007F 的 UCS 字串被編碼為一個多個字元的串, 每個字元都有標記位集. 因此, ASCII 字元 (0x00-0x7F) 不可能作為任何其他字串的一部分.
表示非 ASCII 字串的多字元串的第一個字元總是在 0xC0 到 0xFD 的範圍裡, 並指出這個字串包含多少個字元. 多字元串的其餘字元都在 0x80 到 0xBF 範圍裡. 這使得重新同步非常容易, 並使編碼無國界, 且很少受丟失字元的影響.
可以編入所有可能的 231個 UCS 程式碼
UTF-8 編碼字串理論上可以最多到 6 個字元長, 然而 16 位 BMP 字串最多只用到 3 字元長.
Bigendian UCS-4 字元串的排列順序是預定的.
字元 0xFE 和 0xFF 在 UTF-8 編碼中從未用到.

UTF-8 and Unicode FAQ
http://www.ctosoft.com/book/utf8.html
。 郵件編碼介紹及亂碼的解決

E-mail一般在傳送程序中都要對文件進行編碼。這是因為E-mail只能傳送ASCII碼格式的文字信息。ASCII碼為7位程式碼,非ASCII格 式的文件在傳送中必須經過編碼工具編成相應的A SCII碼進行傳輸,接收端在收到後再根據編碼規則進行解碼。若非如此就會在傳輸程序中出現編碼截位的問題,導致收信方出現亂碼。特別是中文內碼的文字, 屬於8 位程式碼,並非標準的ASCII碼形式,由於國內通行的大部分郵件伺服器都能夠處理GB內碼文件,所以可以直接傳送文件而不需要編碼,但如果要將中文郵件 發到國外或在不支持8 位(非標準ASCII碼格式)的某些郵件主機上傳輸,就會產生亂碼。具體的說就是在直接傳送中文或非ASCII碼的郵件時郵件主機無法處理,會把文件中每 個字串的第八位都濾掉(截去第八位)從而使一些信息和原始信息截然不同,或郵件完全損壞成為亂碼無法閱讀。這也是目前造成郵件亂碼的主要原因之一。如果我 們對郵件進行七位編碼然後進行傳輸解碼,就能解決截位亂碼現象。

  E-mail中一般採用UU、MIME、BINHEX三種編碼標準,當郵件出現亂碼時,很多是由於E-mail編碼不對而造成的,由於每種編碼其格式都有其各自特徵,這就給了我們一個判別的標誌。我們可以根據這些特徵進行編碼判斷並採取相應的方法來解決。

  一、UUENCODE編碼判斷及解決。
  UUENCODE內部所用算法為Base64,其格式為:
  begin 644 gx.zip Mig)0;....
...
end
  其格式特徵為在亂碼之前會有「begin xxx」後緊跟被編碼的原始檔案名稱,然後是編碼郵件內容,在最後一行為「end」。
  根據這些特徵我們判斷出編碼方式為UUENCODE方式,就可以使用一些相應DECODE軟體來解碼。具體方法有:
  (1)將Uuencode「亂碼」郵件轉寄到自己的郵箱中,再使用能夠支持UU解碼的電子郵件接收程序(如Eudora、OutLook Express等)來接收該郵件。
  (2)通過剪貼板將Uuencode「亂碼」存為文本文件,改檔案名後面為UUE,然後使用Winzip解碼。

  二、MIME方式編碼判斷及亂碼解決方法
  (一)Base64 encode編碼判斷
  Base64大體格式為:
  MIME-Version:1.0
  Content-type:text/plain;Charset="us-ascii"
  Content-transfer-encoding;base64
  ....
  在亂碼前面一般有以下幾部分「信頭」:Content - type (內容及類型),Charset(字串集)及Content-Transfer-encoding(內容傳輸編碼方式),根據以上信息非常好判斷。解決方法有:
  (1)將Base64 encode「亂碼」郵件存成一個文本文件,改檔案名後面為.UUE,然後使用Winzip解碼。
  (2)將Base64 encode「亂碼」郵件存成一個文件,將文件後面改為.EML,由OutLook Express開啟,就可以自動解碼。
  (二)QP編碼判斷
  QP編碼大體格式如下:
  =A1A=B1Z=A6N=A1I=AT=DA
  ....
  採用QP編碼的郵件也很容易判斷,只要亂碼內容有很多符號「=」就可判斷為QP編碼。QP亂碼解決方法有:
  (1)將QP-encode「亂碼」郵件轉寄到自己的郵箱中,然後用支持QP解碼的電子郵件接收程序(如Netscape mail、Eudora、OutLookExpress、Becky等)來接收該郵件。
  (2)使用Winzip對Quoted-Printable解碼。必須注意:
  a.在郵件信頭中檢查、增加這樣兩行:Mime-Version:1.0 Content-Transfer-Encoding: quoted-printable ;
  b.信頭中間不要空行,信頭和信體之間要有一個空行。這樣形成的文件,改後面名為UUE,即可雙按啟動Winzip得到解碼。

  三、其它原因造成的郵件亂碼:
  (一)HZ中文亂碼
  由於網友們可能使用不同的電子郵件收發軟體,因此,來自各個網友的郵件內容可能包含著看不懂的亂碼,例如,有時會看到「囊饉跡Z 」這樣奇怪的文字內容,實際上這是一串「簡體中文HZ」編碼。如果使用Outlook Express傳送郵件,選用HZ編碼,而郵件的接收者使用Eudora來閱讀郵件,看到的就是這種亂碼。正確的方法是,在撰寫郵件視窗中,選項「格式」 功能表下的「語言」指令,並選「簡體中文( GB2312)」項,然後傳送郵件。這時,如果你使用OutlookExpress,可以開啟「檢視」功能表點擊「語言」選項中的「簡體中文 (GB2312)」項,或者點擊工作列上「語言」後面的向下箭頭,選項「簡體中文( GB2312)」功能項,螢幕出現一個對話視窗,按擊「是」按鈕,所有郵件主旨中含有指定字串集的郵件套用新的字串集。如果你使用Eudora之類的軟 體,可以用「南極星」之類的軟體自動轉換不同的漢字編碼。如果還看不到的話,可將這些編碼文本,拷貝到一個文本編輯器中檢視。

  (二)「半個漢字」亂碼
  漢字的另一個問題是所謂的「半個漢字」亂碼。如果看到下面這串亂碼,你一定看不懂它的意思: 「把砑⒂萌砑?]⒙蛉砑暮冒槁隆薄*」
  這是由於很多英文編輯軟體以字串為服務機構來處理文本,漢字被刪除一半後,剩餘的部分會和相鄰的漢字重新組合,使得文本面目全非。因此,除了在輸入、 刪除的時候注意這種問題外,還要注意不要在英文字處理軟體中輕易使用「字串替換」功能,否則系統往往會把一個漢字的後一個字串和相鄰漢字的前一個字串當成 一個漢字處理。
  對於「半個漢字」亂碼,只要將「亂碼」郵件存成一個文本文件,然後使用以字串為服務機構的編輯軟體,將「亂碼」行的首字串刪除,後面的部分就會和相鄰的「亂碼」重新組合成可識別的漢字。
  如果上述方法不能奏效,那麼只好告訴對方正確的傳送方式,請對方重新發一份郵件給你了。
  講了這麼多,相信大家對E-MAIL的編碼有了一定瞭解,對於一般的編碼亂碼也有了一定的判別能力了。但E-MAIL亂碼不僅僅是由於編碼不同所造成的,還可能有其它的原因,比如:
  1.該郵件採用了其它少見的編碼方法,如Binhex或XXencode編碼等。如果亂碼前面有「信頭」信息(一般顯示了該郵件所用的編碼方式),即可用X ferp111或其它「智能型」Windows程序將其解碼。
  2.是否在中文環境內。如果你所用的操作系統是英文環境,而你又沒有外掛中文系統(如中文之星)或未切換為中文編碼方式,則你自然看不到中文(如R ICHWIN四通利方或南極星等),看到的只能是亂碼。注意,雙字元字串有中文簡/繁體的GB和BIG5碼及日文的JIS、EUC和朝鮮文的KSC碼等, 在G B碼環境下看其他雙字元字串時也只能看到亂碼。在這些情況下,須用轉碼工具如Richwin、南極星等進行轉換。
  3.郵件未經過編碼造成第8位字元濾掉成為無法還原的死亂碼我的文件。

  四、為了儘量避免出現亂碼問題,下面給出幾點建議:
  1.利用「附件」功能傳送文件。
  2.無法以附件方式傳送文件時,則必須在正文中傳送中文或二進制文件。方法是在你所使用的郵件系統中,選項其首選項或選項配置中的「Q uoted Printalbe」或「MIMEencoding」項。
  3.傳送重要信息時先發測試信。

2008年1月4日 星期五

如何把ubuntu 7.04(完整Linux系統,非Live CD)裝入隨身碟中(傻瓜必勝版)

這篇文章裏,我會非常清楚,一步一步地把如何在一隻4Giga的隨身碟上,安裝最新的ubuntu 7.04版詳細步驟寫出來。

Ubuntu是新一代的Linux作業系統,在國外被認為已經可以取代Windows 作業系統。圖型介面漂亮,必要的功能俱全。最重要的是,它完全免費,不光是作業系統,連用軟體都不必錢。

為什麼要裝在USB隨身碟上?我相信這是一項革命。因為,你可以把所有的軟體帶著走,包括你瀏覽器上的書簽,Cookies,軟體中的特別的設定,你不必 在每一台電腦上重新來一次。你不必提著重重的筆記型電腦。更重要的是,你不必每一套軟體裝在每一台電腦上都要再一次合法授權。Ubuntu做的到,它不必 安裝,幾乎大部份的PC都可以放上去就用。

如何裝Live Cd版本在網路上已經有人寫過了,不過我現在寫的是裝完整的版本。你可以把新的軟體套件再安裝進去USB,帶了就走,在任何一台個人電腦上使用。工作到一 半,你就簡單的存檔,關機,帶走USB隨身碟,離開,插進另一台隨便什麼電腦,開機,叫出存檔,繼續工作。這不是很棒嗎?

我知道你沒玩過Linux,不要怕,就是要把你騙過來,這篇文章就是寫給你看的。Ok,開始了。
準備工作

1。你需要上網抓ubuntu 7.04版的CD-ROM安裝光碟iso檔,ubuntu的正體中文官網在 http://www.ubuntu.org.tw/,
一般所用的PC (intel x86)安裝光碟iso版在此 http://tw.archive.ubuntu.com/ubuntu-releases/7.04/ubuntu-7.04-desktop-i386.iso
抓下來,以燒錄軟體燒成一張光碟,這張光碟就可以直接開機執行一般的工作,它也可以拿來安裝。也有AMD,甚至Mac的版本,官網上都有。

2。你必需要有一隻隨身碟,至少要有4G。當然你也可以把ubuntu安裝在硬碟上,可以和winodws系統並存,不過這在ubuntu官網上有詳細的說明,就不是本文的目的了。
我因此去買了一隻宇瞻Apacer AH320 4G的隨身碟,在順發買的,定價1190元。它是賣場上最便宜的一隻,最重要的是它標明了和Linux相容。



光碟燒好,隨身碟準備好,還要了解電腦開機啟動程序一下。先別急著裝,下面這段如果你不太熟,多看幾遍。因為過程中無法上網,會不知道怎麼辦。如果你已很熟,可以跳到ubuntu開機畫面那兒。

我們先把光碟放進DVD/CD Player,隨身碟插入USB裏。退出windows,重新開機。一般的主機版,按下電源開機之後,馬上按『Delete』鍵,就會進入到bios設定程序之中。


此時按『Delete』


出現設定畫面,每一台的畫面可能不一樣,這台我們要設定在"Advanced BIOS Feature"裏,按『Enter』鍵進去。


裏面 First Boot Device (第一開機設備)就是我們要設定的,按『Enter』,出現下面那個框,先選"CDROM",按Enter。連按兩次ESC鍵出去,選擇存檔Yes,就會 開始以光碟開機。另外第二第三開機先設成Disabled,硬碟,Windows暫時拋棄在一邊。建議其它USB隨身碟和記憶卡也先拔掉,免得安裝搞混。

運氣好的話(一般是不錯),Ubuntu就載入了。

這是英文介面,30秒之內,按下『F2』,出現語文選單,選最後一個"繁體中文"。(為什麼在最後?每次出國回來,到台灣的班機登機門都是最後的那個,超遠,為什麼?)




按第一個進入。

這樣正體中文的ubuntu 7.04就載入了。這版叫雀躍不已的小鹿,是不是你現在的心情?

進入之後,全新的介面,怎麼辦??放心,有我在。最後學這個:

在畫面上紅色箭頭所指的地方就是瀏覽器Firefox,如果你沒用過也OK,幾乎和IE一樣。要學這個,主要的是你在開機後,就把它打開,在網址輸入:
http://www.wretch.cc/blog/billypan101
這樣就能回到這篇文章,一步一步來做下去了,所以,那個網址,給我背起來!! (抄起來也可以啦....)

所以呢,學會了也背好了,就正式開始吧。Ubuntu通常一開機就能上網,後面有我罩著,別怕。現在就去下載那個iso檔,趁這個空檔去買一隻4G的隨身碟。宅男偶而也要出門,快去!!

你回來了?在ubuntu看到這篇文章了?很好,那個Firefox的plug-in先不必管它。我們先來確定幾件事。
1.。桌面上有Install的圖示。如果沒有,你就是不聽話去下載了別的版本,(LiveCD?),出去再抓過。
2。先來確定USB隨身碟的代號,按上面的 "Places",選"Desktop"


按一下上面"Computer的圖示",看到有"USB FLAS DRIVE:"的那個圖示了嗎?

那個圖示下面有寫 "disk-1"(你的可能是disk或別的),通常這玩意兒也會出現在桌面,抄下這代號,我們不要弄錯。

3。再來的步驟有點詭異,但很必要,首先,點選畫面上方的 "Application","附屬應用程式",執行"終端機",如下圖:


通常Linux新手在這裏會被嚇壞,又不敢上網問,會被笑。其實也沒什麼,這和windows下的執行,cmd,跳進DOS視窗差不多。

在這個終端機視窗中,鍵入
『sudo -i』按Enetr,再鍵入
『sudo gedit』按Enter。目的是以root權限開啟gedit這個程式


你辦到了?真勇敢。詭異的來了,在gedit中鍵入『billypan』,然後選擇存檔,檔名也叫『billypan』,存檔位置,如下圖存在我們確定過 是usb隨身碟的那個地方。(我的是disk-1,你的有可能是別的),一定要確定存到USB隨身碟。成功了關掉gedit出去。


billypan??別罵髒話,我是有點自戀,不過這麼做是有苦心的。這些步驟不能出錯,有錯會讓你抓狂的。
再來我們要再進終端機這個程式一次,如下圖在 "ubuntu@ubunru:~$" 之後,這次鍵入『sudo grub』按Enter。


在"grub >"符號後,鍵入『find /billypan』按Enetr:


畫面上出現的 "(hd2,0)" 就是我們要記下的另個東西。你的可能是 "(hd1,0)" 或別的,總之抄下來,這非常重要。
關掉終端機視窗,再執行一次,這次我們換鍵入『sudo fdisk -l』按Enter:

再一次確定隨身碟的代號是 "/dev/sda"(因為後面是4127M,你硬碟不會這麼小吧。)。你的可能是別的,比方 "/dev/sde" 等等。抄下來。退出終端機視窗。

所以我們總共確定了,USB隨身碟竟然有三個代號,(我的分別是 disk-1, (hd2,0), /dev/sda,你的可能是另一組),Linux系統要樣搞我也沒辦法。這三個代號你的和我的可能不一樣。弄清楚,下面的就容易了。

再來就是安裝了。關掉所有視窗,按桌面上的那個Install的圖示。進入安裝步驟:

STEP 1:
不會出錯,按下一頁:


STEP 2: 不會出錯,按下一頁:


STEP 3: 不會出錯,按下一頁:


STEP 4: 會出錯!!小心:


如果要安裝在硬碟,就選預設選項。我們要裝隨身碟,該是選導引,SCSI1那個選項。可是我選這個沒有反應,不給裝。網路上我看文章有人成功的,我不知道 是不是用這個裝。我的APacer AH320隨身碟可是有標Linux相容的,而且,最後我也裝成了,但這兒就是不行。也許別的隨身碟可以,也許ubuntu正不給裝。山不轉路轉,我們改 由手動:


按下一頁,看到 "/dev/sda"了嗎,這我們確定過,是我們的隨身碟。點它,按 "New partition table" 那個圖示。


出現詢問視窗,按 "繼續"


再來要開始規劃磁碟分區,把游標移到 "free space" 上,按 "New Partition" 那個圖示:

有點可怕,對不對?這是隨身碟,怕什麼。在上面 "New partion size in megabytes"的空欄位中,填入 『3500』,表示要用掉 3500M=3.5G。你也可以填入別的數字,不過一定要大於2000。下面 "Mount point:" 空欄中,填入 『/』,就是我們通常在網址上用的那條斜線。畫面最後像上面的那樣,對照清楚了,按右下 "OK"。


在 "/dev/sda1" 下面出現了另一個 "free space",點它,再按 "New partition",這次出現的視窗中,分割大小(partiton size)和掛載點(mount point)不必去管他,預設就Ok,我們要改的的格式是 "use as ",點空白欄的右邊那個有小小的上下箭頭符號的地方,如下圖選擇 "swap":


這樣步驟4大底搞定。畫面如下,再來按 "下一頁":


STEP 5: 這裏可以匯入原先在Windows XP中的Documents and Setting一些設定,打勾填入原先Windows XP帳號密碼它就會去抓。Ubuntu想要取代Windwos的陰謀在此昭然若揭,都說了這是一場革命了。不過我現在不要讓事情太複雜化,你要玩,可以, 不然,就直接按 "下一頁"過去。


STEP 6: 填入在未來的新系統上要用的帳號和密碼。這裏就不必寫 billypan了,寫你自已的名字吧。密碼兩次要一樣。填好,按下一頁


STEP 7: 會出錯!! 小心!!
這一步是最 後,網路上有網友敗在這裏。關鍵在右下面那個 "Advanced" 圖示,你要把它按出來:


出現詢問視窗:問你要把開機程式裝在那裏?

記得我之前要你找的USB硬碟的代號嗎?那一組三個之中,有一個正是 (hd2,0),這是我的,你自已的或許是 (hd1,0)或 (hd3,)等,把你的代號填進去!!

這裏弄錯,grub往機程式就會去裝在別的地方而不是USB 隨身碟上。如果裝錯,下次進windows xp可能出現 Grub Error 21 的代號。那也沒什麼,以windows xp安裝光碟開機,進入修復主控台,在 c:\windwos>下打 『fixmbr』就能修好了。不過,我們可不要這種麼麻煩。

空欄預設是(hd0,0),把它改成你自已的usb隨身碟代號 (hd2,0) 或是(hd1,0).........,這樣才能直接用隨身碟開機玩。之後按按確定。

這個改完,就按Step 7 最後面的Install圖示,開始安裝,這要一點時間。


這時候隨身碟的led燈應該閃個不停。如果你是硬碟的燈在一直閃,那一定是沒有照我的步驟,搞不好是裝到硬碟去了....這也不必難過,乾脆以後就自暴自 棄的投入ubuntu的懷抱,把 windows xp給忘了吧.....(什麼?你硬碟裏的A片還沒有copy出來?沒關係,ubuntu也可以用BT,再抓就有了....)

裝完後畫面消失,船過水無痕。最後我們還要進行一步,把grub開機程式設成USB隨身碟。
再一次打開終端機視窗,鍵入『sudo -i』,按Enter,再鍵入『sudo gedit』按Enter:


這次在gedit視窗中,我們要去開啟位在 "disk-1"的檔案。它在子目錄\boot\grub\之下,打開 menu.lst這個檔案:


開啟之後, 按上面的 "Search",選擇 "Replace..."這個功能:


把那個 (hd2,0) (再提醒一次,你的代號可能是別的),填入 "Search for" 空欄中..
下面那個 "Replace with",就直接填入 (hd0,0),意思是要以隨身機當第一開機選項。
填好後按 "Replaced All"圖示,然後在gedit中存檔,出去。


這樣基本上大功告成。按畫面最右上角那個紅色圖示,關機了。

重開機時按delete,我們把開機裝置改成USB-HDD。其它的第2第3設備都選disable,這樣,就進入我們的隨身系統了。


開機時會先進入Grub的選項,按預設項目進入,不然等10秒也可以。開機我算過,大概是120秒。還不錯。我現在正用這個 USB系統上網打部落格的,現在我的桌面如下:


你可以看到,我加裝套件後,youtube可以上,嘸蝦米可以用,可以看.rmvb檔。桌面可以到和Windwos xp十分接近。至於怎麼做,那是下一帖了。網路上文章也不少。現在,我已經愛上ubuntu,決定不買Vista了。(ubuntu也有3D桌面)

最後還有一點。我把這隻UCB隨身碟帶去辦公室的電腦,改bios,開機:





系統完全正常運作,就和在家裏的一樣。youtube,書簽,嘸蝦米,桌面完全一樣。這可不是LiveCD能做到的。隨身免費完整作業系統終於大功告成。

這時候,眼淚不禁流了出來......


本文歡迎在非商業用途下自由引用轉載,但請註明出處。


2007/05/05 補充: 移到第二台電腦時,有時會出現顯卡不相容的問題,這有解決方法,在此: 把我們的 Ubuntu 7.04 USB 隨身作業系統改造成任何電腦都能開機

Ubuntu無法新增檔案? root 密碼是甚麼?

我在ubuntu7.04 上安裝LAMP(Linux + Apache + MySQL + PHP),可是我卻無法在 /var/www 資料夾中新增檔案,才發現,要以root登入才行,卻又不知道密碼。

這才趕緊上網查資料,以下是wiki Ubuntu7.04Install 查到的

Ubuntu 管理方式是採用 sudo 管理,所以關閉 root 帳號。而安裝時的帳號密碼就是 sudoer ,可以用這帳號進行系統管理。若還是要 root 帳號密碼,請輸入

sudo passwd root

通常建議還是設定比較好,因為某些狀況還是要 root 密碼進行。
重新登入後,又發現無法以root身份登入
再次上網查詢,在安裝Ubuntu 7.04 (Feisty Fwan)得知
和 其他Unix/Linux不同,Ubuntu把最高權力使用者 - 系統管理員 root 收起,禁止直接登入。再用sudo建立了一個新的使用者級別,需要進行系統管理時只需要鍵入自己的密碼。相對一般GNU/Linux需要同時記得root 和一個一般使用者兩個密碼,Ubuntu只需記一個使用者密碼的方式在桌面管理方就顯得簡單而安全得多。如果你需要進行系統管理工作,只需要在命令模式或 終端機(Terminal)打「sudo -i」進入root使用者就可以了:

$ sudo -i
Password: 你的密碼


實際測試才發現這個方法也沒用,只好再度上網求救。
後來,經由高手 ericsk 的幫忙,終於解決這個問題。
我建議你用
sudo chown -R 使用者名稱 /var/www
來把 /var/www 的權限給某個使用者讓他來操作。

當然如果你有潔癖的話,也可以到 /etc/apache2/sites-available/default 裡去改預設的 Document 位址

2007年11月2日 星期五

VB圖像處理之圖像的亮度對比度調整

  在圖像處理中,恐怕大家最熟悉的就是對於圖像的亮度和對比度調整了。
  前面一定也有很多人寫過這樣的文章了,但是想把我的這個系列作一個完整的小結,我就再囉嗦一番了。
  還是以24位色圖像為例子,每種色彩都可以用0-255,一共256種深度來表示。如果我們把它畫在一個二維坐標上,正好是一條直線。
  比如我們將像素的色深作為橫坐標,輸出色深作為縱坐標的畫,正好是一條經過原點(0,0)的45度斜線。


   如圖中直線A所表示的,角T為45度,表示它的對比度正好為1。那麼很容易就可以寫出它的直線方程:Out = In * 1 ,係數1就是對比度的概念如果把條直線加上一個偏移量變成B,那麼它的直線方程就成為:Out = In * 1 + (ab) 偏移量(ab)就是亮度的增量。
  只要有初中的代數知識就很容易看出它滿足一條直線方程:Y= A * X + B。
   但是,我們這裡要處理的情況稍微有些不同,在圖像處理中,對比度和亮度要分別對待。不能因為改變而改變亮度,因為我們習慣上把灰色(127,127)這 一點作為中心點。比如,我們加大了對比度,原來的直線A就變成如直線D所表示的,在改變了對比度的同時,也增加了亮度(ab),而我們心目中的變化應該是 入直線C那樣。也就是說,我們把(127,127)這一點映射成了坐標系的原點。
  那麼我們就要把原來的直線公式修改成:Y=( X - 127 ) * A + B + 127。A表示對比度,B表示亮度增量。我們驗證一下:只要亮度增量 B=0,無論怎麼改變對比度 A,該直線始終通過中心點(127,127),也就是說改變對比度的同時,亮度沒有改變。
  由此,我們就可以推導出顏色的對比度亮度計算公式了:

NewRed = (OldRed -127 ) * A + 127+ B
NewGreen = (OldGreen -127 ) * A + 127+B
NewBlue = (OldBlue -127 ) * A + 127+B

  現在你是否已經準備著手用這個公式來寫出你自己的亮度對比度子程序了呢?
  慢著,再多做一步吧。我們是在遍程序,不是在做初中代數考試。這多出來的一步將使你的程序的執行效率更高一些。
  我們把上面的公式再推導一下:

  Y=( X - 127 ) * A + B + 127 => Y = X * A - 127 * A + 127+B (1)
  令:B = B -127 * A +127 (2)

  由上面(1),(2)兩步,得到一個新的公式:Y = X * A + B
  咦?怎麼又變回來了??
  是的公式的形式確實是變回來了,不過B所代表的東西已經不同了。
  或許你又會說我這是多此一舉,請聰明的讀者想像一下:在一個普通的圖片做亮度對比度運算的時候,我們上面這些小小的變化將帶來什麼樣的效率提升。假設一張圖片大小是1027*768
  一共有786432個像素,而每個像素又要分別計算紅綠藍三種顏色。
  那麼,上述這個公式就需要計算786432 * 3 = 2359296 次,經過這麼多次運算的放大,哪怕是小小的一個重複計算都將浪費很長的時間。
  因為在調用子程序的時候亮度和對比度都已經確定,那麼B = B -127 * A +127這一步就可以放在循環的外面先作好。從而減少了程序的運算時間。
  下面給出我的程序,以供參考:

Public Sub BrightnessAndContrast(ByVal RedOffset As Long, ByVal GreenOffset As Long, ByVal BlueOffset As Long, Optional ByVal RedContrast As Single = 1, Optional ByVal GreenContrast As Single = 1, Optional ByVal BlueContrast As Single = 1)
 Dim X As Long
 Dim Y As Long
 Dim MidR As Integer
 Dim MidG As Integer
 Dim MidB As Integer
 Dim Max As Long
 On Error GoTo ErrLine
 Done = False
 TimeFilter = timeGetTime
 MidR = RedOffset - 127 * (RedContrast - 1) '計算新的位移量B
 MidG = GreenOffset - 127 * (GreenContrast - 1)
 MidB = BlueOffset - 127 * (BlueContrast - 1)
 Max = 255
 For X = 0 To OutPutWid
  For Y = 0 To OutPutHei
   R = ColOut(2, X, Y)
   G = ColOut(1, X, Y)
   B = ColOut(0, X, Y)
   R = R * RedContrast + MidR '計算Y = X * A + B
   G = G * GreenContrast + MidG
   B = B * BlueContrast + MidB
   If R > Max Then R = Max '輸出值判斷是否在0到255之間
   If R < 0 Then R = 0
   If G > Max Then G = Max
   If G < 0 Then G = 0
   If B > Max Then B = Max
   If B < 0 Then B = 0
   ColOut(2, X, Y) = R
   ColOut(1, X, Y) = G
   ColOut(0, X, Y) = B
  Next
 Next
 Done = True
 TimeFilter = timeGetTime - TimeFilter
 Exit Sub
ErrLine:
 MsgBox Err.Description
 Done = True
End Sub

  因為在亮度對比度的過程中會出現計算值超出(0,255)的範圍,因此需要對它做一個判斷,把結果限定在這個範圍之內。
  這個程序很簡單,可以根據給定的紅綠藍的亮度偏移量和對比度參數計算。由於把三種顏色的6個參數分開,也可以只調整單獨的一種顏色。
  還有一個好處,就是當你將對比度參數設為負值的時候,可以直接得到原圖片的反色輸出。(這也是將前面的坐標系原點移動到127這一點的一個好處。)
  下面是用我的程序處理得到的效果:
  原圖:


  亮度+20,對比度1.5效果:


  對比度 -1,反相色彩效果:


  作為這個系列的最後一篇文章,我在我的程序ImageCast中所用到的所效果的算法和主要代碼都已經貼出來了。也算是對自己對大家的一個小小交待吧。

2007年11月1日 星期四

直接使用色彩設定

z RGB 色彩有效範圍
y0 16,777,215 (&HFFFFFF&)
z色彩設定 : 4-位元的整數
y高位元數等於 0,而較低的三個位元,從最低到最高顯著的位元組,分別決定了紅、綠、藍三種色彩的數目
y紅、綠、藍三種元件皆使用 0 255 (&HFF) 之間的數值表示
z十六進位數指定色彩語法:
y&HBBGGRR&
y每個部分都是兩位從 00 FF 的十六進位數。中間值為 80
&H808080&
[object.] point (x, y)
z傳回指定位置的色彩值:
PointColor = Point (500, 500)

調整圖形亮度

改變Picture亮度

●這個範例用到的物件有Picture1及Command1(執行命令用)。
●這個範例是利用Point函數來取得RGB顏色數值,用運算式提高或減低數值,然後在原圖以改變後的數值當顏色再重新畫圖。

Private Sub Command1_Click()
  Dim Bright As Integer '亮度
  Bright = 20 '設亮度增20%(若用負數則亮度變暗如-20)
  Picture1.AutoRedraw = True
  For Y = 1 To Picture1.ScaleHeight Step 15
    For X = 1 To Picture1.ScaleWidth Step 15
      Tmp& = Picture1.Point(X, Y)
      If Len(Hex(Tmp&)) < color="blue">Then RS = Right(Hex(Tmp&), 2)
      If Len(Hex(Tmp&)) = 6 Then
        BS = Left(Hex(Tmp&), 2)
        GS = Mid(Hex(Tmp&), 3, 2)
      End If
      If Len(Hex(Tmp&)) = 4 Then
        GS = Left(Hex(Tmp&), 2)
      End If
      If Len(Hex(Tmp&)) > 1 Then R = Val("&H" + RS) Else R = 0
      If Len(Hex(Tmp&)) > 3 Then G = Val("&H" + GS) Else G = 0
      If Len(Hex(Tmp&)) > 5 Then B = Val("&H" + BS) Else B = 0
      If Len(Hex(Tmp&)) = 8 Then R = 1: G = 1: B = 1
      PerC = (100 + Bright) / 100
      R = Fix(R * PerC): G = Fix(G * PerC): B = Fix(B * PerC)
      If R > 255 Then R = 255
      If G > 255 Then G = 255
      If B > 255 Then B = 255
      If R = 0 Then R = Bright
      If G = 0 Then G = Bright
      If B = 0 Then B = Bright
      Picture1.PSet (X, Y), RGB(R, G, B)
    Next
  Next
  Picture1.AutoRedraw = False
End Sub

2007年10月25日 星期四

鍵盤控制

鍵盤是我們使用電腦的一個很重要的輸入設備了,即使在滑鼠大行其道的今天,很多程式依然離不開鍵盤來操作。但是有時候,一些重複性的,很繁瑣的鍵盤操作總會 讓人疲憊,於是就有了用程式來代替人們按鍵的方法,這樣可以把很多重複性的鍵盤操作交給程式來類比,省了很多精力,按鍵精靈就是這樣的一個軟體。那麼我們怎樣才能用VB來寫一個程式,達到與按鍵精靈類似的功能呢?那就讓我們來先瞭解一下windows中回應鍵盤事件的機制。

當用戶按下鍵盤上的一個鍵時,鍵盤內的晶片會檢測到這個動作,並把這個信號傳送到電腦。如何區別是哪一個鍵被按下了呢?鍵盤上的所有按鍵都有一個編碼,稱作鍵盤掃描 碼。當你按下一個鍵時,這個鍵的掃描碼就被傳給系統。掃描碼是跟具體的硬體相關的,同一個鍵,在不同鍵盤上的掃描碼有可能不同。鍵盤控制器就是將這個掃 描碼傳給電腦,然後交給鍵盤驅動程式。鍵盤驅動程式會完成相關的工作,並把這個掃描碼轉換為鍵盤虛擬碼。什麼是虛擬碼呢?因為掃描碼與硬體相關,不具有通 用性,為了統一鍵盤上所有鍵的編碼,於是就提出了虛擬碼概念。無論什麼鍵盤,同一個按鍵的虛擬碼總是相同的,這樣程式就可以識別了。簡單點說,虛擬碼就 是我們經常可以看到的像VK_A,VK_B這樣的常數,比如鍵A的虛擬碼是65,寫成16進制就是&H41,注意,人們經常用16進 制來表示虛擬 碼。當鍵盤驅動程式把掃描碼轉換為虛擬碼後,會把這個鍵盤操作的掃描碼和虛擬碼還有其他資訊一起傳遞給作業系統。然後作業系統則會把這些資訊封裝在一個消 息中,並把這個鍵盤消息插入到消息列隊。最後,要是不出意外的話,這個鍵盤消息最終會被送到當前的活動視窗那裏,活動視窗所在的應用程式接收到這個消息 後,就知道鍵盤上哪個鍵被按下,也就可以決定該作出什麼回應給用戶了。這個過程可以簡單的如下表示:
用戶按下按鍵-----鍵盤驅動程式將此事件傳遞給作業系統-----作業系統將鍵盤事件插入消息佇列-----鍵盤消息被發送到當前活動窗口。接下來可以編程實現在其中的某個環節來類比鍵盤操作了。在VB中,有多種方法可以實現鍵盤類比,我們就介紹幾種比較典型的。

1.局部級類比

從上面的流程可以看出,鍵盤事件是最終被送到活動視窗,然後才引起目的程式響應的。那麼最直接的模擬方法就是:直接偽造一個鍵盤消息發給目的程式。windows提供了幾個這樣的API函數可以實現直接向目的程式發送消息的功能,常用的有SendMessage PostMessage,它們的區別是PostMessage函數直接把消息仍給目的程式就不管了,而SendMessage把消息發出去後,還要等待目標程式返回些什麼東西才好。這裏要注意的是,類比鍵盤消息一定要用PostMessage函數才好,用SendMessage是不正確的(因為類比鍵盤消 息是不需要返回值的,不然目的程式會沒反應),切記切記!PostMessage函數的VB聲明如下:
Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
參數hwnd 是你要發送消息的目的程式上某個控制項的控制碼,參數wMsg 是消息的類型,表示你要發送什麼樣的消息,最後wParam lParam 這兩個參數是隨消息附加的資料,具體內容要由消息決定。
再來看看wMsg 這個參數,要類比按鍵就靠這個了。鍵盤消息常用的有如下幾個:
WM_KEYDOWN
表示一個普通鍵被按下
WM_KEYUP
表示一個普通鍵被釋放
WM_SYSKEYDOWN
表示一個系統鍵被按下,比如Alt
WM_SYSKEYUP
表示一個系統鍵被釋放,比如Alt
如 果你確定要發送以上幾個鍵盤消息,那麼再來看看如何確定鍵盤消息中的wParam lParam 這兩個參數。在一個鍵盤消息中,wParam 參數的含義較簡單,它表示你要發送的鍵盤事件的按鍵虛擬碼,比如你要對目的程式模擬按下A鍵,那麼wParam 參數的值就設為VK_A ,至於lParam 這個參數就比較複雜了,因為它包含了多個資訊,一般可以把它設為0,但是如果你想要你的模擬更真實一些,那麼建議你還是設置一下這個參數。那麼我們就詳細瞭解 lParam lParam 是一個long類型的參數,在記憶體中占4個位元組,寫成二進位就是 00000000 00000000 00000000 00000000 一共是32位,我們從右向左數,假設最右邊那位為第0(注意是從0而不是 從1開始計數),最左邊的就是第31位,那麼該參數的的0-15位表示鍵的發送次數等擴展資訊,16-23位元為按鍵的掃描碼,24-31位表示是按下鍵還是釋放鍵。一般習慣寫成16進制的,應該是&H00 00 00 00 ,第0-15位一般為&H0001,如果是按下鍵,那 麼24-31位為&H00,釋放鍵則為&HC0,那麼16-23位元的掃描碼怎麼會得呢?這需要用到一個API函數 MapVirtualKey,這個函數可以將虛擬碼轉換為掃描碼,或將掃描碼轉換為虛擬碼,還可以把虛擬碼轉換為對應字元的ASCII碼。

它的VB聲明如 下:
Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long
參 數wCode 表示待轉換的碼,參數wMapType 表示從什麼轉換為什麼,如果是虛擬碼轉掃描碼,則wMapType 設置為0,如果是虛擬掃描碼轉 虛擬碼,則wMapType 設置為1,如果是虛擬碼轉ASCII碼,則wMapType 設置為2.相信有了這些,我們就可以構造鍵盤事件的 lParam參數了。下面給出一個構造lParam參數的函數:
Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long

Function MakeKeyLparam(ByVal VirtualKey As Long, ByVal flag As Long) As Long
'
參數VirtualKey表示按鍵虛擬碼,flag表示是按下鍵還是釋放鍵,用WM_KEYDOWNWM_KEYUP這兩個常數表示
Dim s As String
Dim Firstbyte As String 'lparam
參數的24-31
If flag = WM_KEYDOWN Then '
如果是按下鍵
Firstbyte = "00"
Else
Firstbyte = "C0" '
如果是釋放鍵
End If
Dim Scancode As Long
'
獲得鍵的掃描碼
Scancode = MapVirtualKey(VirtualKey, 0)
Dim Secondbyte As String 'lparam
參數的16-23位元,即虛擬鍵掃描碼
Secondbyte = Right("00" & Hex(Scancode), 2)
s = Firstbyte & Secondbyte & "0001" '0001
lparam參數的0-15位,即發送次數和其他擴展資訊
MakeKeyLparam = Val("&H" & s)
End Function

這個函數像這樣調用,比如按下A鍵,那麼lParam=MakeKeyLparam(VK_A,WM_KEYDOWN) ,很簡單吧。值得注意的是,即使你發送消息時設置了lParam參數的值,但是系統在傳遞消息時仍然可能會根據當時的情況重新設置該參數,那麼目的程式收到的消息中lParam的值可能會 和你發送時的有所不同。所以,如果你很懶的話,還是直接把它設為0吧,對大多數程式不會有影響的,呵呵。
好了,做完以上的事情,現在我們可以向目的程式發送鍵盤消息了。首先取得目的程式接受這個消息的控制項的控制碼,比如目標控制碼是12345,那麼我們來對目標類比按下並釋放A鍵,像這樣:(為了簡單起見,lParam這個參數就不構造了,直接傳0)
PostMessage 12345
WM_KEYDOWNVK_A0& '按下A

PostMessage 12345
WM_UPVK_A0& '釋放A
好了,一次按鍵就完成了。現在你可以迫不及待的打開記事本做實驗,先用FindWindowEx這類API函數找到記事本程式的控制碼,再向它發送鍵盤消息, 期望記事本裏能詭異的自動出現字元。可是你馬上就是失望了,咦,怎麼一點反應也沒有?你欺騙感情啊~~~~~~~~~~55555555555555 不是的哦,接著往下看啊。
一般目的程式都會含有多個控制項,並不是每個控制項都會對鍵盤消息作出反應,只有把鍵盤消息發送給接受它的控制項才會得到期望 的反應。那記事本來說,它的編輯框其實是個edit類,只有這個控制項才對鍵盤事件有反應,如果只是把消息發給記事本的表單,那是沒有用的。現在你找出記事 本那個編輯框的控制碼,比如是54321,那麼寫如下代碼:
PostMessage 54321
WM_KEYDOWNVK_F10& '按下F1
PostMessage 54321
WM_UPVK_F10& '釋放F1
怎麼樣,是不是打開了記事本的幫助資訊?這說明目的程式已經收到了你發的消息,還不錯吧~~~~~~~~
可以馬上新問題就來了,你想模擬向記事本按下A這個鍵,好在記事本裏自動輸入字元,可是,沒有任何反應!這是怎麼一回事呢?

原來,如果要向目的程式發送字元,光靠WM_KEYDOWNWM_UP這兩個事件還不行,還需要一個事件:WM_CHAR,這個消息表示一個字元,程式需 靠它看來接受輸入的字元。一般只有ABC等這樣的按鍵才有WM_CHAR消息,別的鍵(比如方向鍵和功能鍵)是沒有這個消息的,WM_CHAR消息一般發生在WM_KEYDOWN消息之後。WM_CHAR消息的lParam參數的含義與其他鍵盤消息一樣,而它的wParam則表示相應字元的ASCII 編碼(可以輸入中文的),現在你可以寫出一個完整的向記事本裏自動寫入字元的程式了,下面是一個例子,並附有這些消息常數的具體值:
Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long

Public Const WM_KEYDOWN = &H100
Public Const WM_KEYUP = &H101
Public Const WM_CHAR = &H102
Public Const VK_A = &H41

Function MakeKeyLparam(ByVal VirtualKey As Long, ByVal flag As Long) As Long
Dim s As String
Dim Firstbyte As String 'lparam
參數的24-31
If flag = WM_KEYDOWN Then '
如果是按下鍵
Firstbyte = "00"
Else
Firstbyte = "C0" '
如果是釋放鍵
End If
Dim Scancode As Long
'
獲得鍵的掃描碼
Scancode = MapVirtualKey(VirtualKey, 0)
Dim Secondbyte As String 'lparam
參數的16-23位元,即虛擬鍵掃描碼
Secondbyte = Right("00" & Hex(Scancode), 2)
s = Firstbyte & Secondbyte & "0001" '0001
lparam參數的0-15位,即發送次數和其他擴展資訊
MakeKeyLparam = Val("&H" & s)
End Function

Private Sub Form_Load()
dim hwnd as long
hwnd = XXXXXX 'XXXXX
表示記事本編輯框的控制碼
PostMessage hwnd,WM_KEYDOWN
VK_AMakeKeyLparam(VK_A,WM_KEYDOWN) '按下A
PostMessage hwnd,WM_CHAR
ASC("A"),MakeKeyLparam(VK_A,WM_KEYDOWN) '輸入字元A
PostMessage hwnd,WM_UP
VK_AMakeKeyLparam(VK_A,WM_UP) '釋放A

End Sub

這就是通過局部鍵盤消息來類比按鍵。這個方法有一個極大的好處,就是:它可以實現後臺按鍵,也就是說他對你的前臺操作不會有什麼影響。比如,你可以用這個方法做個程式在遊戲中類比按鍵來不斷地執行某些重複的操作,而你則一邊喝茶一邊與QQ上的MM們聊得火熱,它絲毫不會影響你的前臺操作。無論目的程式是否獲 得焦點都沒有影響,這就是後臺類比按鍵的原理啦~~~~

2. 全局級模擬

你會發現,用上面的方法類比按鍵並不 是對所有程式都有效的,有的程式啊,你向它發了一大堆消息,可是它卻一點反應也沒有。這是怎麼回事呢?這就要看實際的情況了,有些程式(特別是一些遊戲) 出於某些原因,會禁止用戶對它使用類比按鍵程式,這個怎麼實現呢?比如可以在程式中檢查一下,如果發現自己不是活動視窗,就不接受鍵盤消息。或者仔細檢查一 下收到的鍵盤消息,你會發現真實的按鍵和類比的按鍵消息總是有一些小差別,從這些小差別上,目的程式就能判斷出:這是假的!是偽造的!!因此,如果用 PostMessage發送局部消息類比按鍵不成功的話,你可以試一試全局級的鍵盤消息,看看能不能騙過目的程式。
類比全局鍵盤消息常見的可以有以下一些方法:
(1)
API函數keybd_event,這個函數可以用來類比一個鍵盤事件,它的VB聲明為:
Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
參數bVk表示要類比的按鍵的虛擬碼,bScan表示該按鍵的掃描碼(一般可以傳0)dwFlags表示是按下鍵還是釋放鍵(按下鍵為0,釋放鍵為2)dwExtraInfo是擴展標誌,一般沒有用。比如要模擬按下A鍵,可以這樣:
Const KEYEVENTF_KEYUP = &H2
keybd_event VK_A, 0, 0, 0 '
按下A
keybd_event VK_A, 0, KEYEVENTF_KEYUP, 0 '
釋放A
注意有時候按鍵的速度不要太快,否則會出問題,可以用API函數Sleep來進行延時,聲明如下:
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
參數dwMilliseconds表示延時的時間,以毫秒為單位。
那麼如果要模擬按下功能鍵怎麼做呢?比如要按下Ctrl+C實現拷貝這個功能,可以這樣:
keybd_event VK_Ctrl, 0, 0, 0 '
按下Ctrl
keybd_event VK_C, 0, 0, 0 '
按下C
Sleep 500 '
延時500毫秒
keybd_event VK_C, 0, KEYEVENTF_KEYUP, 0 '
釋放C
keybd_event VK_Ctrl, 0, KEYEVENTF_KEYUP, 0 '
釋放Ctrl
好了,現在你可以試試是不是可以騙過目的程式了,這個函數對大部分的視窗程式都有效,可是仍然有一部分遊戲對它產生的鍵盤事件熟視無睹,這時候,你就要用上 bScan這個參數了。一般的,bScan都傳0,但是如果目的程式是一些DirectX遊戲,那麼你就需要正確使用這個參數傳入掃描碼,用了它可以產生正確的硬體事件消息,以被遊戲識別。這樣的話,就可以寫成這樣:
keybd_event VK_A, MapVirtualKey(VK_A, 0), 0, 0 '
按下A
keybd_event VK_A, MapVirtualKey(VK_A, 0), KEYEVENTF_KEYUP, 0 '
釋放A
以上就是用keybd_event函數來類比鍵盤事件。除了這個函數,SendInput函數也可以類比全局鍵盤事件。SendInput可以直接把一條消息插入到消息佇列中,算是比較底層的了。它的VB聲明如下:
Declare Function SendInput Lib "user32.dll" (ByVal nInputs As Long, pInputs As GENERALINPUT, ByVal cbSize As Long) As Long
參數:
nlnprts
:定義plnputs指向的結構的數目。

plnputs
:指向INPUT結構陣列的指標。每個結構代表插人到鍵盤或滑鼠輸入流中的一個事件。
cbSize
:定義INPUT結構的大小。若cbSize不是INPUT結構的大小,則函數調用失敗。
返回值:函數返回被成功地插人鍵盤或滑鼠輸入流中的事件的數目。若要獲得更多的錯誤資訊,可以調用GetlastError函數。
備註:Sendlnput函數將INPUT結構中的事件順序地插入鍵盤或滑鼠的輸入流中。這些事件與用戶插入的(用滑鼠或鍵盤)或調用keybd_eventmouse_event,或另外的Sendlnput插人的鍵盤或滑鼠的輸入流不相容。
嗯,這個函數用起來蠻複雜的,因為它的參數都是指標一類的東西。要用它來類比鍵盤輸入,先要構造一組資料結構,把你要類比的鍵盤消息裝進去,然後傳給它。為了方便起見,把它做在一個過程裏面,要用的時候直接調用好了,代碼如下:
Declare Function SendInput Lib "user32.dll" (ByVal nInputs As Long, pInputs As GENERALINPUT, ByVal cbSize As Long) As Long
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)
Type GENERALINPUT
dwType As Long
xi(0 To 23) As Byte
End Type

Type KEYBDINPUT
wVk As Integer
wScan As Integer
dwFlags As Long
time As Long
dwExtraInfo As Long
End Type

Const INPUT_KEYBOARD = 1

Sub MySendKey(bkey As Long)
'
參數bkey傳入要類比按鍵的虛擬碼即可類比按下指定鍵
Dim GInput(0 To 1) As GENERALINPUT
Dim KInput As KEYBDINPUT
KInput.wVk = bkey '
你要類比的按鍵
KInput.dwFlags = 0 '
按下鍵標誌
GInput(0).dwType = INPUT_KEYBOARD
CopyMemory GInput(0).xi(0), KInput, Len(KInput) '
這個函數用來把記憶體中KInput的資料複製到GInput
KInput.wVk = bkey
KInput.dwFlags = KEYEVENTF_KEYUP '
釋放按鍵

GInput(1).dwType = INPUT_KEYBOARD '
表示該消息為鍵盤消息
CopyMemory GInput(1).xi(0), KInput, Len(KInput)
'
以上工作把按下鍵和釋放鍵共2條鍵盤消息加入到GInput資料結構中
SendInput 2, GInput(0), Len(GInput(0)) '
GInput中存放的消息插入到消息列隊
End Sub

除了以上這些,用全局鉤子也可以類比鍵盤消息。如果你對windows中消息鉤子的用法已經有所瞭解,那麼你可以通過設置一個全局HOOK來類比鍵盤消息,比如,你可以用WH_JOURNALPLAYBACK這個鉤子來類比按鍵。WH_JOURNALPLAYBACK是一個系統級的全局鉤子,它和 WH_JOURNALRECORD的功能是相對的,常用它們來記錄並重播鍵盤滑鼠操作。WH_JOURNALRECORD鉤子用來將鍵盤滑鼠的操作忠實地記錄下來,記錄下來的資訊可以保存到檔中,而WH_JOURNALPLAYBACK則可以重現這些操作。當然亦可以單獨使用 WH_JOURNALPLAYBACK來類比鍵盤操作。你需要首先聲明SetWindowsHookEx函數,它可以用來安裝消息鉤子:
Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long,ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
先 安裝WH_JOURNALPLAYBACK這個鉤子,然後你需要自己寫一個鉤子函數,在系統調用它時,把你要模擬的事件傳遞給鉤子參數lParam所指向 的EVENTMSG區域,就可以達到類比按鍵的效果。不過用這個鉤子類比鍵盤事件有一個副作用,就是它會鎖定真實的滑鼠鍵盤,不過如果你就是想在模擬的時候不會受真實鍵盤操作的干擾,那麼用用它倒是個不錯的主意。

3.驅動級模擬

如果上面的方法你都試過了,可是你發現目的程式卻仍然頑固的不接受你類比的消息,寒~~~~~~~~~還好,我還剩下最後一招,這就是驅動級模擬:直接讀寫鍵盤的硬體埠!
有一些使用DirectX介面的遊戲程式,它們在讀取鍵盤操作時繞過了windows的消息機制,而使用DirectInput.這是因為有些遊戲對即時性控制的要求比較高,比如賽車遊戲,要求以最快速度回應鍵盤輸入。而windows消息由於是佇列形式的,消息在傳遞時會有不少延遲,有時1秒鐘也就傳遞 十幾條消息,這個速度達不到遊戲的要求。而DirectInput則繞過了windows消息,直接與鍵盤驅動程式打交道,效率當然提高了不少。因此也就 造成,對這樣的程式無論用PostMessage或者是keybd_event都不會有反應,因為這些函數都在較高層。對於這樣的程式,只好用直接讀寫鍵盤埠的方法來類比硬體事件了。要用這個方法來類比鍵盤,需要先瞭解一下鍵盤編程的相關知識。
DOS時代,當用戶按下或者放開一個鍵 時,就會產生一個鍵盤中斷(如果鍵盤中斷是允許的),這樣程式會跳轉到BIOS中的鍵盤中斷處理程式去執行。打開windows的設備管理器,可以查看到鍵盤控制器由兩個埠控制。其中&H60是資料埠,可以讀出鍵盤資料,而&H64是控制埠,用來發出控制信號。也就是,從& H60號埠可以讀此鍵盤的按鍵資訊,當從這個埠讀取一個位元組,該位元組的低7位元就是按鍵的掃描碼,而高1位則表示是按下鍵還是釋放鍵。當按下鍵時,最高 位為0,稱為通碼,當釋放鍵時,最高位為1,稱為斷碼。既然從這個埠讀數據可以獲得按鍵資訊,那麼向這個埠寫入資料就可以類比按鍵了!用過 QbASIC4.5的朋友可能知道,QB中有個OUT命令可以向指定埠寫入資料,而INP函數可以讀取指定埠的資料。那我們先看看如果用QB該怎麼寫 代碼:
假如你想模擬按下一個鍵,這個鍵的掃描碼為&H50,那就這樣
OUT &H64,&HD2 '
把資料&HD2發送到&H64埠。這是一個KBC指令,表示將要向鍵盤寫入資料
OUT &H60,&H50 '
把掃描碼&H50發送到&H60埠,表示類比按下掃描碼為&H50的這個鍵
那麼要釋放這個鍵呢?像這樣,發送該鍵的斷碼:
OUT &H64,&HD2 '
把資料&HD2發送到&H64埠。這是一個KBC指令,表示將要向鍵盤寫入資料
OUT &H60,(&H50 OR &H80) '
把掃描碼&H50與資料&H80進行或運算,可以把它的高位置1,得到斷碼,表示釋放這個鍵
好了,現在的問題就是在VB中如何向埠寫入資料了。因為在windows中,普通應用程式是無權操作埠的,於是我們就需要一個驅動程式來幫助我們實 現。在這裏我們可以使用一個元件WINIO來完成讀寫埠操作。什麼是WINIOWINIO是一個全免費的、無需註冊的、含根源程式的 WINDOWS2000埠操作驅動程式元件(可以到http://www.internals.com/上 去下載)。它不僅可以操作埠,還可以操作記憶體;不僅能在VB下用,還可以在DELPHIVC等其他環境下使用,性能特別優異。下載該組件,解壓縮後可以看到幾個檔夾,其中Release檔夾下的3個檔就是我們需要的,這3個檔是WinIo.sys(用於win xp下的驅動程式) WINIO.VXD(用於win 98下的驅動程式)WinIo.dll(封裝函數的動態連結程式庫),我們只需要調用WinIo.dll中的函數,然後 WinIo.dll就會安裝並調用驅動程式來完成相應的功能。值得一提的是這個元件完全是綠色的,無需安裝,你只需要把這3個檔複製到與你的程式相同的 檔夾下就可以使用了。用法很簡單,先用裏面的InitializeWinIo函數安裝驅動程式,然後就可以用GetPortVal來讀取埠或者用 SetPortVal來寫入埠了。好,讓我們來做一個驅動級的鍵盤類比吧。先把winio3個檔拷貝到你的程式的檔夾下,然後在VB中新建一個工 程,添加一個模組,在模組中加入下面的winio函數聲明:

Declare Function MapPhysToLin Lib "WinIo.dll" (ByVal PhysAddr As Long, ByVal PhysSize As Long, ByRef PhysMemHandle) As Long
Declare Function UnmapPhysicalMemory Lib "WinIo.dll" (ByVal PhysMemHandle, ByVal LinAddr) As Boolean
Declare Function GetPhysLong Lib "WinIo.dll" (ByVal PhysAddr As Long, ByRef PhysVal As Long) As Boolean
Declare Function SetPhysLong Lib "WinIo.dll" (ByVal PhysAddr As Long, ByVal PhysVal As Long) As Boolean
Declare Function GetPortVal Lib "WinIo.dll" (ByVal PortAddr As Integer, ByRef PortVal As Long, ByVal bSize As Byte) As Boolean
Declare Function SetPortVal Lib "WinIo.dll" (ByVal PortAddr As Integer, ByVal PortVal As Long, ByVal bSize As Byte) As Boolean
Declare Function InitializeWinIo Lib "WinIo.dll" () As Boolean
Declare Function ShutdownWinIo Lib "WinIo.dll" () As Boolean
Declare Function InstallWinIoDriver Lib "WinIo.dll" (ByVal DriverPath As String, ByVal Mode As Integer) As Boolean
Declare Function RemoveWinIoDriver Lib "WinIo.dll" () As Boolean
' ------------------------------------
以上是WINIO函數聲明-------------------------------------------
Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long
'-----------------------------------
以上是WIN32 API函數聲明-----------------------------------------

再添加下面這個過程:
Sub KBCWait4IBE() '
等待鍵盤緩衝區為空
Dim dwVal As Long
Do
GetPortVal &H64, dwVal, 1
'
這句表示從&H64埠讀取一個位元組並把讀出的資料放到變數dwVal
'GetPortVal
函數的用法是GetPortVal 埠號,存放讀出資料的變數,讀入的長度
Loop While (dwVal And &H2)
End Sub
上面的是一個根據KBC規範寫的過程,它的作用是在向鍵盤埠寫入資料前等待一段時間,後面將會用到。
然後再添加如下過程,這2個過程用來類比按鍵:

Public Const KBC_KEY_CMD = &H64 '鍵盤命令埠
Public Const KBC_KEY_DATA = &H60 '
鍵盤資料埠

Sub MyKeyDown(ByVal vKeyCoad As Long)
'
這個用來模擬按下鍵,參數vKeyCoad傳入按鍵的虛擬碼
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)

KBCWait4IBE '
發送資料前應該先等待鍵盤緩衝區為空
SetPortVal KBC_KEY_CMD, &HD2, 1 '
發送鍵盤寫入命令
'SetPortVal
函數用於向埠寫入資料,它的用法是SetPortVal 埠號,欲寫入的資料,寫入資料的長度
KBCWait4IBE
SetPortVal KBC_KEY_DATA, btScancode, 1 '
寫入按鍵資訊,按下鍵
End Sub

Sub MyKeyUp(ByVal vKeyCoad As Long)
'
這個用來模擬釋放鍵,參數vKeyCoad傳入按鍵的虛擬碼
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)
KBCWait4IBE '
等待鍵盤緩衝區為空
SetPortVal KBC_KEY_CMD, &HD2, 1 '
發送鍵盤寫入命令
KBCWait4IBE
SetPortVal KBC_KEY_DATA, (btScancode Or &H80), 1 '
寫入按鍵資訊,釋放鍵
End Sub

定義了上面的過程後,就可以用它來類比鍵盤輸入了。在表單模組中添加一個計時器控制項,然後加入以下代碼:

Private Sub Form_Load()

If InitializeWinIo = False Then
'
InitializeWinIo函數載入驅動程式,如果成功會返回true,否則返回false
MsgBox "
驅動程式載入失敗
!"
Unload Me
End If
Timer1.Interval=3000
Timer1.Enabled=True
End Sub

Private Sub Form_Unload(Cancel As Integer)
ShutdownWinIo '
程式結束時記得用ShutdownWinIo函數卸載驅動程式
End Sub

Private Sub Timer1_Timer()
Dim VK_A as Long = &H41
MyKeyDown VK_A
MyKeyUp VK_A '
模擬按下並釋放A
End Sub

運行上面的程式,就會每隔3秒鐘模擬按下一次A鍵,試試看,怎麼樣,是不是對所有程式都有效果了?
需要注意的問題:
要在VB的調試模式下使用WINIO,需要把那3個檔拷貝到VB的安裝目錄中。
鍵盤上有些鍵屬於擴展鍵(比如鍵盤上的方向鍵就是擴展鍵),對於擴展鍵不應該用上面的MyKeyDownMyKeyUp過程來模擬,可以使用下面的2個過程來準確模擬擴展鍵:
Sub MyKeyDownEx(ByVal vKeyCoad As Long) '
模擬擴展鍵按下,參數vKeyCoad是擴展鍵的虛擬碼
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)

KBCWait4IBE '等待鍵盤緩衝區為空
SetPortVal KBC_KEY_CMD, &HD2, 1 '
發送鍵盤寫入命令
KBCWait4IBE
SetPortVal KBC_KEY_DATA, &HE0, 1 '
寫入擴展鍵標誌資訊


KBCWait4IBE '
等待鍵盤緩衝區為空
SetPortVal KBC_KEY_CMD, &HD2, 1 '
發送鍵盤寫入命令
KBCWait4IBE
SetPortVal KBC_KEY_DATA, btScancode, 1 '
寫入按鍵資訊,按下鍵

End Sub

Sub MyKeyUpEx(ByVal vKeyCoad As Long) '模擬擴展鍵彈起
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)

KBCWait4IBE '等待鍵盤緩衝區為空
SetPortVal KBC_KEY_CMD, &HD2, 1 '
發送鍵盤寫入命令
KBCWait4IBE
SetPortVal KBC_KEY_DATA, &HE0, 1 '
寫入擴展鍵標誌資訊

KBCWait4IBE '
等待鍵盤緩衝區為空
SetPortVal KBC_KEY_CMD, &HD2, 1 '
發送鍵盤寫入命令
KBCWait4IBE
SetPortVal KBC_KEY_DATA, (btScancode Or &H80), 1 '
寫入按鍵資訊,釋放鍵

End Sub

還 應該注意的是,如果要從擴展鍵轉換到普通鍵,那麼普通鍵的KeyDown事件應該發送兩次。也就是說,如果我想模擬先按下一個擴展鍵,再按下一個普通鍵, 那麼就應該向埠發送兩次該普通鍵被按下的資訊。比如,我想模擬先按下左方向鍵,再按下空白鍵這個事件,由於左方向鍵是擴展鍵,空白鍵是普通鍵,那麼流程 就應該是這樣的:
[quote]MyKeyDownEx VK_LEFT '
按下左方向鍵
Sleep 200 '
延時200毫秒
MyKeyUpEx VK_LEFT '
釋放左方向鍵

Sleep 500
MyKeyDown VK_SPACE '
按下空白鍵,注意要發送兩次
MyKeyDown VK_SPACE
Sleep 200
MyKeyUp VK_SPACE '
釋放空白鍵


4.
骨灰級模擬
方法3算是很底層的類比了,我現在還沒有發現有它類比無效的程式。但是如果你用盡上面所有的方法,仍然無效的話,那麼還有最後一個方法,絕對對任何程式都會有效,那就是:把鍵盤拿出來,老老實實地按下去吧。~~~~