在 C# 中使用 UpdateResource?

发布于 2024-10-01 09:27:21 字数 2100 浏览 2 评论 0原文

我正在尝试以编程方式更改外部可执行文件的图标。我用 google 搜索并找到了很多关于使用 C++ 的问题的信息。基本上,我需要使用 BeginUpdateResource、UpdateResource 和 EndUpdateResource。问题是 - 我不知道在 C# 中将什么传递给 UpdateResource。

这是我到目前为止的代码:

class IconChanger
{
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr BeginUpdateResource(string pFileName,
        [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool UpdateResource(IntPtr hUpdate, string lpType, string lpName, ushort wLanguage,
        IntPtr lpData, uint cbData);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);

    public enum ICResult
    {
        Success,
        FailBegin,
        FailUpdate,
        FailEnd
    }

    public ICResult ChangeIcon(string exeFilePath, byte[] iconData)
    {
        // Load executable
        IntPtr handleExe = BeginUpdateResource(exeFilePath, false);

        if (handleExe == null)
            return ICResult.FailBegin;

        // Get language identifier
        CultureInfo currentCulture = CultureInfo.CurrentCulture;
        int pid = ((ushort)currentCulture.LCID) & 0x3ff;
        int sid = ((ushort)currentCulture.LCID) >> 10;
        ushort languageID = (ushort)((((ushort)pid) << 10) | ((ushort)sid));

        // Get pointer to data
        GCHandle iconHandle = GCHandle.Alloc(iconData, GCHandleType.Pinned);

        // Replace the icon
        if (UpdateResource(handleExe, "#3", "#1", languageID, iconHandle.AddrOfPinnedObject(), (uint)iconData.Length))
        {
            if (EndUpdateResource(handleExe, false))
                return ICResult.Success;
            else
                return ICResult.FailEnd;
        }
        else
            return ICResult.FailUpdate;
    }
}

关于 lpType - 在 C++ 中,您传递 RT_ICON (或 RT_GROUP_ICON)。我应该在 C# 中传递什么值? 同样的问题也适用于 lpName 参数。 我不确定语言标识符(我在互联网上找到了这个),因为我无法测试它。 我也不确定我是否提供了适当的图标数据。目前, iconData 包含 .ico 文件中的字节。

有人能指出我正确的方向吗?

非常感谢。

I'm trying to change the icon of external executable programmatically. I've googled and found much information about this problem using C++. Basically, I need to use BeginUpdateResource, UpdateResource and EndUpdateResource. The problem is - I don't know what to pass to UpdateResource in C#.

Here's the code I have so far:

class IconChanger
{
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr BeginUpdateResource(string pFileName,
        [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool UpdateResource(IntPtr hUpdate, string lpType, string lpName, ushort wLanguage,
        IntPtr lpData, uint cbData);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);

    public enum ICResult
    {
        Success,
        FailBegin,
        FailUpdate,
        FailEnd
    }

    public ICResult ChangeIcon(string exeFilePath, byte[] iconData)
    {
        // Load executable
        IntPtr handleExe = BeginUpdateResource(exeFilePath, false);

        if (handleExe == null)
            return ICResult.FailBegin;

        // Get language identifier
        CultureInfo currentCulture = CultureInfo.CurrentCulture;
        int pid = ((ushort)currentCulture.LCID) & 0x3ff;
        int sid = ((ushort)currentCulture.LCID) >> 10;
        ushort languageID = (ushort)((((ushort)pid) << 10) | ((ushort)sid));

        // Get pointer to data
        GCHandle iconHandle = GCHandle.Alloc(iconData, GCHandleType.Pinned);

        // Replace the icon
        if (UpdateResource(handleExe, "#3", "#1", languageID, iconHandle.AddrOfPinnedObject(), (uint)iconData.Length))
        {
            if (EndUpdateResource(handleExe, false))
                return ICResult.Success;
            else
                return ICResult.FailEnd;
        }
        else
            return ICResult.FailUpdate;
    }
}

Regarding lpType - in C++, you pass RT_ICON (or RT_GROUP_ICON). What value should I pass in C#?
The same question goes for lpName parameter.
I'm not sure about language identifier (I found this on Internet) since I cannot test it.
I'm also not sure whether I'm providing appropriate icon data. Currently, iconData contains the bytes from .ico file.

Can anybody point me to right direction?

Thank you very much.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

如梦初醒的夏天 2024-10-08 09:27:21

只是一些提示,这很难做到正确。通过谎报 lpType 参数来传递 RT_ICON。将其从字符串更改为 IntPtr 并传递 (IntPtr)3。

lpData 参数非常棘手。您需要按照资源编译器 (rc.exe) 编译数据的方式传递数据。我不知道它是否会破坏 .ico 文件的原始数据。唯一合理的尝试是使用 FileStream 将 .ico 文件中的数据读取到 byte[] 中,您似乎已经在这样做了。我认为该函数实际上是为了将资源从一个二进制图像复制到另一个二进制图像而设计的。你的方法奏效的几率不为零。

你还忽略了另一个潜在的问题,程序图标的资源ID不一定是1。通常不是,100往往是一个流行的选择,但任何事情都会发生。需要 EnumResourceNames 才能使其可靠。规则是最小编号的 ID 设置文件的图标。我实际上不确定这是否真的意味着资源编译器将最低的数字放在第一位,而 API 可能不会这样做。

一个非常小的故障模式是UpdateResource只能更新编号的资源项,而不能更新命名的资源项。使用名称代替数字并不罕见,但绝大多数图像都使用数字作为图标。

当然,如果没有 UAC 清单,这种方法起作用的可能性为零。您正在侵入通常没有写入权限的文件。

Just some pointers, this is quite hard to get right. Pass an RT_ICON by lying about the lpType argument. Change it from string to IntPtr and pass (IntPtr)3.

The lpData argument is quite tricky. You need to pass the data the way it is compiled by the resource compiler (rc.exe). I have no idea if it mangles the raw data of the .ico file. The only reasonable thing to try is to read the data from the .ico file with FileStream into a byte[], you already seem to be doing this. I think the function was really designed to copy a resource from one binary image to another. Odds of your approach working are not zero.

You are also ignoring another potential problem, the resource ID of the icon of the program isn't necessarily 1. It often is not, 100 tends to be a popular choice, but anything goes. EnumResourceNames would be required to make it reliable. The rule is that the lowest numbered ID sets the icon for the file. I'm not actually sure if that really means that the resource compiler puts the lowest number first, something that the API probably doesn't do.

A very small failure mode is that UpdateResource can only updated numbered resource items, not named ones. Using names instead of numbers is not uncommon but the vast majority of images use numbers for icons.

And of course, the odds that this will work without a UAC manifest are zero. You are hacking files that you don't normally have write access to.

过去的过去 2024-10-08 09:27:21

我设法使用 ResourceHacker 并以这篇文章为例,在纯 C# 中实现了此功能。只需使用常规 .ico 作为输入即可。在 ResourceHacker (http://www.angusj.com/resourcehacker/) 中,您将看到图标标识符(在我的情况下为 1)和语言标识符(在我的情况下为 1043):

在此处输入图像描述

我使用了以下代码:

internal class IconChanger
{

    #region IconReader
    public class Icons : List<Icon>
    {
        public byte[] ToGroupData(int startindex = 1)
        {
            using (var ms = new MemoryStream())
            using (var writer = new BinaryWriter(ms))
            {
                var i = 0;

                writer.Write((ushort)0);  //reserved, must be 0
                writer.Write((ushort)1);  // type is 1 for icons
                writer.Write((ushort)this.Count);  // number of icons in structure(1)

                foreach (var icon in this)
                {

                    writer.Write(icon.Width);
                    writer.Write(icon.Height);
                    writer.Write(icon.Colors);

                    writer.Write((byte)0); // reserved, must be 0
                    writer.Write(icon.ColorPlanes);

                    writer.Write(icon.BitsPerPixel);

                    writer.Write(icon.Size);

                    writer.Write((ushort)(startindex + i));

                    i++;

                }
                ms.Position = 0;

                return ms.ToArray();
            }
        }
    }

    public class Icon
    {

        public byte Width { get; set; }
        public byte Height { get; set; }
        public byte Colors { get; set; }

        public uint Size { get; set; }

        public uint Offset { get; set; }

        public ushort ColorPlanes { get; set; }

        public ushort BitsPerPixel { get; set; }

        public byte[] Data { get; set; }

    }

    public class IconReader
    {

        public Icons Icons = new Icons();

        public IconReader(Stream input)
        {
            using (BinaryReader reader = new BinaryReader(input))
            {
                reader.ReadUInt16(); // ignore. Should be 0
                var type = reader.ReadUInt16();
                if (type != 1)
                {
                    throw new Exception("Invalid type. The stream is not an icon file");
                }
                var num_of_images = reader.ReadUInt16();

                for (var i = 0; i < num_of_images; i++)
                {
                    var width = reader.ReadByte();
                    var height = reader.ReadByte();
                    var colors = reader.ReadByte();
                    reader.ReadByte(); // ignore. Should be 0

                    var color_planes = reader.ReadUInt16(); // should be 0 or 1

                    var bits_per_pixel = reader.ReadUInt16();

                    var size = reader.ReadUInt32();

                    var offset = reader.ReadUInt32();

                    this.Icons.Add(new Icon()
                    {
                        Colors = colors,
                        Height = height,
                        Width = width,
                        Offset = offset,
                        Size = size,
                        ColorPlanes = color_planes,
                        BitsPerPixel = bits_per_pixel
                    });
                }

                // now get the Data
                foreach (var icon in Icons)
                {
                    if (reader.BaseStream.Position < icon.Offset)
                    {
                        var dummy_bytes_to_read = (int)(icon.Offset - reader.BaseStream.Position);
                        reader.ReadBytes(dummy_bytes_to_read);
                    }

                    var data = reader.ReadBytes((int)icon.Size);

                    icon.Data = data;
                }

            }
        }

    }
    #endregion

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern int UpdateResource(IntPtr hUpdate, uint lpType, ushort lpName, ushort wLanguage, byte[] lpData, uint cbData);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr BeginUpdateResource(string pFileName, [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);

    public enum ICResult
    {
        Success,
        FailBegin,
        FailUpdate,
        FailEnd
    }

    const uint RT_ICON = 3;
    const uint RT_GROUP_ICON = 14;

    public ICResult ChangeIcon(string exeFilePath, string iconFilePath)
    {
        using (FileStream fs = new FileStream(iconFilePath, FileMode.Open, FileAccess.Read))
        {
            var reader = new IconReader(fs);

            var iconChanger = new IconChanger();
            return iconChanger.ChangeIcon(exeFilePath, reader.Icons);
        }
    }

    public ICResult ChangeIcon(string exeFilePath, Icons icons)
    {
        // Load executable
        IntPtr handleExe = BeginUpdateResource(exeFilePath, false);

        if (handleExe == null) return ICResult.FailBegin;

        ushort startindex = 1;
        ushort index = startindex;
        ICResult result = ICResult.Success;

        var ret = 1;

        foreach (var icon in icons)
        {
            // Replace the icon
            // todo :Improve the return value handling of UpdateResource
            ret = UpdateResource(handleExe, RT_ICON, index, 0, icon.Data, icon.Size);

            index++;
        }

        var groupdata = icons.ToGroupData();

        // todo :Improve the return value handling of UpdateResource
        ret = UpdateResource(handleExe, RT_GROUP_ICON, startindex, 0, groupdata, (uint)groupdata.Length);
        if (ret == 1)
        {
            if (EndUpdateResource(handleExe, false))
                result = ICResult.Success;
            else
                result = ICResult.FailEnd;
        }
        else
            result = ICResult.FailUpdate;

        return result;
    }
}

I managed to get this working in pure C# using ResourceHacker and this posting as an example. Just use a regular .ico as input. In ResourceHacker (http://www.angusj.com/resourcehacker/) you will see the icon identifier (in my case 1) and the language identifier (in my case 1043):

enter image description here

I used this code:

internal class IconChanger
{

    #region IconReader
    public class Icons : List<Icon>
    {
        public byte[] ToGroupData(int startindex = 1)
        {
            using (var ms = new MemoryStream())
            using (var writer = new BinaryWriter(ms))
            {
                var i = 0;

                writer.Write((ushort)0);  //reserved, must be 0
                writer.Write((ushort)1);  // type is 1 for icons
                writer.Write((ushort)this.Count);  // number of icons in structure(1)

                foreach (var icon in this)
                {

                    writer.Write(icon.Width);
                    writer.Write(icon.Height);
                    writer.Write(icon.Colors);

                    writer.Write((byte)0); // reserved, must be 0
                    writer.Write(icon.ColorPlanes);

                    writer.Write(icon.BitsPerPixel);

                    writer.Write(icon.Size);

                    writer.Write((ushort)(startindex + i));

                    i++;

                }
                ms.Position = 0;

                return ms.ToArray();
            }
        }
    }

    public class Icon
    {

        public byte Width { get; set; }
        public byte Height { get; set; }
        public byte Colors { get; set; }

        public uint Size { get; set; }

        public uint Offset { get; set; }

        public ushort ColorPlanes { get; set; }

        public ushort BitsPerPixel { get; set; }

        public byte[] Data { get; set; }

    }

    public class IconReader
    {

        public Icons Icons = new Icons();

        public IconReader(Stream input)
        {
            using (BinaryReader reader = new BinaryReader(input))
            {
                reader.ReadUInt16(); // ignore. Should be 0
                var type = reader.ReadUInt16();
                if (type != 1)
                {
                    throw new Exception("Invalid type. The stream is not an icon file");
                }
                var num_of_images = reader.ReadUInt16();

                for (var i = 0; i < num_of_images; i++)
                {
                    var width = reader.ReadByte();
                    var height = reader.ReadByte();
                    var colors = reader.ReadByte();
                    reader.ReadByte(); // ignore. Should be 0

                    var color_planes = reader.ReadUInt16(); // should be 0 or 1

                    var bits_per_pixel = reader.ReadUInt16();

                    var size = reader.ReadUInt32();

                    var offset = reader.ReadUInt32();

                    this.Icons.Add(new Icon()
                    {
                        Colors = colors,
                        Height = height,
                        Width = width,
                        Offset = offset,
                        Size = size,
                        ColorPlanes = color_planes,
                        BitsPerPixel = bits_per_pixel
                    });
                }

                // now get the Data
                foreach (var icon in Icons)
                {
                    if (reader.BaseStream.Position < icon.Offset)
                    {
                        var dummy_bytes_to_read = (int)(icon.Offset - reader.BaseStream.Position);
                        reader.ReadBytes(dummy_bytes_to_read);
                    }

                    var data = reader.ReadBytes((int)icon.Size);

                    icon.Data = data;
                }

            }
        }

    }
    #endregion

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern int UpdateResource(IntPtr hUpdate, uint lpType, ushort lpName, ushort wLanguage, byte[] lpData, uint cbData);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr BeginUpdateResource(string pFileName, [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);

    public enum ICResult
    {
        Success,
        FailBegin,
        FailUpdate,
        FailEnd
    }

    const uint RT_ICON = 3;
    const uint RT_GROUP_ICON = 14;

    public ICResult ChangeIcon(string exeFilePath, string iconFilePath)
    {
        using (FileStream fs = new FileStream(iconFilePath, FileMode.Open, FileAccess.Read))
        {
            var reader = new IconReader(fs);

            var iconChanger = new IconChanger();
            return iconChanger.ChangeIcon(exeFilePath, reader.Icons);
        }
    }

    public ICResult ChangeIcon(string exeFilePath, Icons icons)
    {
        // Load executable
        IntPtr handleExe = BeginUpdateResource(exeFilePath, false);

        if (handleExe == null) return ICResult.FailBegin;

        ushort startindex = 1;
        ushort index = startindex;
        ICResult result = ICResult.Success;

        var ret = 1;

        foreach (var icon in icons)
        {
            // Replace the icon
            // todo :Improve the return value handling of UpdateResource
            ret = UpdateResource(handleExe, RT_ICON, index, 0, icon.Data, icon.Size);

            index++;
        }

        var groupdata = icons.ToGroupData();

        // todo :Improve the return value handling of UpdateResource
        ret = UpdateResource(handleExe, RT_GROUP_ICON, startindex, 0, groupdata, (uint)groupdata.Length);
        if (ret == 1)
        {
            if (EndUpdateResource(handleExe, false))
                result = ICResult.Success;
            else
                result = ICResult.FailEnd;
        }
        else
            result = ICResult.FailUpdate;

        return result;
    }
}
一枫情书 2024-10-08 09:27:21

这是对我有用的解决方案。我无法在 .NET 中编写它,但设法编写了我在 C# 应用程序中引用的 C++ DLL。

C++ DLL

C++ 解决方案的内容 我正在构建 DLL:

#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <windows.h>

extern "C"
{
    #pragma pack(push, 2)
    typedef struct {
        WORD Reserved1;       // reserved, must be 0
        WORD ResourceType;    // type is 1 for icons
        WORD ImageCount;      // number of icons in structure (1)
        BYTE Width;           // icon width (32)
        BYTE Height;          // icon height (32)
        BYTE Colors;          // colors (0 means more than 8 bits per pixel)
        BYTE Reserved2;       // reserved, must be 0
        WORD Planes;          // color planes
        WORD BitsPerPixel;    // bit depth
        DWORD ImageSize;      // size of structure
        WORD ResourceID;      // resource ID
    } GROUPICON;
    #pragma pack(pop)

    __declspec(dllexport) void __stdcall ChangeIcon(char *executableFile, char *iconFile, INT16 imageCount)
    {
        int len = strlen(executableFile) + 1;
        wchar_t *executableFileEx = new wchar_t[len];
        memset(executableFileEx, 0, len);
        ::MultiByteToWideChar(CP_ACP, NULL, executableFile, -1, executableFileEx, len);

        len = strlen("MAINICON") + 1;
        wchar_t *mainIconEx = new wchar_t[len];
        memset(mainIconEx, 0, len);
        ::MultiByteToWideChar(CP_ACP, NULL, "MAINICON", -1, mainIconEx, len);

        HANDLE hWhere = BeginUpdateResource(executableFileEx, FALSE);

        char *buffer;    // Buffer to store raw icon data
        long buffersize; // Length of buffer
        int hFile;       // File handle
       
        hFile = open(iconFile, O_RDONLY | O_BINARY);
        if (hFile == -1)
            return; // If file doesn't exist, can't be opened etc. 
       
        // Calculate buffer length and load file into buffer
        buffersize = filelength(hFile);
        buffer = (char *)malloc(buffersize);
        read(hFile, buffer, buffersize);
        close(hFile);

        // Calculate header size
        int headerSize = 6 + imageCount * 16;

        UpdateResource(
            hWhere,  // Handle to executable
            RT_ICON, // Resource type - icon
            MAKEINTRESOURCE(1), // Make the id 1
            MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), // Default language
            buffer + headerSize, // Skip the header bytes
            buffersize - headerSize  // Length of buffer
        );


        GROUPICON grData;

        grData.Reserved1 = 0;     // reserved, must be 0
        grData.ResourceType = 1;  // type is 1 for icons
        grData.ImageCount = 1;    // number of icons in structure (1)

        grData.Width = 32;        // icon width (32)
        grData.Height = 32;       // icon height (32)
        grData.Colors = 0;        // colors (256)
        grData.Reserved2 = 0;     // reserved, must be 0
        grData.Planes = 2;        // color planes
        grData.BitsPerPixel = 32; // bit depth
        grData.ImageSize = buffersize - 22; // size of image
        grData.ResourceID = 1;       // resource ID is 1

        UpdateResource(
            hWhere,
            RT_GROUP_ICON,
            // RT_GROUP_ICON resources contain information
            // about stored icons
            mainIconEx,
            // MAINICON contains information about the
            // application's displayed icon
            MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
            &grData,
            // Pointer to this structure
            sizeof(GROUPICON)
        );

        delete buffer; // free memory
         
        // Perform the update, don't discard changes
        EndUpdateResource(hWhere, FALSE);
    }
}

C# 代码

这是我用来从以前编写的 DLL 导入 ChangeIcon 函数的 C# 代码:

[DllImport("IconChanger.dll")]
static extern void ChangeIcon(String executableFile, String iconFile, short imageCount);

/// <summary>
/// Changes the executable's icon
/// </summary>
/// <param name="exeFilePath">Path to executable file</param>
/// <param name="iconFilePath">Path to icon file</param>
static public void ChangeIcon(string exeFilePath, string iconFilePath)
{
    short imageCount = 0;

    using (StreamReader sReader = new StreamReader(iconFilePath))
    {
        using (BinaryReader bReader = new BinaryReader(sReader.BaseStream))
        {
            // Retrieve icon count inside icon file
            bReader.ReadInt16();
            bReader.ReadInt16();
            imageCount = bReader.ReadInt16();
        }
    }

    // Change the executable's icon
    ChangeIcon(exeFilePath, iconFilePath, imageCount);
}

希望至少有人会发现这很有用。

This is the solution that worked for me. I was not able to write it in .NET, but have managed to write C++ DLL which I am referencing in my C# application.

C++ DLL

The contents of C++ solution I am building the DLL from:

#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <windows.h>

extern "C"
{
    #pragma pack(push, 2)
    typedef struct {
        WORD Reserved1;       // reserved, must be 0
        WORD ResourceType;    // type is 1 for icons
        WORD ImageCount;      // number of icons in structure (1)
        BYTE Width;           // icon width (32)
        BYTE Height;          // icon height (32)
        BYTE Colors;          // colors (0 means more than 8 bits per pixel)
        BYTE Reserved2;       // reserved, must be 0
        WORD Planes;          // color planes
        WORD BitsPerPixel;    // bit depth
        DWORD ImageSize;      // size of structure
        WORD ResourceID;      // resource ID
    } GROUPICON;
    #pragma pack(pop)

    __declspec(dllexport) void __stdcall ChangeIcon(char *executableFile, char *iconFile, INT16 imageCount)
    {
        int len = strlen(executableFile) + 1;
        wchar_t *executableFileEx = new wchar_t[len];
        memset(executableFileEx, 0, len);
        ::MultiByteToWideChar(CP_ACP, NULL, executableFile, -1, executableFileEx, len);

        len = strlen("MAINICON") + 1;
        wchar_t *mainIconEx = new wchar_t[len];
        memset(mainIconEx, 0, len);
        ::MultiByteToWideChar(CP_ACP, NULL, "MAINICON", -1, mainIconEx, len);

        HANDLE hWhere = BeginUpdateResource(executableFileEx, FALSE);

        char *buffer;    // Buffer to store raw icon data
        long buffersize; // Length of buffer
        int hFile;       // File handle
       
        hFile = open(iconFile, O_RDONLY | O_BINARY);
        if (hFile == -1)
            return; // If file doesn't exist, can't be opened etc. 
       
        // Calculate buffer length and load file into buffer
        buffersize = filelength(hFile);
        buffer = (char *)malloc(buffersize);
        read(hFile, buffer, buffersize);
        close(hFile);

        // Calculate header size
        int headerSize = 6 + imageCount * 16;

        UpdateResource(
            hWhere,  // Handle to executable
            RT_ICON, // Resource type - icon
            MAKEINTRESOURCE(1), // Make the id 1
            MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), // Default language
            buffer + headerSize, // Skip the header bytes
            buffersize - headerSize  // Length of buffer
        );


        GROUPICON grData;

        grData.Reserved1 = 0;     // reserved, must be 0
        grData.ResourceType = 1;  // type is 1 for icons
        grData.ImageCount = 1;    // number of icons in structure (1)

        grData.Width = 32;        // icon width (32)
        grData.Height = 32;       // icon height (32)
        grData.Colors = 0;        // colors (256)
        grData.Reserved2 = 0;     // reserved, must be 0
        grData.Planes = 2;        // color planes
        grData.BitsPerPixel = 32; // bit depth
        grData.ImageSize = buffersize - 22; // size of image
        grData.ResourceID = 1;       // resource ID is 1

        UpdateResource(
            hWhere,
            RT_GROUP_ICON,
            // RT_GROUP_ICON resources contain information
            // about stored icons
            mainIconEx,
            // MAINICON contains information about the
            // application's displayed icon
            MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
            &grData,
            // Pointer to this structure
            sizeof(GROUPICON)
        );

        delete buffer; // free memory
         
        // Perform the update, don't discard changes
        EndUpdateResource(hWhere, FALSE);
    }
}

C# code

This is C# code which I'm using to import ChangeIcon function from previously written DLL:

[DllImport("IconChanger.dll")]
static extern void ChangeIcon(String executableFile, String iconFile, short imageCount);

/// <summary>
/// Changes the executable's icon
/// </summary>
/// <param name="exeFilePath">Path to executable file</param>
/// <param name="iconFilePath">Path to icon file</param>
static public void ChangeIcon(string exeFilePath, string iconFilePath)
{
    short imageCount = 0;

    using (StreamReader sReader = new StreamReader(iconFilePath))
    {
        using (BinaryReader bReader = new BinaryReader(sReader.BaseStream))
        {
            // Retrieve icon count inside icon file
            bReader.ReadInt16();
            bReader.ReadInt16();
            imageCount = bReader.ReadInt16();
        }
    }

    // Change the executable's icon
    ChangeIcon(exeFilePath, iconFilePath, imageCount);
}

Hope at least somebody will find this useful.

清醇 2024-10-08 09:27:21

UpdateResource 的 C# 声明:

    /// <summary>
    /// Adds, deletes, or replaces a resource in a portable executable (PE) file. There are some restrictions on resource updates in files that contain Resource Configuration (RC Config) data: language-neutral (LN) files and language-specific resource (.mui) files.
    /// </summary>
    /// <param name="hUpdate">A module handle returned by the BeginUpdateResource function, referencing the file to be updated. </param>
    /// <param name="lpType">The resource type to be updated. Alternatively, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID), where ID is an integer value representing a predefined resource type. If the first character of the string is a pound sign (#), then the remaining characters represent a decimal number that specifies the integer identifier of the resource type. For example, the string "#258" represents the identifier 258. For a list of predefined resource types, see Resource Types. </param>
    /// <param name="lpName">The name of the resource to be updated. Alternatively, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID), where ID is a resource ID. When creating a new resource do not use a string that begins with a '#' character for this parameter.</param>
    /// <param name="wLanguage">The language identifier of the resource to be updated. For a list of the primary language identifiers and sublanguage identifiers that make up a language identifier, see the MAKELANGID macro. </param>
    /// <param name="lpData">The resource data to be inserted into the file indicated by hUpdate. If the resource is one of the predefined types, the data must be valid and properly aligned. Note that this is the raw binary data to be stored in the file indicated by hUpdate, not the data provided by LoadIcon, LoadString, or other resource-specific load functions. All data containing strings or text must be in Unicode format. lpData must not point to ANSI data. If lpData is NULL and cbData is 0, the specified resource is deleted from the file indicated by hUpdate. Prior to Windows 7: If lpData is NULL and cbData is nonzero, the specified resource is NOT deleted and an exception is thrown.</param>
    /// <param name="cbData">The size, in bytes, of the resource data at lpData. </param>
    /// <returns>Returns TRUE if successful or FALSE otherwise. To get extended error information, call GetLastError.</returns>
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)]
    public static extern Int32 UpdateResourceW(void* hUpdate, char* lpType, char* lpName, UInt16 wLanguage, [CanBeNull] void* lpData, UInt32 cbData);

对于字符串资源类型或名称,只需传递字符串即可。
对于系统预定义的类型(例如 RT_ICON)和 int ID(例如 IDI_APPLICATION),您可以将该整数值重新解释转换为指针,例如 (char*)3< /code> 代表 RT_ICON

C# declaration for UpdateResource:

    /// <summary>
    /// Adds, deletes, or replaces a resource in a portable executable (PE) file. There are some restrictions on resource updates in files that contain Resource Configuration (RC Config) data: language-neutral (LN) files and language-specific resource (.mui) files.
    /// </summary>
    /// <param name="hUpdate">A module handle returned by the BeginUpdateResource function, referencing the file to be updated. </param>
    /// <param name="lpType">The resource type to be updated. Alternatively, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID), where ID is an integer value representing a predefined resource type. If the first character of the string is a pound sign (#), then the remaining characters represent a decimal number that specifies the integer identifier of the resource type. For example, the string "#258" represents the identifier 258. For a list of predefined resource types, see Resource Types. </param>
    /// <param name="lpName">The name of the resource to be updated. Alternatively, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID), where ID is a resource ID. When creating a new resource do not use a string that begins with a '#' character for this parameter.</param>
    /// <param name="wLanguage">The language identifier of the resource to be updated. For a list of the primary language identifiers and sublanguage identifiers that make up a language identifier, see the MAKELANGID macro. </param>
    /// <param name="lpData">The resource data to be inserted into the file indicated by hUpdate. If the resource is one of the predefined types, the data must be valid and properly aligned. Note that this is the raw binary data to be stored in the file indicated by hUpdate, not the data provided by LoadIcon, LoadString, or other resource-specific load functions. All data containing strings or text must be in Unicode format. lpData must not point to ANSI data. If lpData is NULL and cbData is 0, the specified resource is deleted from the file indicated by hUpdate. Prior to Windows 7: If lpData is NULL and cbData is nonzero, the specified resource is NOT deleted and an exception is thrown.</param>
    /// <param name="cbData">The size, in bytes, of the resource data at lpData. </param>
    /// <returns>Returns TRUE if successful or FALSE otherwise. To get extended error information, call GetLastError.</returns>
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)]
    public static extern Int32 UpdateResourceW(void* hUpdate, char* lpType, char* lpName, UInt16 wLanguage, [CanBeNull] void* lpData, UInt32 cbData);

For a string resource type or name, you just pass the string.
For system-predefined types like RT_ICON and int IDs like IDI_APPLICATION, you pass that integer value reinterpret-casting it to a pointer, like (char*)3 for RT_ICON.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文