为什么 resourceReader.GetResourceData 返回偏移 4 的“ResourceTypeCode.Stream”类型的数据

Eri*_*let 5 c# resources stream character-encoding offset

在我的函数 GetAssemblyResourceStream(下面的代码)中,我使用“assembly.GetManifestResourceStream”和“resourceReader.GetResourceData”从 Dll 读取资源。

当我从资源的字节数组设置内存流时,我必须包含 4 个字节的偏移量:

const int OFFSET = 4;
resStream = new MemoryStream(data, OFFSET, data.Length - OFFSET);
Run Code Online (Sandbox Code Playgroud)

那个偏移的原因是什么?它来自哪里?

参考:MSDN ResourceReader 类末尾的示例

另外:我制作了一个测试应用程序来更好地理解资源。该应用程序显示了我在偏移量方面的问题。我的小测试应用程序可在Github (VS 2015)

更新 2015-10-05 10h28由于答案非常低,我怀疑存在错误和/或未记录的行为。我在Connect.Microsoft.com 上报告了一个错误,并将看到结果。

2015-10-07 更新我删除了错误。我仍然认为它没有得到很好的记录和/或可以被视为一个错误,但我非常怀疑他们会在不做任何事情的情况下关闭我的请求。我希望没有人会遇到和我一样的问题。

代码:

   // ******************************************************************
    /// <summary>
    /// The path separator is '/'.  The path should not start with '/'.
    /// </summary>
    /// <param name="asm"></param>
    /// <param name="path"></param>
    /// <returns></returns>
    public static Stream GetAssemblyResourceStream(Assembly asm, string path)
    {
        // Just to be sure
        if (path[0] == '/')
        {
            path = path.Substring(1);
        }

        // Just to be sure
        if (path.IndexOf('\\') == -1)
        {
            path = path.Replace('\\', '/');
        }

        Stream resStream = null;

        string resName = asm.GetName().Name + ".g.resources"; // Ref: Thomas Levesque Answer at:
        // http://stackoverflow.com/questions/2517407/enumerating-net-assembly-resources-at-runtime

        using (var stream = asm.GetManifestResourceStream(resName))
        {
            using (var resReader = new System.Resources.ResourceReader(stream))
            {
                string dataType = null;
                byte[] data = null;
                try
                {
                    resReader.GetResourceData(path.ToLower(), out dataType, out data);
                }
                catch (Exception ex)
                {
                    DebugPrintResources(resReader);
                }

                if (data != null)
                {
                    switch (dataType) // COde from 
                    {
                        // Handle internally serialized string data (ResourceTypeCode members).
                        case "ResourceTypeCode.String":
                            BinaryReader reader = new BinaryReader(new MemoryStream(data));
                            string binData = reader.ReadString();
                            Console.WriteLine("   Recreated Value: {0}", binData);
                            break;
                        case "ResourceTypeCode.Int32":
                            Console.WriteLine("   Recreated Value: {0}", BitConverter.ToInt32(data, 0));
                            break;
                        case "ResourceTypeCode.Boolean":
                            Console.WriteLine("   Recreated Value: {0}", BitConverter.ToBoolean(data, 0));
                            break;
                        // .jpeg image stored as a stream.
                        case "ResourceTypeCode.Stream":
                            ////const int OFFSET = 4;
                            ////int size = BitConverter.ToInt32(data, 0);
                            ////Bitmap value1 = new Bitmap(new MemoryStream(data, OFFSET, size));
                            ////Console.WriteLine("   Recreated Value: {0}", value1);

                            const int OFFSET = 4;
                            resStream = new MemoryStream(data, OFFSET, data.Length - OFFSET);

                            break;
                        // Our only other type is DateTimeTZI.
                        default:
                            ////// No point in deserializing data if the type is unavailable.
                            ////if (dataType.Contains("DateTimeTZI") && loaded)
                            ////{
                            ////    BinaryFormatter binFmt = new BinaryFormatter();
                            ////    object value2 = binFmt.Deserialize(new MemoryStream(data));
                            ////    Console.WriteLine("   Recreated Value: {0}", value2);
                            ////}
                            ////break;
                            break;
                    }

                    // resStream = new MemoryStream(resData);
                }
            }
        }

        return resStream;
    }
Run Code Online (Sandbox Code Playgroud)

Eri*_*let 6

byte[]开头的4个字节是size后面的数据大小。但它完全没用,因为它是 byte[] 的一部分,而且 byte[] 的大小是已知的。此外,流的内容只是一个项目,其中 4 个字节的偏移量不能用于指示第一个项目的大小,因为不能有任何项目。

阅读ResourceReader.GetResourceData 方法文档后:我尝试了 BinaryReader 和 BinaryFormatter 都没有成功。我将继续以与之前相同的方式读取资源的内容(绕过大小并使用 BitConverter 直接转换为流)。

感谢“嘿你”让我想到了那个方向。

仅供参考。这是我的代码,但它可能不像它应该的那样准确......它对我有用,但没有经过深入测试。只是为了开始。

// ******************************************************************
/// <summary>
/// Will load resource from any assembly that is part of the application.
/// It does not rely on Application which is specific to a (UI) frameowrk.
/// </summary>
/// <param name="uri"></param>
/// <param name="asm"></param>
/// <returns></returns>
public static Stream LoadResourceFromUri(Uri uri, Assembly asm = null)
{
    Stream stream = null;

    if (uri.Authority.StartsWith("application") && uri.Scheme == "pack")
    {
        string localPath = uri.GetComponents(UriComponents.Path, UriFormat.UriEscaped);

        int indexLocalPathWithoutAssembly = localPath.IndexOf(";component/");
        if (indexLocalPathWithoutAssembly == -1)
        {
            indexLocalPathWithoutAssembly = 0;
        }
        else
        {
            indexLocalPathWithoutAssembly += 11;
        }

        if (asm != null) // Take the provided assembly, do not check for the asm in the uri.
        {
            stream = GetAssemblyResourceStream(asm, localPath.Substring(indexLocalPathWithoutAssembly));
        }
        else
        {
            if (uri.Segments.Length > 1)
            {
                if (uri.Segments[0] == "/" && uri.Segments[1].EndsWith(";component/"))
                {
                    int index = uri.Segments[1].IndexOf(";");
                    if (index > 0)
                    {
                        string assemblyName = uri.Segments[1].Substring(0, index);

                        foreach (Assembly asmIter in AppDomain.CurrentDomain.GetAssemblies())
                        {
                            if (asmIter.GetName().Name == assemblyName)
                            {
                                stream = GetAssemblyResourceStream(asmIter, localPath.Substring(indexLocalPathWithoutAssembly));
                                break;
                            }
                        }
                    }
                }
            }

            if (stream == null)
            {
                asm = Assembly.GetCallingAssembly();
                stream = GetAssemblyResourceStream(asm, localPath.Substring(indexLocalPathWithoutAssembly));
            }
        }
    }
    return stream;
}

// ******************************************************************
/// <summary>
/// The path separator is '/'.  The path should not start with '/'.
/// </summary>
/// <param name="asm"></param>
/// <param name="path"></param>
/// <returns></returns>
public static Stream GetAssemblyResourceStream(Assembly asm, string path)
{
    // Just to be sure
    if (path[0] == '/')
    {
        path = path.Substring(1);
    }

    // Just to be sure
    if (path.IndexOf('\\') == -1)
    {
        path = path.Replace('\\', '/');
    }

    Stream resStream = null;

    string resName = asm.GetName().Name + ".g.resources"; // Ref: Thomas Levesque Answer at:
    // http://stackoverflow.com/questions/2517407/enumerating-net-assembly-resources-at-runtime

    using (var stream = asm.GetManifestResourceStream(resName))
    {
        using (var resReader = new System.Resources.ResourceReader(stream))
        {
            string dataType = null;
            byte[] data = null;
            try
            {
                resReader.GetResourceData(path.ToLower(), out dataType, out data);
            }
            catch (Exception)
            {
                DebugPrintResources(resReader);
            }

            if (data != null)
            {
                switch (dataType) // COde from 
                {
                    // Handle internally serialized string data (ResourceTypeCode members).
                    case "ResourceTypeCode.String":
                        BinaryReader reader = new BinaryReader(new MemoryStream(data));
                        string binData = reader.ReadString();
                        Console.WriteLine("   Recreated Value: {0}", binData);
                        break;
                    case "ResourceTypeCode.Int32":
                        Console.WriteLine("   Recreated Value: {0}", BitConverter.ToInt32(data, 0));
                        break;
                    case "ResourceTypeCode.Boolean":
                        Console.WriteLine("   Recreated Value: {0}", BitConverter.ToBoolean(data, 0));
                        break;
                    // .jpeg image stored as a stream.
                    case "ResourceTypeCode.Stream":
                        ////const int OFFSET = 4;
                        ////int size = BitConverter.ToInt32(data, 0);
                        ////Bitmap value1 = new Bitmap(new MemoryStream(data, OFFSET, size));
                        ////Console.WriteLine("   Recreated Value: {0}", value1);

                        const int OFFSET = 4;
                        resStream = new MemoryStream(data, OFFSET, data.Length - OFFSET);

                        break;
                    // Our only other type is DateTimeTZI.
                    default:
                        ////// No point in deserializing data if the type is unavailable.
                        ////if (dataType.Contains("DateTimeTZI") && loaded)
                        ////{
                        ////    BinaryFormatter binFmt = new BinaryFormatter();
                        ////    object value2 = binFmt.Deserialize(new MemoryStream(data));
                        ////    Console.WriteLine("   Recreated Value: {0}", value2);
                        ////}
                        ////break;
                        break;
                }

                // resStream = new MemoryStream(resData);
            }
        }
    }

    return resStream;
}
Run Code Online (Sandbox Code Playgroud)