在Quicker用C#给无图标的exe添加图标

瞑空凌 2025/2/8 发布 · 2025/2/8 更新 · 1634 次阅读

注: 原本是替换图标来着, 但还没读取原本图标信息代码, 而自己又想分享出来所以变成给没有图标的exe加图标
代码来源:使用deepseekvb替换exe文件转译出的C#代码,deepseek真是神器啊.写的代码能直接运行,我自己找不到的问题,deepseek轻轻松松分析出来并优化.复制即可使用.属实流弊👍
相关资料还有:C#更新exe文件图标 - fanu - 博客园   代码实现pe文件图标替换_pe图标修改-CSDN博客

以下是GIF演示(代码在GIF下面):


以下是运行成功的代码:

//.cs  文件类型,便于外部编辑时使用
// 引用必要的命名空间
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
 
// Quicker将会调用的函数。可以根据需要修改返回值类型。
public static void Exec(Quicker.Public.IStepContext context)
{
    //var oldValue = context.GetVarValue("varName");  // 读取动作里的变量值
    //MessageBox.Show(oldValue as string);
    //context.SetVarValue("varName", "从脚本输出的内容。"); // 向变量里输出值
    UpdateIcon(@"C:\Users\dell\Desktop\批处理调试\html\test\showPopup.exe",@"C:\Users\dell\Desktop\批处理调试\html\test\test.ico");
}
 
// API声明
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CreateFile(
    string lpFileName,
    uint dwDesiredAccess,
    uint dwShareMode,
    IntPtr lpSecurityAttributes,
    uint dwCreationDisposition,
    uint dwFlagsAndAttributes,
    IntPtr hTemplateFile);
 
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadFile(
    IntPtr hFile,
    byte[] lpBuffer,
    uint nNumberOfBytesToRead,
    out uint lpNumberOfBytesRead,
    IntPtr lpOverlapped);
 
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint SetFilePointer(
    IntPtr hFile,
    int lDistanceToMove,
    IntPtr lpDistanceToMoveHigh,
    uint dwMoveMethod);
 
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr BeginUpdateResource(
    string pFileName,
    [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);
 
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool UpdateResource(
    IntPtr hUpdate,
    IntPtr lpType,
    IntPtr lpName,
    ushort wLanguage,
    byte[] lpData,
    uint cbData);
 
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool EndUpdateResource(
    IntPtr hUpdate,
    [MarshalAs(UnmanagedType.Bool)]bool fDiscard);
 
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
 
// 常量定义
private const uint GENERIC_READ = 0x80000000;
private const uint OPEN_EXISTING = 3;
private const uint FILE_ATTRIBUTE_NORMAL = 0x80;
private const uint FILE_BEGIN = 0;
private static readonly IntPtr RT_ICON = (IntPtr)3;
private static readonly IntPtr RT_GROUP_ICON = (IntPtr)14;
private static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
 
// 结构体定义
[StructLayout(LayoutKind.Sequential)]
private struct ICONDIR
{
    public ushort idReserved;
    public ushort idType;
    public ushort idCount;
}
 
[StructLayout(LayoutKind.Sequential)]
private struct ICONDIRENTRY
{
    public byte bWidth;
    public byte bHeight;
    public byte bColorCount;
    public byte bReserved;
    public ushort wPlanes;
    public ushort wBitCount;
    public uint dwBytesInRes;
    public uint dwImageOffset;
}
 
[StructLayout(LayoutKind.Sequential, Pack = 2)]
private struct GRPICONDIRENTRY
{
    public byte bWidth;
    public byte bHeight;
    public byte bColorCount;
    public byte bReserved;
    public ushort wPlanes;
    public ushort wBitCount;
    public uint dwBytesInRes;
    public ushort nID;
}
 
//static void Main(string[] args)
//{
//    if (args.Length != 2)
//    {
//        Console.WriteLine("Usage: IconUpdater.exe [TargetExe] [IconFile]");
//        return;
//    }
//
//    UpdateIcon(args[0], args[1]);
//}
 
static void UpdateIcon(string exeFile, string iconFile)
{
    IntPtr hIconFile = INVALID_HANDLE_VALUE;
    IntPtr hUpdate = IntPtr.Zero;
 
    try
    {
        // 打开图标文件
        hIconFile = CreateFile(iconFile, GENERIC_READ, 0, IntPtr.Zero,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
 
        if (hIconFile == INVALID_HANDLE_VALUE)
            throw new IOException("无法打开图标文件", Marshal.GetLastWin32Error());
 
        // 读取图标文件头
        ICONDIR iconDir = ReadStruct<ICONDIR>(hIconFile);
        if (iconDir.idType != 1)
            throw new InvalidDataException("无效的图标文件");
 
        // 读取图标条目
        ICONDIRENTRY[] entries = new ICONDIRENTRY[iconDir.idCount];
        for (int i = 0; i < iconDir.idCount; i++)
        {
            entries[i] = ReadStruct<ICONDIRENTRY>(hIconFile);
        }
 
        // 开始更新资源
        hUpdate = BeginUpdateResource(exeFile, false);
        if (hUpdate == IntPtr.Zero)
            throw new IOException("无法开始更新资源", Marshal.GetLastWin32Error());
 
        // 写入图标资源
        for (ushort i = 0; i < iconDir.idCount; i++)
        {
            byte[] iconData = ReadIconData(hIconFile, entries[i]);
            if (!UpdateResource(hUpdate, RT_ICON, (IntPtr)(i + 1), 0, iconData, (uint)iconData.Length))
                throw new IOException("更新图标资源失败", Marshal.GetLastWin32Error());
        }
 
        // 创建图标组资源
        byte[] groupData = CreateGroupIconData(iconDir, entries);
        if (!UpdateResource(hUpdate, RT_GROUP_ICON, (IntPtr)32512, 0, groupData, (uint)groupData.Length))
            throw new IOException("更新图标组失败", Marshal.GetLastWin32Error());
    }
    finally
    {
        if (hUpdate != IntPtr.Zero)
            EndUpdateResource(hUpdate, false);
        if (hIconFile != INVALID_HANDLE_VALUE)
            CloseHandle(hIconFile);
    }
}
 
private static T ReadStruct<T>(IntPtr hFile) where T : struct
{
    int size = Marshal.SizeOf<T>();
    byte[] buffer = new byte[size];
    uint bytesRead;
 
    if (!ReadFile(hFile, buffer, (uint)size, out bytesRead, IntPtr.Zero) || bytesRead != size)
        throw new IOException("读取文件失败", Marshal.GetLastWin32Error());
 
    GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
    try
    {
        return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
    }
    finally
    {
        handle.Free();
    }
}
 
private static byte[] ReadIconData(IntPtr hFile, ICONDIRENTRY entry)
{
    byte[] data = new byte[entry.dwBytesInRes];
    uint bytesRead;
 
    if (SetFilePointer(hFile, (int)entry.dwImageOffset, IntPtr.Zero, FILE_BEGIN) == 0xFFFFFFFF)
        throw new IOException("设置文件指针失败", Marshal.GetLastWin32Error());
 
    if (!ReadFile(hFile, data, entry.dwBytesInRes, out bytesRead, IntPtr.Zero) || bytesRead != entry.dwBytesInRes)
        throw new IOException("读取图标数据失败", Marshal.GetLastWin32Error());
 
    return data;
}
 
private static byte[] CreateGroupIconData(ICONDIR dir, ICONDIRENTRY[] entries)
{
    using (MemoryStream ms = new MemoryStream())
    using (BinaryWriter writer = new BinaryWriter(ms))
    {
        // 写入组头(固定3个字段,每个字段2字节)
        writer.Write(dir.idReserved);    // ushort
        writer.Write(dir.idType);        // ushort
        writer.Write(dir.idCount);       // ushort
 
        // 手动写入每个 GRPICONDIRENTRY 条目,避免结构体对齐问题
        for (ushort i = 0; i < entries.Length; i++)
        {
            ICONDIRENTRY entry = entries[i];
 
            // 按标准 GRPICONDIRENTRY 结构写入(共14字节)
            writer.Write(entry.bWidth);         // byte
            writer.Write(entry.bHeight);        // byte
            writer.Write(entry.bColorCount);    // byte
            writer.Write(entry.bReserved);      // byte
            writer.Write(entry.wPlanes);        // ushort
            writer.Write(entry.wBitCount);      // ushort
            writer.Write(entry.dwBytesInRes);   // uint
            writer.Write((ushort)(i + 1));      // nID: ushort(关键修正点)
        }
 
        return ms.ToArray();
    }
}
· {{comment.createTimeStr}}
{{reply.votePoints}}
回复   – {{reply.createTimeStr}}
回复 x
标签
目录
相关操作