使用 C# exe 修改不同 C# exe 的资源 [英] Use C# exe to modify resources of a different C# exe

查看:40
本文介绍了使用 C# exe 修改不同 C# exe 的资源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

解决了!见下文.

我有 2 个 C# 应用程序.应用程序 a 应该修改应用程序 b 的内部资源.应用程序 b 应该在执行时对其(修改后的)资源做一些事情.

I have 2 C# applications. Application a is supposed to modify the internal resources of application b. Application b is supposed to do something with its (modified) resources when executed.

我怎样才能做到这一点?

How can I accomplish that?

这是我尝试过的:

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 以提供数据.

为了让事情变得更容易一些假设:

I get a System.ArgumentException with The stream is readonly. on System.Resources.ResourceWriter..ctor(Stream stream)

EDIT
Since that does not seem to be possible with .net resources: Is there any other way?
I want to produce a single file (namely the exe of application b) which is executable and can work with the data (stored in the exe) that is given prior to execution from application a. Preferably without having to actually compile b in order to give it the data.

Assumptions in order to make it a little easier:

  • a 总是在 b
  • 之前执行
  • a 只执行一次
  • 两个应用程序都是我写的
  • a is always executed before b
  • a is only executed once
  • both applications are written by me

编辑 - 解决方案
由于无法通过资源实现这一点,我使用了以下解决方法:
显然,您可以将任何内容附加到 exe 文件中,它仍然可以执行,所以这是我想出的:

Edit - Sollution
Since it is not possible to accomplish that via resources I used the following workaround:
Apparently you can append ANYTHING to an exe file and it will still be executable, so here is what I came up with:

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.

If you want to use this snippet go ahead but note that if you add data to a file the indexes are in reverse order when retrieving:
Let's say you write data set A first and then data set B, if you read the data later B will have the index 0 and A the index 1.

推荐答案

根据您的假设,您可以使用 Mono.Cecil 库更新/添加可执行文件的资源

Based on your assumptions you can update/add resources of your executable using Mono.Cecil library

以下是使用 Mono.Cecil 进行资源操作的三种基本方法:

Here are three essential methods for Resource Manipulation using Mono.Cecil:

    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方法来获取当前资源流(Writable),

You can use GetResource method to retrieve current resource stream (Writable),

使用ResourceWriterResourceReaderResourceEditor 类你可以读/写或修改当前资源或创建一个新资源,然后简单地把它通过调用 ReplaceResource 返回可执行文件或通过调用 AddResource

using ResourceWriter, ResourceReader or ResourceEditor classes you can read/write or modify current resource or create a new resource, then simply put it back in the executable by calling ReplaceResource or add it as a new one by calling AddResource

以下是替换资源中图像的示例(通过从头开始创建新资源):

Here is an example of replacing an image in resource (by creating a new resource from scratch):

            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>获得Cecil;安装包 Mono.Cecil nuget.

you can obtain Cecil through PM> Install-Package Mono.Cecil nuget.

这篇关于使用 C# exe 修改不同 C# exe 的资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆