分享
 
 
 

组件介绍 (清晰,全面的快速入门读物)

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

完全剖析COM

元件(Component)......

--

一般而言,整個應用程式就是單一的一個二進位檔案,一旦由編譯器產生之後,就得等到重新編譯新版本的原始程式,應用程式的功能才有辦法更新。因此,不管是作業系統的改變、或是硬體技術的變化、還是客戶需求的變動,通通都急不得,必須等重新編譯過的程式出爐才能滿足需求!但是由於整個軟體工業進步神速,往往應用程式上市時,馬上就變舊、而且過時了。

隨著軟體工業技術變換的步調,應用程式再也不能像是過去一樣,上市後就以為是一個結束,靜靜躺在那邊不用更改。軟體發展人員必須找出一種方法,可以在應用程式上市之後仍然能夠隨時提昇應用程式的功能。這個解決的方法就是把整個程式切開來,變成由一塊一塊的組件,或者稱之為元件(Component)所組裝起來,如圖1-1。

圖1-1 把應用程式從原來單一的程式(左邊)切割成元件(右邊),以適應隨時所需的變動1

在這樣的作法下,隨著新技術的精進,我們就可以使用新的元件來取代舊的元件(如圖1-2)。現在,應用程式就不再是上市前就注定會過時的產品,而是會隨著每一次的元件替換而提昇效能。最後,只要透過現成的元件,就可以非常快速地組裝出新版的應用程式。

在傳統的觀念中,一個程式會被切成檔案、模組或是類別,然後分別加以編譯之後,連結成一個單一的程式。可是如果像剛剛所說,利用元件來組裝程式,那麼觀念就截然不同,這種建構程式的方式我們稱之為元件架構法(components architecture)。在這種方式下,每一個元件就像是一個迷你程式,都是經過編譯連結過的二進位程式碼,隨時可以使用。因此,您再也看不到單一的程式,而是透過在執行時期元件之間相互的連結,來組裝出一個程式。如果需要更新程式,就只要利用新版的元件替換掉舊的元件即可。

圖1-2新發展的 D 元件取代舊的 D2 元件

要把單一程式切成元件,可得要有一個好方法才行,我們所要使用的工具稱為COM(Component Object Model),這是一種製作元件以及從元件組裝程式的規格。事實上,微軟發展COM已經超過四年,目的就是為了要讓應用程式能更具有彈性、更能動態更新、以及更容易讓使用者自訂功能。目前,幾乎所有微軟的應用程式都使用了COM,像是微軟的ActiveX技術也是以COM元件為基礎延伸而來。

在這一本書中,我們所要介紹的是如何利用C++來製作COM元件。透過書中的範例,大家將會學習到如何建構可以用來組裝程式的元件,而且所組裝出來的程式不但在未來不會過時,還能夠隨著時間的演化而更新。

在我們深入COM的技術細節之前,讓我們先來看看利用元件組裝程式有什麼好處、以及組裝程式前所需要的條件。

元件的優點前面我們已經提過,元件架構的好處之一,就是可以隨著時間改良程式。事實上,除了這個優點外,您還會發現到元件組裝方式在應用程式的升級時所帶來的彈性與便利性。我們可以將元件的優點分成三大項:應用程式功能的自訂性、元件庫、以及分散式元件。應用程式功能的自訂性3通常,使用者都會希望能夠自訂程式的功能,就好像每個人都喜歡佈置自己的房子一樣。使用者總是希望應用程式能夠根據他們喜歡的方式來運作,而一般公司的程式師也希望應用程式能夠容易改裝,以便能夠修改成符合各個使用者自訂的需求。元件架構就剛好可以提供這樣的自訂功能,因為每個元件都可以用另一個更符合使用者需求的元件來取代。舉個例子來說,假設我們有兩個分別以vi及Emacs這兩種編輯器為基礎的元件,那麼某個使用者就可以將程式設定成使用vi,而另一個使用者如果比較喜歡Emacs,也可以將程式設定成使用Emacs,就如圖1-3所示。只要加入新的元件或是取代舊的元件,就可以輕易地自訂應用程式的功能。圖1-3 由元件建立程式更能提供自訂功能,像是圖中第一個使用者較喜歡vi編輯器,而第二個使用者則比較喜歡Emacs元件庫(component libraries)元件架構最大的優點之一,就是可以快速地開發程式。這最終的理想,就是可以從元件庫中挑選適當的元件,然後就像拼湊樂高積木一樣,由各個元件組裝成一個應用程式(如圖1-4)。4 圖1-4 多個元件可以集合成元件庫,而程式就可以由各個元件庫中的元件快速拼裝起來這種從公定的標準元件組裝程式的作法,長久以來就是軟體工程師期望實現的夢想。不過還好,隨著ActiveX控制項,也就是以前稱為OLE控制項的盛行,不管是Visual Basic、C、C++、還是Java程式設計師,現在都可以使用ActiveX控制項來加速應用程式以及網頁的開發過程。當然啦,所有的程式還是會需要一些自製的元件,但是程式主體的大部份卻都可以使用標準元件來組成。分散式元件隨著網路頻寬以及網路重要性的與日遽增,由分散於網路上的各個獨立部份來組裝程式的需求也在慢慢增加。元件架構也可以簡化開發這種分散式應用程式的過程。以主從(Client/Server)程式來說,已經將程式切割成兩個部份:用戶端以及伺服端,這可以說是已經跨出邁向元件架構的第一步了!如果程式本來就是由元件組裝起來,那麼要將程式改成分散式架構就會容易的多!這可以分成兩方面來說:首先,由於程式本來就已經依據功能性切成各個元件,因此我們可以把這些元件擺放到遠端的機器上。另一方面,由於元件具有可替換的特性,因此我們可以使用一個專門負責與遠端元件溝通的代理元件,來取代舊有的元件。舉例來說,在圖1-5中,我們將C與D這兩個元件分別放置在不同的遠端機器上,然後在本地端的機器中,我們使用C代理元件以及D代理元件這兩個新元件來取代;這兩個新元件專門負責將其他元件所提出對於C與D元件的需求,跨越網路傳遞給C與D元件。在這樣的作業方式下,本地端機器上的程式根本就不在乎元件實際所在的位置;相同的道理,遠端機器上的元件也不必擔心自己所在的位置,只要有適當的代理元件,應用程式就可以完全忽略元件實際所在的位置。圖1-5 位於網路遠端系統上的元件5現在大家應該都已經瞭解了使用元件的優點,接下來我們就要和大家說明製作元件的必備條件。然後,我們也會和大家討論COM在製作元件的過程中所扮演的角色。使用元件的先決條件使用元件的好處就在於它們可以動態地加入應用程式或是從應用程式中移除,為了具有這樣的能力,元件必需符合兩個條件:第一,元件必須是以動態連結(dynamically linking)的方式加入應用程式中;第二,元件必須隱藏(或封裝)實作的細節。如果您硬要評比那個條件比較重要,那就跟爭辯先有雞還是先有蛋的問題一樣沒有結果。事實上,這兩個條件可以說是息息相關,如果以我自己的說法,那麼動態連結可以說是使用元件的關鍵條件,而資訊隱藏則是達成動態連結的必要條件,您說那個比較重要呢?現在就讓我們來進一步分析這兩個條件。動態連結我們最終的目的,是要讓使用者可以在應用程式執行時期替換所需要的元件,雖然這樣的狀況並不見得會常常發生,但總是得具備這項功能。要做到這一點,當然就得靠動態連結的能力了。要能夠了解動態連結的重要性,最好的方法就是反證法,看看沒有動態連結的支援下,所建構起來的應用程式有什麼問題。在這種情況下,如果您想要變更系統中的某個元件,那麼就必須重新靜態連結、甚至重新編譯程式,然後再重新發佈新版本的程式。其中麻煩的是您不可能期望讓使用者自己來進行重新連結的工作,即使這些使用者都知道如何連結整個程式,他們也不見得都有適當的連結器(linker),總不可能幫所有的使用者都添購一個連結器程式吧?因此,像這樣每次元件改版時,都必須再重新靜態連結所建構起來的程式,就跟這一章一開始所提的單一程式沒有什麼兩樣了。封裝(encapsulation)6瞭解了動態連結的重要性之後,現在讓我們來看看為什麼動態連結需要封裝的幫忙才能行的通。在元件的架構下,為了要能組裝成程式,元件與元件之間必須彼此互相連接,如果想要使用新的元件替換舊的元件,就必須切斷舊元件跟系統的連接,並換上新的元件,而新的元件必須使用與舊元件相同的方式和其他元件溝通,否則就必須重寫、重新編譯、或是重新連結程式。不管元件跟應用程式是否支援動態連結,只要變更了元件之間的連接方法,就破壞了整個元件系統的規矩,那麼即使不用重新撰寫程式、也得被迫重新編譯程式了。在進一步說明封裝之前,讓我們先定義一些名詞。我們將使用其他元件的程式或元件稱為用戶端程式(Client),用戶端程式透過介面(Interface)連接上其他的元件。如果只有元件本身變更而保持介面不變,那麼用戶端程式就不需要更改。相同的道理,如果光是用戶端程式變更而介面不變,那麼所用到的各個元件也不需要改變。可是如果因為元件或用戶端程式的變更,使得介面改變,那麼介面的另外一端也需要隨著改變。因此,為了要能利用動態連結的優點,元件跟用戶端程式就必須堅持不能變更到它們之間的介面。要做到這一點,元件或是用戶端程式就必須將整個內部的實作加以封裝起來,避免因為實作上的細節而影響到介面。介面越能獨立於實作細節之外,就越不會受到用戶端程式或元件變更的影響而改變,如果介面能夠保持不變,那麼替換元件的動作就不會影響應用程式的其他部份。為了要將用戶端程式獨立於元件之外,元件就必須符合一些重要的限制,分述如下:元件必須隱藏實作時所使用的程式語言。任何用戶端程式都必須能夠使用任何元件,而不必顧慮用戶端程式或元件所使用的程式語言。如果元件與用戶端程式間必須倚賴特定的程式語言,那麼就會造成用戶端程式跟特定元件的相依性( dependency )。7 元件必須以二進位格式存在。如果元件希望能夠隱藏實作時所使用的程式語言,那麼就必須以已經編譯、連結、且能夠立即使用的格式存在。 元件的升級不能造成目前使用者的困擾。任何元件的新版本都必須要能夠同時跟新舊用戶端程式一起運作。 元件必須具有網路透通性。任何元件以及使用該元件的用戶端程式應該能夠在同一個行程( process )內、或是分處於不同的行程、甚至於位於不同的機器上執行。用戶端程式必須能夠在不管元件是在本地端還是遠端機器上時,都可以用相同的方式來使用該元件。否則的話,如果元件放在遠端機器上跟放在本地端時必須以不同的方式來處理,那麼每次只要一個本地端的元件搬移到網路上的其他位置,用戶端程式就必須重新編譯了。現在,讓我們再針對這些要求作進一步的討論。與語言無關8 許多人不認為我們前所說的第一點:語言的獨立性是元件的必要條件。為了客觀一點,讓我們假設現在有一個程式,只能使用Objective C寫成的元件來自訂功能,那麼大家不難發現,根本就不會有多少人幫我們撰寫新的元件,因為大部份的人都是使用C++。瞭解這一點後,我們只好再發明一種方法,可以讓別人使用C++ 來撰寫元件,情況自然開始好轉,有比較多的元件出現了。可惜好景不常,假設又出現了一種新的語言,叫做濃縮咖啡豆語言(譯註:這好像有暗指某語言的意思),瞬間造成風潮,每個人都開始改用這種語言,把C++ 編譯器都丟在一旁囤積灰塵。這下為了維持競爭力,只好又再想出一種方法,可以讓大家使用咖啡豆語言來撰寫元件。現在光是為了我們的應用程式,就已經有三種不同的方式來撰寫元件了!可是當我們走回現實,發現原來我們已經快跟市場脫節了,因為在我們的目標客戶中,其實有許多使用者是用Visual Basic撰寫程式,而我們的競爭廠商卻允許客戶可以使用任何語言來撰寫元件,當然也包含Visual Basic在內,這豈不是糟了。您看,如果是在語言獨立的條件下,由於任何人都可以撰寫元件,自然不會隨著程式語言的演進而過時,整個架構也就可以在競爭市場中佔有一席之地。版本(Version)問題每一個使用者都有可能使用兩個以上用到同一個元件的用戶端程式,假設其中一個應用程式是設計成使用新版本的元件,而另外一個原本是使用舊版本的元件,那麼安裝新版本的元件就不應該干擾到使用舊版本元件的應用程式。以圖1-6為例,舊的應用程式和新的應用程式一樣,都使用新的vi元件,而且都應該能夠正常運作。圖1-6 新元件不應該干擾到舊的元件,但卻應該能夠與其他新的元件共同強化應用程式9要注意的是,回溯相容不應該限制到元件的改良,而必須在仍舊支援舊應用程式的前提下,也一樣可以提昇元件的功能。接下來我們來看看利用COM製作元件,是不是能夠符合這些要件。COMCOM是一個規格,規定了製作具有動態替換能力元件的方法,也制訂了用戶端程式以及元件之間為了確立可以相互合作所應該遵循的一套標準。這個標準對於元件架構來說,就跟任何提供物件交換功能的系統一樣重要,舉例來說,如果沒有VHS影帶的標準,那麼就得碰碰運氣才知道別人要拿給您的帶子是不是可以在你家裡的放影機中播放(譯註:還記得那個VHS大戰Beta系統的年代嗎?);還有,如果花園水管的大小跟水龍頭的標準不符,不曉得您要如何灑水;當然囉,PCMCIA卡跟它們的插槽也必須遵循同樣的標準;電視跟收音機接收的訊號也有固定的標準,否則可能就雞同鴨講,不曉得在看(聽)什麼了!標準對於不同國家、不同組織、不同的人共同合作時特別重要,如果沒有標準,任何事情都沒有辦法合作完成!即使在微軟,我們也遵循特定的程式撰寫標準(至少大部份時間都遵循啦!)。COM規格其實是一份訂定我們剛剛所說元件架構標準的說明文件,而我們在這一本書中所製作的元件都遵循了這個標準,您可以在本書所附的光碟中找到COM的規格書,不過我想現在大家最好奇的,大概就是「到底什麼是COM元件?」了。COM元件是...COM元件包含的是以Win32動態連結程式庫(DLL)或是可執行檔(EXE)形式存在的可執行程式碼,以COM標準所撰寫出來的元件符合前面所提對於元件架構的所有需求。10COM元件是動態連結的形式。雖然COM使用DLL來達成動態連結元件的功能,但光是動態連結並不足以保證符合元件的架構,這些元件還必須加以封裝才行。由於COM元件符合以下幾點限制,因此要封裝起來一點也不難:COM元件具有與語言無關的特性:您幾乎可以使用包括 Ada到C之間的任何一種程序式(procedural)語言、Java、Modula-3、Oberon或Pascal來開發COM元件,而且任何一種語言,包含Smalltalk跟Visual Basic在內,都可以適度修改,以便能夠使用COM元件。事實上,現在甚至也已經有方法可以讓您撰寫巨集語言能夠使用的COM元件呢。COM元件可以用二進位的格式傳送。COM元件可以在不干擾舊有用戶端程式的前提下隨時改版,我們會在 第三章 中看到,COM提供了實作不同版本元件的標準方法。11

COM元件具有網路的透通性,用戶端程式可以將遠端機 器上的元件當成本地端機器上的元件一樣使用。

COM元件可以透過標準的方式宣告它們的存在,用戶端程式可以藉由COM的公告機制(publication scheme),動態地尋找所需要的元件。

COM元件也是提供物件導向式API或服務給其他程式的好方法,也很適合用來建立與語言無關的元件庫,供快速發展應用程式時使用。

雖然COM元件擁有我們前面所提到的這些特點,但是COM在許多方面還是非常容易引起誤解。

COM不是...

COM並不是一種電腦語言。COM可不是要拿來跟程式語言競爭,如果要去討論C++ 跟COM那一個比較好是沒有意義的,因為COM跟C++ 的目的不同,COM制訂的是撰寫元件的方法,至於您要使用哪一種語言來實作,完全隨您的喜好!在這一本書中,我們則是選擇C++ 作為實作COM元件時的語言。

COM也不是用來跟DLL競爭或是要取代DLL,COM只是使用DLL來提供元件動態連結的能力。不過話說回來,以我個人的意見,COM卻是將DLL能力運用的最好的一種形式,任何使用DLL可以解決的問題,如果換成COM元件,會解決的更好。因此,我自己只有在使用COM元件的情況下才會使用DLL。12

COM的主體也不是像Win32 API這樣的一組API或者函式,COM並沒有提供像是MoveWindow等等這類的服務(不過COM提供了一些管理元件的服務,稍後我們會加以說明)。相反的,COM只是一種撰寫元件的方式,以方便您利用物件導向式API的形式來提供服務。另外,COM也不是Microsoft Foundation Classes(MFC)之類的C++ 類別程式庫,COM提供給您的是一種發展與語言無關的元件庫的方法,但是COM卻沒有提供任何實作完成的元件庫。

COM程式庫

事實上,COM並不是只有我們所說的那一份規格,它還實作了一組API,叫做COM程式庫,提供所有用戶端程式跟元件都會用到的元件管理服務。對於想要在非Windows系統下發展COM形式元件的人來說,要自己實作出這一組API中的大部份功能並不會很困難。COM程式庫的撰寫也保證了對於所有的元件,一些最重要的動作都是一樣,不會因元件的不同而有所差異。除此之外,COM程式庫也省下發展人員實作自己的元件跟用戶端程式時所花費的時間,像是COM程式庫中大部份的程式碼都支援分散式或網路元件,在Windows系統上的分散式COM(DCOM)的實作就提供有與網路上元件溝通時所需的程式碼,這不僅僅只是省下發展人員自己撰寫網路程式碼的時間,也省下了去了解如何撰寫這些程式細節的時間。

COM的風格

我自己對於COM最喜愛的部份是COM是一種程式撰寫的風格!您可以在任何系統、任何程式語言中使用COM的程式設計風格,完全不需要任何Windows系統上的COM程式碼來撰寫類似COM形式的元件。以本書的前面八章為例,您就可以在不使用任何Windows系統程式碼的要求下,輕易改寫所有的範例程式。COM使得元件式程式設計的概念得以具體化,就像是結構化程式設計或物件導向程式設計一樣,COM也是一種組織軟體的方法,在COM規格中,就已經包含了優良的軟體設計概念在內了!

COM物超所值

COM符合我們前面所討論的有關元件架構的任何需求,像是COM使用D=LL來提供執行時期替換元件的能力,COM同時也透過以下各點保證元件可以完全利用到動態連結的優點:13

提供一套元件所必須遵循的標準

允許幾乎透明的方式,讓多個版本的元件並存

允許類似的元件可以相同的方式來使用

定義一種與語言無關的架構

支援透明化連結到遠端機器上的元件

COM嚴格地分隔用戶端程式與元件,這也是COM能力得以完全發揮的根基。 現在是不是已經勾起您對於COM的興趣了呢?在往下面的章節之前,先來看看COM的歷史吧!

COM世界簡史14

在最初的時候,發展COM是為了讓應用程式能夠更容易自訂功能、以及更具彈性。原先的目的是為了支援所謂物件連結與內崁(Object Linking and Embedding)的概念,希望創造以文件為中心的使用觀點。舉例來說,在這樣的環境下,您就可以在文字處理軟體中直接編輯文件中的試算表(worksheet),而不需要另外開啟試算表軟體。微軟自己的物件連結與內崁的版本稱為OLE,其中第一個版本的OLE使用動態資料交換(Dynamic Data Exchange,DDE)的技術來做為用戶端程式跟元件之間的通訊方式。在這個時候,COM還沒有誕生。DDE是完全建構在Windows訊息傳遞的架構上,如果要冠冕堂皇來說的話,我只能說使用DDE的確可以運作,但是DDE實在是慢的可憐!而且要把DDE程式寫的正確,實在也難了點,最重要的是,DDE的穩定性以及彈性都不夠。當然啦,這都是過去的事,現在早就已經找到改進的方法了。

這一切的解決方法就是COM。COM跟DDE比起來既簡潔、也比較快、夠穩定、彈性也大。因此在OLE的第二個版本中,就使用COM重新撰寫,使得COM成為OLE的基石。不過,由於OLE是第一個使用COM發展的系統,在不純熟的前提下,OLE並不算是COM的適當範例。一般對於OLE的評價就是過於龐大、效率緩慢、而且程式又難以撰寫,但這是因為OLE自己實作上的問題,不是使用COM的結果,這一點是大家要釐清的觀念。

儘管如此,仍然不能不重視OLE所嘗試的這一些以前沒有辦法完成的事情!有了OLE,您就可以將某家廠商的繪圖軟體所畫出來的圖片貼到其他廠商的文件處理軟體上,而且能夠直接在這個文字處理軟體上修改圖片。也正由於OLE嘗試以沒有限制的方式來提供這樣的功能,而不是強迫用戶端程式跟元件之間只能做有限的合作,使得OLE程式設計的困難度大幅增加。

現在,微軟不斷地發展以COM為基礎的新產品,而且其中有些產品的確讓人吃驚,除此之外,其他廠商也不斷地撰寫COM元件,可以說COM的未來真是前途無量!

元件結論軟體工業科技的進步神速使得兩年不到的程式就必須等著升級,面對這個問題,解決的方法就是把程式切成一些小程式、或者說是元件,讓這些元件在執行時期組裝成完整的程式,這樣,我們就可以針對個別的元件獨立升級,使得程式可以隨時間的演變而進化。COM提供了撰寫元件的標準方法,只要遵循COM標準撰寫出來的元件,就可以用來組裝程式,而不必顧慮元件的撰寫人、或是元件細部的實作方法。COM元件也可以伴隨其他的元件一起使用,要能替換元件,關鍵就在於封裝,COM透過強調元件跟用戶端程式間的連接或介面來提供封裝的特性。在接下來的章節中,我們將會看到介面對於元件的重要性,同時也會學習到實作COM介面的方法。15完全剖析COM

元件(Component)......

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有