您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 质量控制/管理 > COM 组件设计与应用
起源及複合檔案前言西元一九九五年某個夜黑風高的晚上,我的一位老師跟我說:“小楊呀,以後寫程式就和搭積木一樣啦。你趕快學習一些OLE的技術吧......”,當時我心裡就尋思:“開什麼玩笑?搭積木方式寫程式?再過100年吧......”,但作為一名聽話的好學生,我開始在書店裡“踅摸”(注1)有關OLE的書籍(注2)。功夫不負有心人,終於買到了我的第一本COM書《OLE2高級程式設計技術》,這本800多頁的大布頭花費了我1/5的月工資呀......於是開始日夜耕讀.....功夫不負有心人,我堅持讀完了全部著作,感想是:這本書,在說什麼呐?功夫不負有心人,我又讀完了一遍大布頭,感想是:咳~~~,沒懂!功夫不負有心人,我再,我再,我再讀...感想是:哦~~~,讀懂了一點點啦,哈哈哈。............功夫不負有心人,我終於,我終於懂了。800頁的書對現在的我來說,其實也就10幾頁有用。到這時候才體會出什麼叫“書越讀越薄”的道理了。到後來,能買到的書也多了,上網也更方便更便宜了......為了讓VCKBASE上的朋友,不再經歷我曾經的痛苦、不再重蹈我“無頭蒼蠅”般探索的艱辛、為了VCKBASE的蓬勃發展、為了中國軟體事業的騰飛(糟糕,吹的太也高了)......我打算節約一些在BBS上賺分的時間,寫個系列論文,就叫“COM組件設計與應用”吧。今天是第一部分——起源。二、檔的存儲傳說350年前,牛頓被蘋果砸到了頭,於是發現了萬有引力。但到了二十一世紀的現在,任何一個技術的發明和發展,已經不再依靠聖人靈光的一閃。技術的進步轉而是被社會的需求、商業的利益、競爭的壓力、行業的滲透等推動的。微軟在Windows平臺上的組件技術也不例外,它的發明,有其必然因素。什麼是這個因素那?答案是——檔的存儲。打開記事本程式,輸入了一篇文章後,保存。——這樣的檔叫“非結構化檔”;打開試算表程式,輸入一個班的學生姓名和考試成績,保存。——這樣的檔叫“標準結構化檔”;在我們寫的程式中,需要把特定的資料按照一定的結構和順序寫到檔中保存。——這樣的檔叫“自訂結構化檔”;(比如*.bmp文件)以上三種類型的檔,大家都見的多了。那麼檔存儲就依靠上述的方式能滿足所有的應用需求嗎?恩~~~,至少從電腦發明後的50多年來,一直是夠用的了。嘿嘿,下面看看商業利益的推動作用,對檔的存儲形式產生了什麼變化吧。30歲以上的朋友,我估計以前都使用過以下幾個著名的軟體:WordStar(獨霸DOS下的英文編輯軟體),WPS(裘伯君寫的中文編輯軟體,據說當年的市場佔有率高達90%,各種電腦培訓班的必修課程),LOTUS-123(蓮花公司出品的試算表軟體)......微軟在成功地推出Windows3.1後,開始垂涎桌面辦公自動化軟體領域。微軟的OFFICE開發部門,各小組分別獨立地開發了WORD和EXCEL等軟體,並採用“自訂結構”方式,對檔進行存儲。在激烈的市場競爭下,為了打敗競爭對手,微軟自然地產生了一個念頭------如果我能在WORD程式中嵌入EXCEL,那麼用戶在購買了我WORD軟體的情況下,不就沒有必要再買LOTUS-123了嗎?!“惡毒”(中國微軟的同志們看到了這個詞,不要激動,我是加了引號的呀)的計畫產生後,他們開始了實施工作,這就是COM的前身OLE的起源(注3)。但立刻就遇到了一個嚴重的技術問題:需要把WORD產生的DOC檔和EXCEL產生的XLS檔保存在一起。方案優點缺點建立一個子目錄,把DOC、XLS存儲在這同一個子目錄中。資料隔離性好,WORD不用瞭解EXCEL的存儲結構;容易擴展。結構太鬆散,容易造成資料的損壞或丟失。不易攜帶。修改檔存儲結構,在DOC結構基礎上擴展出包容XLS的結構。結構緊密,容易攜帶和統一管理。WORD的開發人員需要通曉EXCEL的存儲格式;缺少擴展性,總不能新加一個類型就擴展一下結構吧?!以上兩個方案,都有嚴重的缺陷,怎麼解決那?如果能有一個新方案,能夠合併前兩個方案的優點,消滅缺點,該多好呀......微軟是作磁片作業系統起家的,於是很自然地他們提出了一個非常完美的設計方案,那就是把磁片檔的管理方式移植到檔中了------複合檔案,俗稱“文件中的檔案系統”。連微軟當年都沒有想到,就這麼一個簡單的想法,居然最後就演變出了COM元件程式設計的方法。可以說,複合檔案是COM的基石。下圖是磁片檔組織方式與複合檔案組織方式的類比圖:圖一、左側表示一個磁片下的檔組織方式,右側表示一個複合檔案內部的資料組織方式。三、複合檔案的特點1.複合檔案的內部是使用指針構造的一棵樹進行管理的。編寫程式的時候要注意,由於使用的是單向指標,因此當做定位操作的時候,向後定位比向前定位要快;2.複合檔案中的“流物件”,是真正保存資料的空間。它的存儲單位為512位元組。也就是說,即使你在流中只保存了一個位元組的資料,它也要佔據512位元組的檔空間。啊~~~,這也太浪費了呀?不浪費!因為檔保存在磁片上,即使一個位元組也還要佔用一個“簇”的空間那;3.不同的進程,或同一個進程的不同執行緒可以同時訪問一個複合檔案的不同部分而互不干擾;4.大家都有這樣的體會,當需要往一個檔中插入一個位元組的話,需要對整個檔進行操作,非常煩瑣並且效率低下。而複合檔案則提供了非常方便的“增量訪問”能力;5.當頻繁地刪除檔,複製檔後,磁碟空間會變的很零碎,需要使用磁片整理工具進行重新整合。和磁片管理非常相似,複合檔案也會產生這個問題,在適當的時候也需要整理,但比較簡單,只要調用一個函數就可以完成了。四、流覽複合檔案VC6.0附帶了一個工具軟體“複合檔案流覽器”,檔案名是“vc目錄\Common\Tools\DFView.exe”。為了方便使用該程式,可以把它加到工具(tools)功能表中。方法是:Tools\Customize...\Tools卡片中增加新的專案。運行DFView.exe,就可以打開一個複合檔案進行觀察了(注4)。但奇怪的是,在MicrosoftVisualStudio.NET2003中,我反而找不到這個工具程式了,汗!不過這恰好提供給大家一個練習的機會,在你閱讀完本篇文章並掌握了程式設計方法後,自己寫一個“複合檔案流覽編輯器”程式,又練手了,還有實用的價值。五、複合檔案函數複合檔案的函數和磁片目錄檔的操作非常類似。所有這些函數,被分為3種類型:WINAPI全域函數,存儲IStorage介面函數,流IStream介面函數。什麼是介面?什麼是介面函數?以後的文章中再陸續介紹,這裡大家只要把“介面”看成是完成一組相關操作功能的函數集合就可以了。WINAPI函數功能說明StgCreateDocfile()建立一個複合檔案,得到根存儲物件StgOpenStorage()打開一個複合檔案,得到根存儲物件StgIsStorageFile()判斷一個檔是否是複合檔案IStorage函數功能說明CreateStorage()在當前存儲中建立新存儲,得到子存儲物件CreateStream()在當前存儲中建立新流,得到流物件OpenStorage()打開子存儲,得到子存儲物件OpenStream()打開流,得到流物件CopyTo()複製存儲下的所有物件到目標存儲中,該函數可以實現“整理檔,釋放碎片空間”的功能MoveElementTo()移動物件到目標存儲中DestoryElement()刪除對象RenameElement()重命名對象EnumElements()枚舉當前存儲中所有的物件SetElementTimes()修改對象的時間SetClass()在當前存儲中建立一個特殊的流物件,用來保存CLSID(注5)Stat()取得當前存儲中的系統資訊Release()關閉存儲物件IStream函數功能說明Read()從流中讀取數據Write()向流中寫入資料Seek()定位讀寫位置SetSize()設置流尺寸。如果預先知道大小,那麼先調用這個函數,可以提高性能CopyTo()複製流資料到另一個流物件中Stat()取得當前流中的系統資訊Clone()克隆一個流物件,方便程式中的不同模組操作同一個流物件Release()關閉流對象WINAPI補充函數功能說明WriteClassStg()寫CLSID到存儲中,同IStorage::SetClass()ReadClassStg()讀出WriteClassStg()寫入的CLSID,相當於簡化調用IStorage::Stat()WriteClassStm()寫CLSID到流的開始位置ReadClassStm()讀出WriteClassStm()寫入的CLSIDWriteFmtUserTypeStg()寫入使用者指定的剪貼板格式和名稱到存儲中ReadFmtUserTypeStg()讀出WriteFmtUserTypeStg()寫入的資訊。方便應用程式快速判斷是否是它需要的格式資料。CreateStreamOnHGlobal()記憶體控制碼HGLOBAL轉換為流對象GetHGlobalFromStream()取得CreateStreamOnHGlobal()調用中使用的記憶體控制碼為了讓大家快速地流覽和掌握基本方法,上面所清單的函數並不是全部,我省略了“事務”函數和未實現函數部分。更全面的介紹,請閱讀MSDN。下面程式片段,演示了一些基本函數功能和調用方法。示例一:建立一個複合檔案,並在其下建立一個子存儲,在該子存儲中再建立一個流,寫入資料。voidSampleCreateDoc(){::CoInitialize(NULL);//COM初始化//如果是MFC程式,可以使用AfxOleInit()替代HRESULThr;//函數執行返回值IStorage*pStg=NULL;//根存儲介面指標IStorage*pSub=NULL;//子存儲介面指標IStream*pStm=NULL;//流介面指標hr=::StgCreateDocfile(//建立複合檔案Lc:\\a.stg,//檔案名稱STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,//打開方式0,//保留參數&pStg);//取得根存儲介面指標ASSERT(SUCCEEDED(hr));//為了突出重點,簡化程式結構,所以使用了斷言。//在實際的程式中則要使用條件判斷和異常處理hr=pStg-CreateStorage(//建立子存儲LSubStg,//子存儲名稱STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,0,0,&pSub);//取得子存儲介面指標ASSERT(SUCCEEDED(hr));hr=pSub-CreateStream(//建立流LStm,//流名稱STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,0,0,&pStm);//取得流介面指標ASSERT(SUCCEEDED(hr));hr=pStm-Write(//向流中寫入資料Hello,//資料位址5,//位元組長度(注意,沒有寫入字串結尾的\0)NULL);//不需要得到實際寫入的位元組長度ASSERT(SUCCEEDED(hr));if(pStm)pStm-Release();//釋放流指針if(pSub)pSub-Release();//釋放子存儲指標if(pStg)pStg-Release();//釋放根存儲指標::CoUninitialize()//COM釋放//如果使用AfxOleInit(),則不調用該函數}圖二、運行示例程式一後,使用DFView.exe打開觀察複合檔案的效果圖示例二:打開一個複合檔案,枚舉其根存儲下的所有物件。#includeatlconv.h//ANSI、MBCS、UNICODE轉換voidSampleEnum(){//假設你已經做過COM初始化了LPCTSTRlpFileName=_T(c:\\a.stg);HRESULThr;IStorage*pStg=NULL;
本文标题:COM 组件设计与应用
链接地址:https://www.777doc.com/doc-6278472 .html