2014-09-30 69 views
2

解決!見下文。使用C#EXE修改不同C#EXE文件的資源

我有2個C#應用程序。應用程序a應該修改應用程序的內部資源b。應用程序b應該在執行時對其(已修改)資源執行某些操作。

我該如何做到這一點?

這裏是我的嘗試:

public static void addFileToResources(string dest, string src) 
{ 
    Assembly a_dest = Assembly.LoadFile(dest); 

    using (Stream s_dest = a_dest.GetManifestResourceStream("Elevator.Properties.Resources.resources")) 
    { 
     using (ResourceWriter rw = new ResourceWriter(s_dest)) 
     { 
      byte[] b_src = File.ReadAllBytes(src); 
      rw.AddResource("target", b_src); 
     } 
    } 
} 

我得到了System.ArgumentExceptionThe stream is readonly.System.Resources.ResourceWriter..ctor(Stream stream)

編輯
因爲這似乎並不可能與.NET資源:有沒有另一種方式?
我想生成一個單個文件(即應用程序b的exe文件),它是可執行的,並且可以處理從應用程序a執行之前給出的數據(存儲在exe中)。最好不必實際編譯b以便爲其提供數據。

假設,以使其更容易一些:

  • a始終執行之前b
  • a只執行一次
  • 兩個應用程序都是由我

編輯 - 溶劑
因爲它是不可能實現這一目標通過資源我用以下解決方法:
顯然,你可以添加任何東西,exe文件,它仍然會執行,所以這裏是我想出了:

public class Packer : IDisposable 
{ 
    // chosen quite arbitrarily; can be anything you'd like but should be reasonably unique 
    private static byte[] MAGIC_NUMBER = { 0x44, 0x61, 0x6c, 0x65, 0x6b, 0x4c, 0x75, 0x63 }; 

    private Stream inStream; 

    public Packer(string filename, bool openReadonly = false) 
    { 
     // The FileAccess.Read is necessary when I whant to read from the file that is being executed. 
     // Hint: To get the path for the executing file I used: 
     // System.Reflection.Assembly.GetExecutingAssembly().Location 
     inStream = File.Open(filename, FileMode.Open, openReadonly ? FileAccess.Read : FileAccess.ReadWrite, openReadonly ? FileShare.Read : FileShare.None); 
    } 

    public byte[] ReadData(int index) 
    { 
     byte[] mn_buf = new byte[MAGIC_NUMBER.Length]; 
     byte[] len_buf = new byte[sizeof(Int32)]; 
     int data_len = 0; 
     inStream.Seek(0, SeekOrigin.End); 
     for (int i = 0; i <= index; ++i) 
     { 
      // Read the last few bytes 
      inStream.Seek(-MAGIC_NUMBER.Length, SeekOrigin.Current); 
      inStream.Read(mn_buf, 0, MAGIC_NUMBER.Length); 
      inStream.Seek(-MAGIC_NUMBER.Length, SeekOrigin.Current); 
      for (int j = 0; j < MAGIC_NUMBER.Length; ++j) 
      { // Check if the last bytes are equals to my MAGIC_NUMBER 
       if (mn_buf[j] != MAGIC_NUMBER[j]) 
       { 
        throw new IndexOutOfRangeException("Not enough data."); 
       } 
      } 
      inStream.Seek(-sizeof(Int32), SeekOrigin.Current); 
      inStream.Read(len_buf, 0, sizeof(Int32)); 
      inStream.Seek(-sizeof(Int32), SeekOrigin.Current); 
      // Read the length of the data 
      data_len = BitConverter.ToInt32(len_buf, 0); 
      inStream.Seek(-data_len, SeekOrigin.Current); 
     } 
     byte[] data = new byte[data_len]; 
     // Read the actual data and return it 
     inStream.Read(data, 0, data_len); 
     return data; 
    } 

    public void AddData(byte[] data) 
    { 
     // append it 
     inStream.Seek(0, SeekOrigin.End); 
     inStream.Write(data, 0, data. 
     inStream.Write(BitConverter.GetBytes(data.Length), 0, sizeof(Int32)); 
     inStream.Write(MAGIC_NUMBER, 0, MAGIC_NUMBER.Length); 
    } 

    public void Dispose() 
    { 
     inStream.Dispose(); 
    } 
} 

如果您想要使用此代碼段,請注意,如果您將數據添加到文件中,索引在檢索時的順序相反:
假設您先寫入數據集A,然後再寫入數據集B數據後B將有索引0和A索引1.

+0

您是否驗證過您將「非空」傳遞給'ResourceWriter'的構造函數? – 2014-09-30 14:44:33

+3

兩個問題。您沒有正確猜測資源流名稱,這是導致異常的原因。而當你解決這個問題時,你會遇到更嚴重的問題,資源嵌入到.NET程序集後是隻讀的。重建可執行文件是不實際的。 – 2014-09-30 14:44:36

+0

呃...該死的。不管怎麼說,還是要謝謝你! – Ch33f 2014-09-30 14:49:18

回答

2

基於喲你假設你可以使用更新/添加可執行文件的資源Mono.Cecil

以下是使用Mono進行資源操作的三種基本方法。塞西爾:使用ResourceWriterResourceReaderResourceEditor

public static void ReplaceResource(string path, string resourceName, byte[] resource) 
    { 
     var definition = 
      AssemblyDefinition.ReadAssembly(path); 

     for (var i = 0; i < definition.MainModule.Resources.Count; i++) 
      if (definition.MainModule.Resources[i].Name == resourceName) 
      { 
       definition.MainModule.Resources.RemoveAt(i); 
       break; 
      } 

     var er = new EmbeddedResource(resourceName, ManifestResourceAttributes.Public, resource); 
     definition.MainModule.Resources.Add(er); 
     definition.Write(path); 
    } 

    public static void AddResource(string path, string resourceName, byte[] resource) 
    { 
     var definition = 
      AssemblyDefinition.ReadAssembly(path); 

     var er = new EmbeddedResource(resourceName, ManifestResourceAttributes.Public, resource); 
     definition.MainModule.Resources.Add(er); 
     definition.Write(path); 
    } 

    public static MemoryStream GetResource(string path, string resourceName) 
    { 
     var definition = 
      AssemblyDefinition.ReadAssembly(path); 

     foreach (var resource in definition.MainModule.Resources) 
      if (resource.Name == resourceName) 
      { 
       var embeddedResource =(EmbeddedResource) resource; 
       var stream = embeddedResource.GetResourceStream(); 

       var bytes = new byte[stream.Length]; 
       stream.Read(bytes, 0, bytes.Length); 

       var memStream = new MemoryStream(); 
       memStream.Write(bytes,0,bytes.Length); 
       memStream.Position = 0; 
       return memStream; 
      } 

     return null; 
    } 

您可以使用GetResource方法來檢索當前資源流(可寫),

你可以讀/寫或修改現有資源或創建新的資源,然後通過調用ReplaceResource簡單地將其放回可執行文件中,或通過調用AddResource

將其添加爲新文件(以下是從頭開始創建新資源的示例):

  var ms = new MemoryStream(); 
      var writer = new ResourceWriter(ms); 
      writer.AddResource("good_luck",new Bitmap("good_luck.png")); 
      writer.Generate(); 
      ReplaceResource(@"my executale.exe", "ResourceTest.Properties.Resources.resources",ms.ToArray()); 

您可以通過PM> Install-Package Mono.Cecil nuget獲得塞西爾。