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