如何在没有Reflection的情况下从.Net程序集中读取资源?

时间:2017-09-25 11:38:30

标签: .net resources

我想通过读取文件的二进制数据来访问.Net程序集中的嵌入式资源,但没有通过反射加载程序集的开销。有可能吗?

1 个答案:

答案 0 :(得分:1)

这是一个有效的解决方案,虽然它可能是天真且低效的:

using System;
using System.IO;
using System.Text;
using System.Resources;

namespace ResUtil {

class Substream: Stream
{  Stream Base;
   long   Orig;
   long   Len;

   public override bool CanSeek
   {  get
      {  return Base.CanSeek;  }
   }

   public override bool CanRead
   {  get
      {  return Base.CanRead && Position < Len;  }
   }

   public override bool CanTimeout
   {
      get { return Base.CanTimeout;  }
   }

   public override Boolean CanWrite {
      get {
         return Base.CanWrite;
      }
   }

   public override long Position
   {  get
      {  return Base.Position - Orig;  }
      set
      {  Base.Position = value + Orig;  }
   }

   public override Int64 Length {
      get { return Len; }
   }

   public override void SetLength(Int64 value)
   {  throw new NotSupportedException();  }

   // This is unnecessary, but very desirable, in order to override the default
   // inefficient implementation via Read( Byte[],...):
   // see: https://msdn.microsoft.com/en-us/library/system.io.stream.readbyte%28v=vs.110%29.aspx
   public override Int32 ReadByte()
   {  return Base.ReadByte();  }

   public override Int32 Read(Byte[] buffer,Int32 offset,Int32 count)
   {  return Base.Read( buffer, offset, count );  }

   public override void Write(Byte[] buffer,Int32 offset,Int32 count)
   {  throw new NotSupportedException();  }

   public Substream( Stream s, long l )
   {  Base   = s;
      Orig   = s.Position;
      Len    = l;
   }

   public override long Seek( long ofs, SeekOrigin orig )
   {  if( orig == SeekOrigin.Begin )
      {  ofs = ofs + Orig;  }
      return Base.Seek( ofs, orig );
   }

   public override void Flush()
   {  Base.Flush();  }
}

public static class BinResReader { // TODO: a more generic overload accepting a stream instead of the file path

public static void Get
( string file, string resName, out string type, out byte[] data )
{  FileStream s;
   ResourceReader rr;
   bool found;
   long len;

   type = null;
   data = null;
   s = new FileStream( file, FileMode.Open );
   while( true )
   {  if( !FindResHeader( s, out len ) )
      {  break;  }
      try
      {  Substream ss = new Substream( s, len );
         rr = new ResourceReader( ss );
      }
      catch( BadImageFormatException ) // no resource here
      {  continue;  }

      found = true;
      try
      {  rr.GetResourceData( resName, out type, out data );  }
      catch // resource may not be present
      {  found = false;  } 
      ( ( IDisposable )rr ).Dispose(); // rr.Dispose() won't compile in .NET 3.5
      if( found )
      {  break;  }
      if( !s.CanRead )
      {  break;  }
   }
   s.Dispose();
}

// ---------------- Shift-based eight-byte queue in a UInt64 ----------------

static void Q_LR_In( ref UInt64 data, byte b )
{  data = data << 8;
   data = data | b;
}

static void Q_RL_In( ref UInt64 data, byte b )
{  UInt64 bm;

   data = data >> 8;
   bm   = (ulong)b << 7 * 8;
   data = data | bm;
}

static uint Q_High( ulong data )
{  return (uint)( data );  }

static uint Q_Low ( ulong data )
{  return (uint)( (data >> 32) );  }

// ------------- Find the next location of suspected resource ---------------

// locate next resource in stream s, return its length in bytes
static bool FindResHeader( Stream s, out long len )
{  bool   found = false;

   ulong data;

   System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

   len     = 0;
   data    = 0xFFFFFFFFFFFFFFFF; // to decrease false alarms

   if( s.Length < 9 )
   {  goto NotFound;  }

   sw.Start();

   /* Buffered reading: */
   const int BufSize = 1024;
   int i;
   byte[] buffer = new byte[BufSize];
   int read;

   while( !found )
   {  read = s.Read( buffer, 0, BufSize );
      if( read == 0 )
      {  break;  }
      for( i = 0; i < read; i++ )
      {  Q_RL_In( ref data, buffer[i] );
         if( Q_Low( data ) != MagicNumber )
         {  continue;  }
         len = Q_High( data );
         if( len > s.Length - s.Position - 4 )
         {  continue;  }
         s.Seek( -4 - read + i + 1, SeekOrigin.Current );
         found = true;
         break;
      }
   }
   sw.Stop();

NotFound:
   return found;
}

// --------- Parsing of the various formats of string resource -----------

public static string GetStr( string file, string resName )
{  return GetStr( file, resName, Encoding.ASCII );  }

public static string GetStr( string file, string resName, Encoding enc )
{  byte[] data;
   string text;
   string type;
   int count;
   int index;
   text = null;
   Get( file, resName, out type, out data );
   if( data != null )
   {  switch( type )
      {  case "ResourceTypeCode.ByteArray":
            count = GetIntBE( data );
            index = 4;
         break;
         case "ResourceTypeCode.String":
            count = Read7BitEncodedInt( data, out index );
         break;
         case "ResourceTypeCode.Stream":
            goto case "ResourceTypeCode.ByteArray";
         default: throw new Exception( "Cannot convert resource type " +
            type + " to string.");
      }
      text = enc.GetString( data, index, count );
   }
   return text;
}

// This is copied from the reference source:
static int Read7BitEncodedInt( byte[] data, out int offset )
{
   int count = 0;
   int shift = 0;
   offset = 0;
   byte b;
   do
   {  b = data[offset];
      offset++;
      count |= (b & 0x7F) << shift;
      shift += 7;
   }
   while ((b & 0x80) != 0);
   return count;
}

// -------------------------- Utility routines ---------------------------

static int GetIntBE( byte[] bytes )
{  return bytes[0] | bytes[1]<<8 | bytes[2]<<16 | bytes[3]<<24;  }

static int GetIntLE( byte[] bytes )
{  return bytes[3] | bytes[2]<<8 | bytes[1]<<16 | bytes[0]<<24;  }

// -------------------------- Initialization -----------------------------
static long  MagicNumber;
static BinResReader()
{  MagicNumber = (uint)ResourceManager.MagicNumber;  }

}}

它至少需要这些改进:

一个。一次读取几个或所有资源,

湾在搜索过程中提取并使用嵌入资源文件的名称,而不是全部扫描。

另一个选择是从microsoft.public.dotnet.languages.csharp使用Mono.Cecil - thanks到ArneVajhøj。