如何从包含其程序集和任何程序集路径的Uri加载资源? [英] How to load a resource from an Uri containing its assembly and path from any assembly?

查看:70
本文介绍了如何从包含其程序集和任何程序集路径的Uri加载资源?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想仅使用其uri从(同一应用程序的)任何程序集中加载任何资源。这两个程序集都是同一个应用程序的一部分。



因为我希望能够从任何程序集中加载,所以不能使用 App或 Application



我认为部分或答案使用的是: System.Reflection.Assembly.GetCallingAssembly()。GetManifestResourceStream(path );



但是我找不到如何从uri中正确找到/提取/解析程序集的方法?



注意:




  • 资源实际上定义为资源。

  • 资源不是WPF资源字典的标准。

  • 资源实际上是XML文件,但可以是任何东西



到目前为止(使用反射器后约3小时),使用静态System.Windows.Application.GetResourceStream(uri).Stream似乎具有资源缓存之类的优势。这很糟糕,因为它已连接到WPF(System.windows)。我正在寻找一种更好的方法(不依赖于任何特定的UI框架)来完成这项工作。

解决方案

我解决了我的问题下面定义的 LoadResourceFromUri功能出现问题。我已经完成并在

 使用系统; 
使用System.Collections;
使用System.Collections.Generic;
使用System.Diagnostics;
使用System.Drawing;
使用System.IO;
使用System.Linq;
使用System.Reflection;
使用System.Resources;
使用System.Runtime.Serialization.Formatters.Binary;
使用System.Text;
使用System.Threading.Tasks;
使用System.Windows.Resources;

命名空间HQ.Util.General
{
公共静态类ResourceHelper
{
// ************ ****************************************************** ****
///< summary>
///应将资源定义为资源而不是嵌入式资源。
///< / summary>
///< param name = resourcePath>资源路径< / param>
///< param name = assembly>如果为null,则使用调用程序集查找资源< / param>
///< returns>< / returns>
public static Uri GetLocationUri(string resourcePath,Assembly Assembly = null)
{
if(assembly == null)
{
assembly = Assembly.GetCallingAssembly();
}

resourcePath = resourcePath.Replace(’\\’,’/’);

返回新的Uri(@ pack:// application:,,, / + assembly.GetName()。Name +; component +
(resourcePath [0] == '/'?resourcePath: / + resourcePath),UriKind.Absolute);
}

// *************************************** ***********************************
///< summary>
///将从应用程序一部分的任何程序集中加载资源。
///它不依赖于特定于(UI)frameowrk的Application。
///< / 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;
}
其他
{
indexLocalPathWithoutAssembly + = 11;
}

if(asm!= null)//使用提供的程序集,不要在uri中检查asm。
{
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(AppDomain.CurrentDomain.GetAssemblies()中的Assembly asmIter)
{
if(asmIter.GetName()。Name == assemblyName)
{
流= GetAssemblyResourceStream(asmIter,localPath.Substring(indexLocalPathWithoutAssembly));
休息时间;
}
}
}
}
}

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

// *************************************** ***********************************
///< summary>
///路径分隔符为 /。路径不应以 /开头。
///< / summary>
///< param name = asm>< / param>
///< param name = path>< / param>
///< returns>< / returns>
public static Stream GetAssemblyResourceStream(Assembly asm,string path)
{
//要确保
if(path [0] =='/')
{
path = path.Substring(1);
}

//要确保
if(path.IndexOf('\\')== -1)
{
path = path.Replace('\\','/');
}

Stream resStream = null;

字符串resName = asm.GetName()。Name + .g.resources; //参考:Thomas Levesque的答案:
// http://stackoverflow.com/questions/2517407/enumerating-net-assembly-resources-at-runtime

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

if(data!= null)
{
switch(dataType)//来自
的COde {
//内部处理序列化的字符串数据(ResourceTypeCode成员)。
case ResourceTypeCode.String:
BinaryReader reader = new BinaryReader(new MemoryStream(data));
字符串binData = reader.ReadString();
Console.WriteLine(重新创建的值:{0},binData);
休息时间;
case ResourceTypeCode.Int32:
Console.WriteLine(重新创建的值:{0},BitConverter.ToInt32(data,0));
休息时间;
case ResourceTypeCode.Boolean:
Console.WriteLine(重新创建的值:{0},BitConverter.ToBoolean(data,0));
休息时间;
// .jpeg图片作为流存储。
case ResourceTypeCode.Stream:
///// const int OFFSET = 4;
//// int大小= BitConverter.ToInt32(数据,0);
///// Bitmap value1 = new Bitmap(new MemoryStream(data,OFFSET,size));
///// Console.WriteLine(重新创建的值:{0},value1);

const int偏移= 4;
resStream =新的MemoryStream(data,OFFSET,data.Length-OFFSET);

休息时间;
//我们唯一的其他类型是DateTimeTZI。
默认值:
//////如果类型不可用,则反序列化数据没有意义。
////如果(dataType.Contains( DateTimeTZI)&&已加载)
//// {
///// BinaryFormatter binFmt = new BinaryFormatter();
////对象值2 = binFmt.Deserialize(new MemoryStream(data));
///// Console.WriteLine(重新创建的值:{0},value2);
////}
//// break;
休息时间;
}

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

return resStream;
}

// *************************************** ***********************************
private static void DebugPrintResources(System.Resources.ResourceReader reader)
{
Debug.Print(开始转储资源:---------------------);
foreach(阅读器中的DictionaryEntry项)
{
Debug.Print(item.Key.ToString());
}
Debug.Print(结束转储资源:---------------------);
}

// *************************************** *********************************** // ****************** ******************************************************
}
}


I want to load any resource from any assembly (of the same app) using only its uri. Both assemblies are part of the same application.

Because I want to be able to load from any assembly, I can't use "App" or "Application" which is not defined in a general Dll.

I think part or the answer is using : System.Reflection.Assembly.GetCallingAssembly().GetManifestResourceStream(path);

But I can't find how to properly find/extract/resolve the assembly from the uri?

Notes:

  • The resource is actually defined as "Resource".
  • The resource is not par of a WPF resource dictionary.
  • The resource is actually an XML file but could be anything

Up to now (~3 hours after), after using reflector, it seems that using static System.Windows.Application.GetResourceStream(uri).Stream has advantages like resource caching. It is bad because it is hooked to WPF (System.windows). I'm looking for a better way (non depending on any specific UI framework) to do the job.

解决方案

I solved my problem with this "LoadResourceFromUri" fonction defined below. I've done and published a project at GitHub with a tool to load any module (dll) and display its resources. Could also get info on uri.

About the offset of 4 while converting byteArray to stream, I used Microsoft code because otherwise (without offset), it just don't work as expected.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Resources;

namespace HQ.Util.General
{
    public static class ResourceHelper
    {
        // ******************************************************************
        /// <summary>
        /// The resource should be defined as 'Resource' not as 'Embedded resource'.
        /// </summary>
        /// <param name="resourcePath">The resource path</param>
        /// <param name="assembly">If null, then use calling assembly to find the resource</param>
        /// <returns></returns>
        public static Uri GetLocationUri(string resourcePath, Assembly assembly = null)
        {
            if (assembly == null)
            {
                assembly = Assembly.GetCallingAssembly();
            }

            resourcePath = resourcePath.Replace('\\', '/');

            return new Uri(@"pack://application:,,,/" + assembly.GetName().Name + ";component" +
                (resourcePath[0] == '/' ? resourcePath : "/" + resourcePath), UriKind.Absolute);
        }

        // ******************************************************************
        /// <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 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;
        }

        // ******************************************************************
        private static void DebugPrintResources(System.Resources.ResourceReader reader)
        {
            Debug.Print("Begin dump resources: ---------------------");
            foreach (DictionaryEntry item in reader)
            {
                Debug.Print(item.Key.ToString());
            }
            Debug.Print("End   dump resources: ---------------------");
        }

        // ******************************************************************        // ******************************************************************
    }
}

这篇关于如何从包含其程序集和任何程序集路径的Uri加载资源?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆