2012-05-07 7 views
4

私はリソースとして複数のファイルを追加した.NETアセンブリを持っています(バイナリ、各500KB以上)。私は以前、自動生成されたResourcesクラスのResourceManager.GetObject()メソッドを使用してこれらにアクセスしました。これはbyte[]を返します。リソースとして追加されたファイルのバイト[]ではなくMemoryStream?

パフォーマンスと構文上の理由から、バイト配列の代わりにストリームとしてこれらのバイナリリソースを操作したいと考えています。私は、手動で.resxファイルを編集し、<value>要素のクラス名をSystem.Byte[]からSystem.IO.MemoryStreamに変更することで、ResourceManager.GetStream()メソッドを使用してストリームとしてリソースに正常にアクセスできることがわかりました。

<data name="MyFile" type="System.Resources.ResXFileRef, System.Windows.Forms"> 
    <value>..\Resources\MyFile.ext;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> 
</data> 

は次のようになります。

<data name="MyFile" type="System.Resources.ResXFileRef, System.Windows.Forms"> 
    <value>..\Resources\MyFile.ext;System.IO.MemoryStream, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> 
</data> 

このアプローチの唯一の欠点は、Visual Studioは常にbyte[]形で新しいファイルリソースを追加することです。タイプをMemoryStreamに設定する方法がありますか?

答えて

5

ResourceManagerに拡張方法を作成できます。けれども

ResourceManager resourceManager = Properties.Resources.ResourceManager; 
MemoryStream stream = resourceManager.GetMemoryStream("binaryResource"); 
を呼び出す

public static class ResourceExtensions 
{ 
    public static MemoryStream GetMemoryStream(this ResourceManager resourceManager, String name) { 
     object resource = resourceManager.GetObject(name); 

     if (resource is byte[]) { 
      return new MemoryStream((byte[])resource); 
     } 
     else { 
      throw new System.InvalidCastException("The specified resource is not a binary resource."); 
     } 
    } 
} 

、これは全く同じように思えます。

MemoryStream stream = new MemoryStream(Properties.Resources.SomeBinaryResource); 

私は、彼らが変更する場合が壊れやすいので、私は、リソースファイルを変更するかどうか分からない、と私は、Visual Studioは変更が上書きされますシナリオがあると確信しています。

この問題は、懸念事項として、データのコピーをメモリに作成してメモリフットプリントを作成することです。短命になる軽量リソースの場合、それは問題ではありませんが、大きな問題になる可能性があります。

答えは短いです:ResourceManagerを使用してメモリフットプリントを回避することはできません。問題は、ResourceManager.GetObject(String)ResourceManager.GetStream(String)の両方がデータのコピーを作成することです。 GetStream(String)UnmanagedMemoryStreamを返しますが、実際には内部でGetObject(String)を呼び出すため、コピーは作成されます。アプリケーションをデバッグしてメモリをプロファイリングすると、メモリがまだ割り当てられていることがわかります。

unsafeコンテキストでポインタを使用してこれを回避するために複数の方法を試しましたが、反射と何も機能しませんでした。 ResourceManagerはそれほどフレキシブルでも最適化されていません。

解決策は見つかりましたが、Embedded Resourcesを使用する必要があります。これは、Build ActionのリソースファイルのビルドアクションをEmbedded Resourceに設定する以外は何も変更しません。これを使用して、リフレクションを使用してデータのコピーを作成しないUnmanagedMemoryStreamを作成できます。

private UnmanagedMemoryStream GetUnmanagedMemoryStream(String embeddedResourceName) { 
    Assembly assembly = Assembly.GetExecutingAssembly(); 

    string[] resourceNames = assembly.GetManifestResourceNames(); 
    string resourceName = resourceNames.SingleOrDefault(resource => resource.EndsWith(embeddedResourceName, StringComparison.InvariantCultureIgnoreCase)); 

    if (resourceName != null) { 
     return (UnmanagedMemoryStream)assembly.GetManifestResourceStream(resourceName); 
    } 
    else { 
     throw new System.ArgumentException("The specified embedded resource could not be found.", "embeddedResourceName"); 
    } 
} 

これは広範にテストされていませんが、動作します。私のテストデータは17メガバイトの小さなファイルでした。私のテストアプリケーションのワーキングセットメモリは約50メガバイトから始まり、リソースをストリームに取り込んだ後も変更されません。ResourceManagerを使用すると、ワーキングセットがリソースのサイズ分だけすぐに増加します。

EndsWithへのコールをスワップアウトする必要があります。これは、リソースの名前がResourceManagerを介して直接アクセスする場合とわずかに異なるため、マニフェスト内の適切なリソース名をチェックします。

私は実際には、既存のResourceManagerを使用して解決策を見つけることができなかったことに失望していますが、十分な柔軟性がありません。

私はin-depth blog article here about this subjectと書きました。

+0

あなたのアプローチを使用すると、ストリーム全体が生成されて返される前に、byte []全体がメモリに読み込まれることになります。これが、私が 'GetObject'の代わりに' GetStream'を呼び出せることを望んでいた理由です。ファイル全体を最初にメモリに読み込まないようにします。 –

+1

私はそれを持っていると思っていましたが、もう少し詳しく作業する必要があります。私はあなたのために何かを持っているときに更新します。 –

+0

私の答えを更新しました。要するに、ResourceManagerでフットプリントを回避することはできません。 –

関連する問題