托管资源全攻略
2006-07-19 11:30:59 来源:WEB开发网本文示例源代码或素材下载
本文内容适用于所有使用.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使用的托管资源有什么区别呢?请看下表:95/98/me/nt/2000及以上 | 依靠资源ID找到资源 | 不可以 | 使用纯资源DLL,比较麻烦 | 由程序员决定,管理混乱 | 还没想好:) |
目前还没有支持的 | 取消资源ID,改为依靠资源名,也可以依靠资源的值(内容) | 可序列化的就可以 | 使用专用的资源文件,很简单 | 由程序集统一管理(注2) |
- 注1:比如说,对一个文件夹右键选"属性"->"自定义"->"更改图标",在"浏览"中选择.exe文件,如果被系统支持则会读出.exe中打包的所有图标,反之只能读出1个;
- 注2: 有关程序集的讨论超出了本文的范围,感兴趣的读者可以参考MSDN;
.resX | 由xml标签构成的文本文件 | 文本构成的,体积当然很大啦 | 文本资源不变,其它对象用Base64算法翻译成文本 | 在写.net窗体程序时VS使用.resX文件保存资源,编译时一边将resX中的资源打包进.exe文件,一边生成.resources文件 |
.resources | 由2进制数据构成的2进制文件 | 相对于同样内容的xml文件,体积要小很多 | 文本资源转换为2进制数据,其它对象不变 |
正传2:对托管资源的操作
下面的几个System::Resources命名空间中的操作类基本可以满足所有的资源操作:
ResXResourceSet() | 直接索引或枚举XML 资源 (.resx) 文件或流中的资源 |
ResXResourceReader() | 枚举 XML 资源 (.resx) 文件或流中的资源 |
ResXResourceWriter() | 将资源写入.resx文件或输出流 |
ResourceSet() | 直接索引或枚举 .resources 文件和流或流中的资源 |
ResourceReader() | 枚举 .resources 文件和流或流中的资源 |
ResourceWriter() | 将资源写入.resources文件或输出流 |
按名称直接访问文件内特定区域的资源,只能索引 |
正传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本人原创,如要引用本文内容或代码或程序,请说明出处。
更多精彩
赞助商链接