如何从包含其程序集和任何程序集路径的 Uri 加载资源? [英] How to load a resource from an Uri containing its assembly and path from any assembly?
问题描述
我想仅使用其 uri 从任何程序集(同一应用程序的)加载任何资源.两个程序集都是同一个应用程序的一部分.
因为我希望能够从任何程序集加载,所以我不能使用通用 Dll 中未定义的应用程序"或应用程序".
我认为部分或答案是使用: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{//**********************************************************************///<总结>///资源应定义为资源"而不是嵌入式资源".///</总结>///<param name="resourcePath">资源路径</param>///<param name="assembly">如果为null,则使用调用程序集来查找资源</param>///<returns></returns>public static Uri GetLocationUri(string resourcePath, Assembly assembly = null){如果(程序集 == 空){装配 = Assembly.GetCallingAssembly();}resourcePath = resourcePath.Replace('\', '/');return new Uri(@"pack://application:,,,/" + assembly.GetName().Name + ";component" +(resourcePath[0] == '/' ? resourcePath : "/" + resourcePath), UriKind.Absolute);}//**********************************************************************///<总结>///将从作为应用程序一部分的任何程序集中加载资源.///它不依赖于特定于(UI)框架的应用程序.///</总结>///<param name="uri"></param>///<param name="asm"></param>///<returns></returns>公共静态流 LoadResourceFromUri(Uri uri,Assembly asm = null){流流 = 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.{流 = GetAssemblyResourceStream(asm, localPath.Substring(indexLocalPathWithoutAssembly));}别的{如果(uri.Segments.Length > 1){if (uri.Segments[0] == "/" && uri.Segments[1].EndsWith(";component/")){int index = uri.Segments[1].IndexOf(";");如果(索引 > 0){string assemblyName = uri.Segments[1].Substring(0, index);foreach (Assembly asmIter in AppDomain.CurrentDomain.GetAssemblies()){if (asmIter.GetName().Name == assemblyName){流 = GetAssemblyResourceStream(asmIter, localPath.Substring(indexLocalPathWithoutAssembly));休息;}}}}}如果(流==空){asm = Assembly.GetCallingAssembly();流 = GetAssemblyResourceStream(asm, localPath.Substring(indexLocalPathWithoutAssembly));}}}返回流;}//**********************************************************************///<总结>///路径分隔符是'/'.路径不应以/"开头.///</总结>///<param name="asm"></param>///<param name="path"></param>///<returns></returns>public static Stream GetAssemblyResourceStream(Assembly asm,字符串路径){//只是要确定如果(路径 [0] == '/'){path = path.Substring(1);}//只是要确定如果 (path.IndexOf('\') == -1){path = path.Replace('\', '/');}流 resStream = null;string resName = asm.GetName().Name + ".g.resources";//参考:Thomas Levesque 回答://http://stackoverflow.com/questions/2517407/enumrating-net-assembly-resources-at-runtime使用 (var stream = asm.GetManifestResourceStream(resName)){使用 (var resReader = new System.Resources.ResourceReader(stream)){字符串数据类型 = 空;字节 [] 数据 = 空;尝试{resReader.GetResourceData(path.ToLower(), out dataType, out data);}捕获(异常前){DebugPrintResources(resReader);}如果(数据!= null){开关(数据类型)//代码来自{//处理内部序列化的字符串数据(ResourceTypeCode 成员).案例ResourceTypeCode.String":BinaryReader reader = new BinaryReader(new MemoryStream(data));string binData = reader.ReadString();Console.WriteLine(" 重新创建的值:{0}", binData);休息;案例ResourceTypeCode.Int32":Console.WriteLine(" 重新创建的值:{0}", BitConverter.ToInt32(data, 0));休息;案例ResourceTypeCode.Boolean":Console.WriteLine(" 重新创建的值:{0}", BitConverter.ToBoolean(data, 0));休息;//.jpeg 图像存储为流.案例ResourceTypeCode.Stream":////const int OFFSET = 4;////int size = BitConverter.ToInt32(data, 0);////位图值1 = new Bitmap(new MemoryStream(data, OFFSET, size));////Console.WriteLine(" Recreated Value: {0}", value1);const int 偏移 = 4;resStream = new MemoryStream(data, OFFSET, data.Length - OFFSET);休息;//我们唯一的其他类型是 DateTimeTZI.默认://////如果类型不可用,则反序列化数据没有意义.////if (dataType.Contains("DateTimeTZI") &&loaded)////{////BinaryFormatter binFmt = new BinaryFormatter();////对象 value2 = binFmt.Deserialize(new MemoryStream(data));////Console.WriteLine("重新创建的值:{0}", value2);///}////休息;休息;}//resStream = new MemoryStream(resData);}}}返回 resStream;}//**********************************************************************私有静态无效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屋!