当前位置:首页 > .NET其他

尝试做自己的安装包 - (4) 提取文件

发表于 2017-06-14 23:50

 上一篇文章中我们讲解了如何打包文件,这一篇我们讲解如何提取文件。

打包的主要思路:将所需要打包的文件的二进制写入Resx文件中,然后运用生成项目时可以把Resx文件作为嵌入的资源的特性,把打包文件隐藏在安装包的Exe中,然后安装的时候在提取出来。

.NET提供了两个可以写Resx文件的类:ResXResourceReader和ResourceManager。我们可以看看其构造函数。

名称 说明
ResXResourceReader (Stream) 为指定的流初始化 ResXResourceReader 类的新实例。
ResXResourceReader (String) 为指定的资源文件初始化 ResXResourceReader 类的新实例。
ResXResourceReader (TextReader) 为指定的 TextReader 初始化 ResXResourceReader 类的新实例。
ResXResourceReader (Stream, AssemblyName[]) 使用流和程序集名称数组初始化 ResXResourceReader 类的新实例。
ResXResourceReader (Stream, ITypeResolutionService) 使用输入流和类型解析服务初始化 ResXResourceReader 类的新实例。
ResXResourceReader (String, AssemblyName[]) 使用 XML 资源文件名和程序集名称数组初始化 ResXResourceReader 类的新实例。
ResXResourceReader (String, ITypeResolutionService) 使用文件名和类型解析服务初始化 ResXResourceReader 类的新实例。
ResXResourceReader (TextReader, AssemblyName[]) 使用 TextReader 对象和程序集名称数组初始化 ResXResourceReader 类的新实例。
ResXResourceReader (TextReader, ITypeResolutionService) 使用文本流读取器和类型解析服务初始化 ResXResourceReader 类的新实例。
名称 说明
ResourceManager()

使用默认值初始化 ResourceManager 类的新实例。

ResourceManager(String, Assembly)

初始化 ResourceManager 类的新实例,该实例在给定的程序集中查找从指定根名称导出的文件中包含的资源。

ResourceManager(String, Assembly, Type)

初始化使用指定 ResourceSetResourceManager 类的新实例,该实例在给定的程序集中的指定根名称类的文件中查找资源。

ResourceManager(Type)

它根据指定的对象中的信息在附属程序集内查找资源来初始化 ResourceManager 类的新实例。

 比较后,我们可以发现ResourceManager才是我们想要的。因为ResXResourceReader只能读取存在与磁盘上的Resx文件的信息,而我们的Resx文件时嵌入到Exe文件当中的,所以ResourceManager才是最适合我们的。


提取文件

我们可以使用ResourceManager(String, Assembly)构造函数来创建我们想要的ResourceManager,第一个参数为Resx文件对应的命名空间加类名,第二个参数为安装包Exe所对应的Assembly。

下一步我们需要遍历资源文件中的节点,读取每个节点的二进制,然后写到磁盘上。但是纵观ResourceManager提供遍历Resx中所有节点的功能的。如下:

CreateFileBasedResourceManager(String, String, Type)
GetNeutralResourcesLanguage(Assembly)
GetObject(String)
GetObject(String, CultureInfo)
GetResourceFileName(CultureInfo)
GetResourceSet(CultureInfo, Boolean, Boolean)
GetSatelliteContractVersion(Assembly)
GetStream(String)
GetStream(String, CultureInfo)
GetString(String)
GetString(String, CultureInfo)
GetType()
InternalGetResourceSet(CultureInfo, Boolean, Boolean)
MemberwiseClone()
ReleaseAllResources()

那么我们怎么知道用户当初打包的时候到底打包了哪些文件呢。我们可以在打包文件的时候,也同时把所有的文件名都写入到某一个节点中,并且该节点的Name时固定的。提取文件之前先访问该节点,获取每个文件的文件名,然后把文件名作为节点的Name去获取对对应的二进制。其实上一篇打包的代码中我们已经考虑到这一点了,我们将所有的文件名存入了一个Name为"^FileNames^"的节点中。

这时我们就可以写提取的方法了,如下:

private bool ExtractFiles()
{
    ResourceManager resManager = new ResourceManager("WinditeInstaller.FileList", typeof(FormInstallerBase).Assembly);

    string fileNames = resManager.GetString(NODE_FILENAMES);
    string[] fileNameArray = fileNames.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
    string fileName = "";

    if (fileNameArray.Length == 0)
    {
        return true;
    }

    string installLocation = this.TLocation.Text;
    string filePath = "";

    try
    {
        if (!System.IO.Directory.Exists(installLocation))
        {
            System.IO.Directory.CreateDirectory(installLocation);

            this._currentStep += 1;
            this.RefreshProgress();
        }

        for (int i = 0; i < fileNameArray.Length; i++)
        {
            fileName = fileNameArray[i].Trim();
            filePath = System.IO.Path.Combine(installLocation, fileName);

            System.IO.File.WriteAllBytes(filePath, (byte[])resManager.GetObject(fileName));

            _extractedFiles.Add(filePath);

            this._currentStep += 1;
            this.RefreshProgress();
        }
    }
    catch (Exception e)
    {
        MessageBox.Show(e.ToString());
        return false;
    }

    return true;
}
}


本文章由创风网原创,转载请注明出处:http://www.windite.com/article/details/rlwv2d16