托管資源全攻略
作者:caeser2
下載下傳源代碼
本文内容适用于所有使用.net v1.1及以上架構的語言。
前傳1:提出問題(急于了解正式内容的讀者請轉到下面的正傳部分^_^)
前段時間寫了一篇題為“VC.NET輕松實作按鈕控件自繪”的文章,其中按鈕按下時的效果是由一張圖檔呈現的。這時問題就來了,最初我将該圖檔加入資源清單(.rc)中,然後使用下面的2個讀取資源的方法;編譯通過,可執行時程式抛出異常,提示找不到資源檔案。
//方法1,從指定的hinstance中名為bitmapName的資源建立Bitmap對象
public: static Bitmap* System::Drawing::Bitmap::FromResource(IntPtr hinstance,String* bitmapName);
//方法2,使用構造函數從指定的type類中提取的resource資源初始化Bitmap對象
public: System::Drawing::Bitmap::Bitmap(Type* type,String* resource);
前傳2:分析問題
接着我就犯難了,在論壇上提出該問題,沒有得到建設性的回答,為了趕時間隻好匆匆将圖檔和程式放在一起釋出了。事後回想一下心裡挺不是滋味,将分離的資源檔案和程式檔案一起釋出,搞的目錄裡亂七八糟很不好看,為什麼加入資源的圖檔不能用呢?會不會是資源沒有打包進.exe檔案呢?于是我用2進制檢視工具打開資源圖檔和.exe檔案,做了下對比,結果發現資源圖檔已打包進.exe檔案:
資源雖已打包進.exe檔案,可是除了上面提到的那兩個方法,似乎再沒有其它讀取内部資源的函數了,那怎麼辦呢?
前傳3:看到解決問題的曙光
打開MSDN,查找有關"資源"的索引,看了半天,最後終于弄明白了,原來.net架構對通過資源編輯器(.rc)打包程序式的資源不再提供支援,而改用一種名為"托管資源"的資源。那麼這種資源與普通的非托管資源有何異同?既然不能使用Visual Stdio 200x提供的資源編輯器(.rc)打包資源,這種資源又該怎麼打包程序式?打包成功後又應該怎樣去使用呢?
正傳1:什麼是托管資源
資源可以了解為是在邏輯上由應用程式部署的任何非可執行資料,托管資源其本質也大抵如此。那麼mfc使用的rc資源和.net使用的托管資源有什麼差別呢?請看下表:
資源類型 | Windows支援度(注1) | 索引方式 | 可否打包"對象" | 獨立于.exe打包的難易度 | 資源的部署 | 其它 |
---|---|---|---|---|---|---|
.rc資源 | 95/98/me/nt/2000及以上 | 依靠資源ID找到資源 | 不可以 | 使用純資源DLL,比較麻煩 | 由程式員決定,管理混亂 | 還沒想好:) |
.net托管資源 | 目前還沒有支援的 | 取消資源ID,改為依靠資源名,也可以依靠資源的值(内容) | 可序列化的就可以 | 使用專用的資源檔案,很簡單 | 由程式集統一管理(注2) |
- 注1:比如說,對一個檔案夾右鍵選"屬性"->"自定義"->"更改圖示",在"浏覽"中選擇.exe檔案,如果被系統支援則會讀出.exe中打包的所有圖示,反之隻能讀出1個;
- 注2: 有關程式集的讨論超出了本文的範圍,感興趣的讀者可以參考MSDN;
從上表可以看出,雖然托管資源的本質概念和以往的"資源"沒有差別,但是使用方式卻被徹底颠覆了。托管資源将一個資源視為一個對象,對資源的查找引用不再使用資源ID,改為資源名,就像使用一個對象一樣;反過來給可序列化的任意對象起個好記的名字就可以當作資源儲存起來。.net托管資源的打包方式有兩種,分别是打包程序式的資源和獨立的資源檔案。獨立資源檔案的組織方式也有兩種形式,一種是擴充名為resX的xml檔案,其内部是用xml文本方式組織的;另一種是擴充名為resources的2進制檔案,其内部是以2進制方式組織的。
.net資源檔案類型 | 擴充名 | 檔案的組織方式 | 檔案體積 | 資源的儲存形式 | 其它 |
---|---|---|---|---|---|
xml檔案 | .resX | 由xml标簽構成的文本檔案 | 文本構成的,體積當然很大啦 | 文本資源不變,其它對象用Base64算法翻譯成文本 | 在寫.net窗體程式時VS使用.resX檔案儲存資源,編譯時一邊将resX中的資源打包進.exe檔案,一邊生成.resources檔案 |
resources檔案 | .resources | 由2進制資料構成的2進制檔案 | 相對于同樣内容的xml檔案,體積要小很多 | 文本資源轉換為2進制資料,其它對象不變 |
正傳2:對托管資源的操作
下面的幾個System::Resources命名空間中的操作類基本可以滿足所有的資源操作:
資源的打包方式 | 操作類 | 功能 |
---|---|---|
獨立于.exe的檔案 | 資源檔案類型 | |
xml檔案 | ResXResourceSet() | 直接索引或枚舉XML 資源 (.resx) 檔案或流中的資源 |
ResXResourceReader() | 枚舉 XML 資源 (.resx) 檔案或流中的資源 | |
ResXResourceWriter() | 将資源寫入.resx檔案或輸出流 | |
resources檔案 | ResourceSet() | 直接索引或枚舉 .resources 檔案和流或流中的資源 |
ResourceReader() | 枚舉 .resources 檔案和流或流中的資源 | |
ResourceWriter() | 将資源寫入.resources檔案或輸出流 | |
包含在.exe檔案中 | ResourceManager() | 按名稱直接通路檔案内特定區域的資源,隻能索引 |
正傳3:實戰
假設我們有一個窗體叫做form1.h,那麼他的資源檔案就是form1.resX,那麼我們是不是通過修改這個.resX檔案,就能實作資源打包程序式呢?simple1中描述的就是這個例子,設計思想如下,具體内容請下載下傳全部代碼檔案,那裡有詳細的說明,注意我均使用檔案操作方式,讀者可以自己試驗一下流操作方式:
操作 | 設計思想 | 備注 | |
---|---|---|---|
生成 | 将生成資源打包程序式 | 必須操作窗體的資源檔案(比如form1.resX)。先用ResXResourceReader("form1.resX")讀入,再用ResXResourceWriter("form1.resX")打開同一個檔案,用AddResource()将讀入的資源輸出,然後将自己的資源加上,最後用Generate()覆寫form1.resX | 1>如果添加資源的操作是在程式編譯後做的,則需要重新編譯一下! 2>一定要在視窗布局徹底完成後再做該操作,否則編譯器會在每次視窗布局後自動清理窗體自身的資源檔案! |
生成獨立的資源檔案 | 直接用ResourceWriter()/ResXResourceWriter()構造函數建立一個資源檔案,然後AddResource(),最後Generate()生成 | 這種情況建議生成體積較小的.resources檔案 | |
關于打包自定義資源 | 如果是對象則需要序列化,如果是某種檔案則做成Byte[]數組的形式比較好 | 不要打包體積太大的媒體流檔案喔:) | |
使用 | 使用本程序本窗體(form1)的資源 | ResourceManager * resources = new ResourceManager(__typeof(My::Form1)); resources->GetObject("資源名"); | 直接用資源名索引到資源,但不可以枚舉! |
使用本程序其它窗體(form2)的資源 | ResourceManager * resources = new ResourceManager(__typeof(My::Form2)); resources->GetObject("資源名"); | 同上 | |
使用外部資源 | ResXResourceReader* s1 = new ResXResourceReader("External.resX");然後枚舉或 ResXResourceSet *s2=new ResXResourceSet("External.resX"); 然後直接索引 s2->GetObject("資源名"); | 對于.resources檔案思想一樣 | |
關于使用自定義資源 | 如果是對象則反序列化即可,如果是其它格式則需要自己去解析了 | 對于其它格式,建議打包時做成Byte[]數組比較好 |
為了給大家一個使用托管資源的映像,我将simple1的程式稍微改動了一下,将已經打好包的demo1.exe拿給大家,效果如下:
外傳:應用
simple2是一個具有商業價值的托管資源生成工具,不過偶也不是那種小氣的人,于是就将其中的核心部分拿過來開源了^_^,使用它可以很友善的生成托管資源,而這種資源又可以給ASP.net、VB.net、C#.net、J++.net、Delphi.net、SmallTalk.net等等等等等等的.net語言使用,應用範圍非常廣。程式效果如下:
該程式仿Visual Stdio資源編輯器的樹界面,支援拖拽,背景為半透明^_^在樹中按下"Del"可以快速删除。樹下面那個滾動條不關偶的事哦,那是.net v1.1的Bug之一:) 另一個Bug是不可以開啟System效果,否則樹将無法繪制圖示,我猜測可能是作業系統不支援托管的圖示資源所緻。
由于程式是用Visual C++7.0 寫的,有些地方文法十分古怪,和标準ISO C++及微軟自己的CLI C++均不一樣,整個程式基本上是用指針堆出來的,大家就當僞代碼着吧^_^
總結
天呐,還沒完啊,寫完程式寫文章,累死我了,再堅持一下下就好.....:)
System::Resources命名空間中還有很多用于操作資源的類,這裡就不一一列出了,感興趣的讀者可以自己去參考MSDN,需要注意的是該命名空間中的所有類實際上是封裝了IResourceReader/IResourceWriter接口,高手們也可以自己封裝出比它更好的類:)
我在Simple1、Simple2中做了大量的注釋,如果還有什麼問題,可以在下面留言或者聯系我,我會盡量給予解答。
本文全部内容均為caeser2本人原創,如要引用本文内容或代碼或程式,請說明出處。