在C#中使用UpdateResource?
我正试图以编程方式更改外部可执行文件的图标。我已经使用GC++搜索并找到了关于这个问题的很多信息。基本上,我需要使用BeginUpdateResource、UpdateResource和EndUpdateResource。问题是-我不知道在C#中传递什么给UpdateResource 以下是我目前掌握的代码:在C#中使用UpdateResource?,c#,.net,resources,icons,C#,.net,Resources,Icons,我正试图以编程方式更改外部可执行文件的图标。我已经使用GC++搜索并找到了关于这个问题的很多信息。基本上,我需要使用BeginUpdateResource、UpdateResource和EndUpdateResource。问题是-我不知道在C#中传递什么给UpdateResource 以下是我目前掌握的代码: class IconChanger { [DllImport("kernel32.dll", SetLastError = true)] static extern Int
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;
}
}
class-IconChanger
{
[DllImport(“kernel32.dll”,SetLastError=true)]
静态外部IntPtr BEGINUUPDATERESOURCE(字符串pFileName,
[Marshallas(UnmanagedType.Bool)]Bool-b删除现有资源;
[DllImport(“kernel32.dll”,SetLastError=true)]
静态外部布尔更新资源(IntPtr hUpdate、字符串lpType、字符串lpName、ushort wLanguage、,
IntPtr lpData,uint cbData);
[DllImport(“kernel32.dll”,SetLastError=true)]
静态外部bool EndUpdateResource(IntPtr hUpdate,bool fDiscard);
公共枚举结果
{
成功,,
失败开始,
故障更新,
失效端
}
公共ICResult ChangeIcon(字符串exeFilePath,字节[]iconData)
{
//加载可执行文件
IntPtr handleExe=beginupdatesource(exeFilePath,false);
if(handleExe==null)
返回ICResult.FailBegin;
//获取语言标识符
CultureInfo currentCulture=CultureInfo.currentCulture;
intpid=((ushort)currentCulture.LCID)和0x3ff;
intSID=((ushort)currentCulture.LCID)>>10;
ushort languageID=(ushort)((ushort)pid)只是一些指针,这很难正确。通过谎报lpType参数来传递RT_图标。将其从字符串更改为IntPtr并传递(IntPtr)3
lpData参数相当棘手。您需要以资源编译器(rc.exe)编译的方式传递数据。我不知道它是否会损坏.ico文件的原始数据。唯一合理的尝试是使用FileStream将.ico文件中的数据读入字节[],您似乎已经在这样做了。我认为该函数实际上是为了将一个资源从一个二进制图像复制到另一个二进制图像而设计的。您的方法工作的可能性不是零
您还忽略了另一个潜在的问题,即程序图标的资源ID不一定是1。通常情况下不是,100往往是一个流行的选择,但任何东西都可以。EnumResourceNames将需要确保它的可靠性。规则是编号最低的ID设置文件的图标。我不确定这是否是真的ly意味着资源编译器将最小的数字放在第一位,而API可能不会这样做
一个非常小的故障模式是UpdateResource只能更新编号的资源项,而不能更新命名的资源项。使用名称代替数字并不少见,但绝大多数图像都使用数字作为图标
当然,没有UAC清单的情况下,这种可能性是零。你正在窃取那些你通常无法访问的文件。
< P>这是我的解决方案。我不能在.NET中写它,但是已经成功地编写了我在C语言应用中引用的C++ DLL。< /P>
C++动态链接库
< C++的内容>我从d:/p>中构建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#更新资源声明:
//
///添加、删除或替换可移植可执行文件(PE)中的资源。对包含资源配置(RC Config)数据的文件中的资源更新有一些限制:语言中立(LN)文件和特定于语言的资源(.mui)文件。
///
///BeginUpdateResource函数返回的模块句柄,引用要更新的文件。
///要更新的资源类型。或者,此参数可以是MAKEINTRESOURCE(ID),而不是指针,其中ID是表示预定义资源类型的整数值。如果字符串的第一个字符是磅符号(#),则其余字符表示指定资源类型整数标识符的十进制数。例如,字符串“#258”表示标识符258。有关预定义资源类型的列表,请参阅资源类型。
///要更新的资源的名称。或者,此参数可以是MAKEINTRESOURCE(ID),而不是指针,其中ID是资源ID。创建新资源时,不要使用以“#”字符开头的字符串作为此参数。
///要更新的资源的语言标识符。有关组成语言标识符的主语言标识符和子语言标识符的列表,请参阅MAKELANGID宏。
///要插入hUpdate指示的文件中的资源数据。如果资源是预定义类型之一,则数据必须有效且正确对齐。请注意,这是hUpdate指示的文件中要存储的原始二进制数据,而不是LoadIcon、LoadString或其他特定于资源的加载函数提供的数据。所有数据包含的字符串或文本必须为Unicode格式。lpData不能指向ANSI数据。如果lpData为NULL且cbData为0,则从hUpdate指示的文件中删除指定的资源。在Windows 7之前:如果lpData为NULL且cbData为非零,则不会删除指定的资源并引发异常。
///lpData处资源数据的大小(字节)。
///如果成功,则返回TRUE,否则返回FALSE。若要获取扩展错误信息,请调用GetLastError。
[DllImport(“kernel32.dll”,CharSet=CharSet.Unicode,PreserveSig=true,SetLastError=true,ExactSpelling=true)]
public static extern Int32 UpdateResourceW(void*hUpdate,char*lpType,char*lpName,UInt16语言,[CanBeNull]void*lpData,UInt32 cbData);
对于字符串资源类型或名称,只需传递字符串。
对于系统预定义的类型,如RT_ICON
和int-id,如IDI_APPLICATION
,将该整数值传递给
[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);
}
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;
}
}