public unsafe void Save(MultiIcon multiIcon, Stream stream) { // LoadLibraryEx only can load files from File System, lets create a tmp file string tmpFile = null; IntPtr hLib = IntPtr.Zero; MemoryStream ms; bool bResult; try { stream.Position = 0; // Find a tmp file where to dump the DLL stream, later we will remove this file tmpFile = Path.GetTempFileName(); FileStream fs = new FileStream(tmpFile, FileMode.Create, FileAccess.Write); byte[] buffer = Resource.EmptyDll; stream.Read(buffer, 0, buffer.Length); fs.Write(buffer, 0, buffer.Length); fs.Close(); // Begin the injection process IntPtr updPtr = Win32.BeginUpdateResource(tmpFile, false); if (updPtr == IntPtr.Zero) { throw new InvalidFileException(); } ushort iconIndex = 1; foreach (SingleIcon singleIcon in multiIcon) { // Lets scan all groups GRPICONDIR grpIconDir = GRPICONDIR.Initalizated; grpIconDir.idCount = (ushort)singleIcon.Count; grpIconDir.idEntries = new GRPICONDIRENTRY[grpIconDir.idCount]; for (int i = 0; i < singleIcon.Count; i++) { // Inside every Icon let update every image format IconImage iconImage = singleIcon[i]; grpIconDir.idEntries[i] = iconImage.GRPICONDIRENTRY; grpIconDir.idEntries[i].nID = iconIndex; // Buffer creation with the same size of the icon to optimize write call ms = new MemoryStream((int)grpIconDir.idEntries[i].dwBytesInRes); iconImage.Write(ms); buffer = ms.GetBuffer(); // Update resource but it doesn't write to disk yet bResult = Win32.UpdateResource(updPtr, (int)ResourceType.RT_ICON, iconIndex, 0, buffer, (uint)ms.Length); iconIndex++; // For some reason Windows will fail if there are many calls to update resource and no call to endUpdateResource // It is like there some internal buffer that gets full, after that all calls fail. // This workaround will save the changes every 70 icons, for big files this slow the saving process significantly // but I didn't find a way to make EndUpdateResource works without save frequently if ((iconIndex % 70) == 0) { bResult = Win32.EndUpdateResource(updPtr, false); updPtr = Win32.BeginUpdateResource(tmpFile, false); if (updPtr == IntPtr.Zero) { throw new InvalidFileException(); } } } // Buffer creation with the same size of the group to optimize write call ms = new MemoryStream(grpIconDir.GroupDirSize); grpIconDir.Write(ms); buffer = ms.GetBuffer(); int id; if (int.TryParse(singleIcon.Name, out id)) { // Write id as an integer bResult = Win32.UpdateResource(updPtr, (int)ResourceType.RT_GROUP_ICON, (IntPtr)id, 0, buffer, (uint)ms.Length); } else { // Write id as string IntPtr pName = Marshal.StringToHGlobalAnsi(singleIcon.Name.ToUpper()); bResult = Win32.UpdateResource(updPtr, (int)ResourceType.RT_GROUP_ICON, pName, 0, buffer, (uint)ms.Length); Marshal.FreeHGlobal(pName); } } // Last call to update the file with the rest not that was not write before bResult = Win32.EndUpdateResource(updPtr, false); // Because Windows Resource functions requiere a filepath, and we need to return an string then lets open // the temporary file and dump it to the stream received as parameter. fs = new FileStream(tmpFile, FileMode.Open, FileAccess.Read); buffer = new byte[fs.Length]; fs.Read(buffer, 0, buffer.Length); stream.Write(buffer, 0, buffer.Length); fs.Close(); } catch (Exception) { throw new InvalidFileException(); } finally { if (hLib != null) { Win32.FreeLibrary(hLib); } if (tmpFile != null) { File.Delete(tmpFile); } } }
public unsafe void Save(MultiIcon multiIcon, Stream stream) { //Lets prepare the complete file in memory, then we dump everything to a file IMAGE_DOS_HEADER dos_header = new IMAGE_DOS_HEADER(); IMAGE_OS2_HEADER os2_header = new IMAGE_OS2_HEADER(); RESOURCE_TABLE resource_table = new RESOURCE_TABLE(); TYPEINFO rscTypes_Group = new TYPEINFO(); TYPEINFO rscTypes_Icon = new TYPEINFO(); TNAMEINFO[] nameInfos_Group; TNAMEINFO[] nameInfos_Icon; byte[] resourceNames; List <GRPICONDIR> groupIcons = new List <GRPICONDIR>(); Dictionary <ushort, IconImage> icons = new Dictionary <ushort, IconImage>(); int offset = 0; // Lets set the MS DOS header dos_header.e_magic = (int)HeaderSignatures.IMAGE_DOS_SIGNATURE; // MZ dos_header.e_lfanew = 144; // NE Header location. dos_header.e_cblp = 80; dos_header.e_cp = 2; dos_header.e_cparhdr = 4; dos_header.e_lfarlc = 64; dos_header.e_maxalloc = 65535; dos_header.e_minalloc = 15; dos_header.e_sp = 184; offset += (int)dos_header.e_lfanew; // Lets set the NE header os2_header.ne_magic = (int)HeaderSignatures.IMAGE_OS2_SIGNATURE; // NE os2_header.ne_ver = 71; os2_header.ne_rev = 70; os2_header.ne_enttab = 178; os2_header.ne_cbenttab = 10; os2_header.ne_crc = 0; os2_header.ne_flags = 33545; os2_header.ne_autodata = 3; os2_header.ne_heap = 1024; os2_header.ne_stack = 0; os2_header.ne_csip = 65536; os2_header.ne_sssp = 0; os2_header.ne_cseg = 0; // Entries in Segment Table os2_header.ne_cmod = 1; os2_header.ne_cbnrestab = 26; os2_header.ne_segtab = 64; // Offset to Segment Table os2_header.ne_rsrctab = 64; // Offset to Resource Table os2_header.ne_restab = 132; // Later will be overwriten os2_header.ne_modtab = 168; os2_header.ne_imptab = 170; os2_header.ne_nrestab = 332; os2_header.ne_cmovent = 1; os2_header.ne_align = SHIFT_FACTOR; os2_header.ne_cres = 0; os2_header.ne_exetyp = 2; // OS target = Windows. os2_header.ne_flagsothers = 0; os2_header.ne_pretthunks = 0; os2_header.ne_psegrefbytes = 0; os2_header.ne_swaparea = 0; os2_header.ne_expver = 768; // OS version = 300 offset += os2_header.ne_rsrctab; // Resoruce Table resource_table.rscAlignShift = SHIFT_FACTOR; // 9 for now, lets split the entries every 512 bytes; offset += 2; // rscAlignShift // Type Info Groups rscTypes_Group.rtTypeID = 0x8000 + (ushort)ResourceType.RT_GROUP_ICON; rscTypes_Group.rtResourceCount = (ushort)multiIcon.Count; offset += 8; // rtTypeID + rtResourceCount + rtReserved nameInfos_Group = new TNAMEINFO[multiIcon.Count]; offset += sizeof(TNAMEINFO) * multiIcon.Count; // Type Info Icons int iconCounter = 0; foreach (SingleIcon singleIcon in multiIcon) { iconCounter += singleIcon.Count; } rscTypes_Icon.rtTypeID = 0x8000 + (ushort)ResourceType.RT_ICON; rscTypes_Icon.rtResourceCount = (ushort)iconCounter; offset += 8; // rtTypeID + rtResourceCount + rtReserved nameInfos_Icon = new TNAMEINFO[iconCounter]; offset += sizeof(TNAMEINFO) * iconCounter; resource_table.rscEndTypes = 0; offset += 2; // rscEndTypes // Resource Names os2_header.ne_restab = (ushort)(offset - dos_header.e_lfanew); MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write("ICL"); foreach (SingleIcon singleIcon in multiIcon) { bw.Write(singleIcon.Name); } resourceNames = new byte[ms.Length]; Array.Copy(ms.GetBuffer(), resourceNames, resourceNames.Length); ms.Dispose(); offset += resourceNames.Length + 1; //resourceNames + rscEndNames // Here is the offset where we are going to start writting the directory int shiftOffset = (offset >> resource_table.rscAlignShift) + 1; // Name Infos Group int iconIndex = 0; for (int i = 0; i < multiIcon.Count; i++) { SingleIcon singleIcon = multiIcon[i]; GRPICONDIR groupIconDir = new GRPICONDIR(); groupIconDir.idCount = (ushort)singleIcon.Count; groupIconDir.idType = (ushort)ResourceType.RT_GROUP_ICON; // Name Infos Icons GRPICONDIRENTRY[] goupIconDirEntries = new GRPICONDIRENTRY[singleIcon.Count]; for (int j = 0; j < singleIcon.Count; j++) { nameInfos_Icon[iconIndex].rnFlags = (ushort)(ResourceMemoryType.Moveable | ResourceMemoryType.Pure | ResourceMemoryType.Unknown); nameInfos_Icon[iconIndex].rnHandle = 0; nameInfos_Icon[iconIndex].rnID = (ushort)(0x8000 + iconIndex + 1); nameInfos_Icon[iconIndex].rnUsage = 0; nameInfos_Icon[iconIndex].rnOffset = (ushort)shiftOffset; nameInfos_Icon[iconIndex].rnLength = (ushort)Math.Ceiling(singleIcon[j].IconImageSize / (float)(1 << resource_table.rscAlignShift)); shiftOffset += nameInfos_Icon[iconIndex].rnLength; goupIconDirEntries[j] = singleIcon[j].GRPICONDIRENTRY; goupIconDirEntries[j].nID = (ushort)(iconIndex + 1); icons.Add((ushort)(iconIndex + 1), singleIcon[j]); iconIndex++; } nameInfos_Group[i].rnFlags = (ushort)(ResourceMemoryType.Moveable | ResourceMemoryType.Pure | ResourceMemoryType.Unknown); nameInfos_Group[i].rnHandle = 0; nameInfos_Group[i].rnID = (ushort)(0x8000 + i + 1); nameInfos_Group[i].rnUsage = 0; nameInfos_Group[i].rnOffset = (ushort)shiftOffset; nameInfos_Group[i].rnLength = (ushort)Math.Ceiling((6 + singleIcon.Count * sizeof(GRPICONDIRENTRY)) / (float)(1 << resource_table.rscAlignShift)); groupIconDir.idEntries = goupIconDirEntries; groupIcons.Add(groupIconDir); shiftOffset += nameInfos_Group[i].rnLength; } resource_table.rscTypes = new TYPEINFO[2]; resource_table.rscTypes[0] = rscTypes_Group; resource_table.rscTypes[0].rtNameInfo = nameInfos_Group; resource_table.rscTypes[1] = rscTypes_Icon; resource_table.rscTypes[1].rtNameInfo = nameInfos_Icon; resource_table.rscResourceNames = resourceNames; // HERE WE GO TO THE FS... // Lets write the MS DOS header dos_header.Write(stream); // Lets write first Bin segment stream.Write(MSDOS_STUB, 0, MSDOS_STUB.Length); // Lets position over where the "NE" header will be stream.Seek(dos_header.e_lfanew, SeekOrigin.Begin); // Lets write the NE header os2_header.Write(stream); // Lets position over the "Resource Table" stream.Seek(os2_header.ne_rsrctab + dos_header.e_lfanew, SeekOrigin.Begin); // Lets write the "Resource Table" resource_table.Write(stream); // Now write the Icons Directory resource_table.SetGroupIcons(stream, groupIcons); // And the Images... resource_table.SetIcons(stream, icons); }
public unsafe void Save(MultiIcon multiIcon, Stream stream) { //Lets prepare the complete file in memory, then we dump everything to a file IMAGE_DOS_HEADER dos_header = new IMAGE_DOS_HEADER(); IMAGE_OS2_HEADER os2_header = new IMAGE_OS2_HEADER(); RESOURCE_TABLE resource_table = new RESOURCE_TABLE(); TYPEINFO rscTypes_Group = new TYPEINFO(); TYPEINFO rscTypes_Icon = new TYPEINFO(); TNAMEINFO[] nameInfos_Group; TNAMEINFO[] nameInfos_Icon; byte[] resourceNames; List<GRPICONDIR> groupIcons = new List<GRPICONDIR>(); Dictionary<ushort, IconImage> icons = new Dictionary<ushort,IconImage>(); int offset = 0; // Lets set the MS DOS header dos_header.e_magic = (int) HeaderSignatures.IMAGE_DOS_SIGNATURE; // MZ dos_header.e_lfanew = 144; // NE Header location. dos_header.e_cblp = 80; dos_header.e_cp = 2; dos_header.e_cparhdr = 4; dos_header.e_lfarlc = 64; dos_header.e_maxalloc = 65535; dos_header.e_minalloc = 15; dos_header.e_sp = 184; offset += (int) dos_header.e_lfanew; // Lets set the NE header os2_header.ne_magic = (int) HeaderSignatures.IMAGE_OS2_SIGNATURE; // NE os2_header.ne_ver = 71; os2_header.ne_rev = 70; os2_header.ne_enttab = 178; os2_header.ne_cbenttab = 10; os2_header.ne_crc = 0; os2_header.ne_flags = 33545; os2_header.ne_autodata = 3; os2_header.ne_heap = 1024; os2_header.ne_stack = 0; os2_header.ne_csip = 65536; os2_header.ne_sssp = 0; os2_header.ne_cseg = 0; // Entries in Segment Table os2_header.ne_cmod = 1; os2_header.ne_cbnrestab = 26; os2_header.ne_segtab = 64; // Offset to Segment Table os2_header.ne_rsrctab = 64; // Offset to Resource Table os2_header.ne_restab = 132;// Later will be overwriten os2_header.ne_modtab = 168; os2_header.ne_imptab = 170; os2_header.ne_nrestab = 332; os2_header.ne_cmovent = 1; os2_header.ne_align = SHIFT_FACTOR; os2_header.ne_cres = 0; os2_header.ne_exetyp = 2; // OS target = Windows. os2_header.ne_flagsothers = 0; os2_header.ne_pretthunks = 0; os2_header.ne_psegrefbytes = 0; os2_header.ne_swaparea = 0; os2_header.ne_expver = 768; // OS version = 300 offset += os2_header.ne_rsrctab; // Resoruce Table resource_table.rscAlignShift = SHIFT_FACTOR; // 9 for now, lets split the entries every 512 bytes; offset += 2; // rscAlignShift // Type Info Groups rscTypes_Group.rtTypeID = 0x8000 + (ushort) ResourceType.RT_GROUP_ICON; rscTypes_Group.rtResourceCount = (ushort) multiIcon.Count; offset += 8; // rtTypeID + rtResourceCount + rtReserved nameInfos_Group = new TNAMEINFO[multiIcon.Count]; offset += sizeof(TNAMEINFO) * multiIcon.Count; // Type Info Icons int iconCounter = 0; foreach(SingleIcon singleIcon in multiIcon) iconCounter += singleIcon.Count; rscTypes_Icon.rtTypeID = 0x8000 + (ushort) ResourceType.RT_ICON; rscTypes_Icon.rtResourceCount = (ushort) iconCounter; offset += 8; // rtTypeID + rtResourceCount + rtReserved nameInfos_Icon = new TNAMEINFO[iconCounter]; offset += sizeof(TNAMEINFO) * iconCounter; resource_table.rscEndTypes = 0; offset += 2; // rscEndTypes // Resource Names os2_header.ne_restab = (ushort) (offset - dos_header.e_lfanew); MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write("ICL"); foreach(SingleIcon singleIcon in multiIcon) bw.Write(singleIcon.Name); resourceNames = new byte[ms.Length]; Array.Copy(ms.GetBuffer(), resourceNames, resourceNames.Length); ms.Dispose(); offset += resourceNames.Length + 1; //resourceNames + rscEndNames // Here is the offset where we are going to start writting the directory int shiftOffset = (offset >> resource_table.rscAlignShift) + 1; // Name Infos Group int iconIndex = 0; for(int i=0; i<multiIcon.Count; i++) { SingleIcon singleIcon = multiIcon[i]; GRPICONDIR groupIconDir = new GRPICONDIR(); groupIconDir.idCount = (ushort) singleIcon.Count; groupIconDir.idType = (ushort) ResourceType.RT_GROUP_ICON; // Name Infos Icons GRPICONDIRENTRY[] goupIconDirEntries = new GRPICONDIRENTRY[singleIcon.Count]; for(int j=0; j<singleIcon.Count; j++) { nameInfos_Icon[iconIndex].rnFlags = (ushort) (ResourceMemoryType.Moveable | ResourceMemoryType.Pure | ResourceMemoryType.Unknown); nameInfos_Icon[iconIndex].rnHandle = 0; nameInfos_Icon[iconIndex].rnID = (ushort) (0x8000 + iconIndex + 1); nameInfos_Icon[iconIndex].rnUsage = 0; nameInfos_Icon[iconIndex].rnOffset = (ushort) shiftOffset; nameInfos_Icon[iconIndex].rnLength = (ushort) Math.Ceiling(singleIcon[j].IconImageSize / (float) (1 << resource_table.rscAlignShift)); shiftOffset += nameInfos_Icon[iconIndex].rnLength; goupIconDirEntries[j] = singleIcon[j].GRPICONDIRENTRY; goupIconDirEntries[j].nID = (ushort) (iconIndex + 1); icons.Add((ushort) (iconIndex + 1), singleIcon[j]); iconIndex++; } nameInfos_Group[i].rnFlags = (ushort) (ResourceMemoryType.Moveable | ResourceMemoryType.Pure | ResourceMemoryType.Unknown); nameInfos_Group[i].rnHandle = 0; nameInfos_Group[i].rnID = (ushort) (0x8000 + i + 1); nameInfos_Group[i].rnUsage = 0; nameInfos_Group[i].rnOffset = (ushort) shiftOffset; nameInfos_Group[i].rnLength = (ushort) Math.Ceiling((6 + singleIcon.Count * sizeof(GRPICONDIRENTRY)) / (float) (1 << resource_table.rscAlignShift)); groupIconDir.idEntries = goupIconDirEntries; groupIcons.Add(groupIconDir); shiftOffset += nameInfos_Group[i].rnLength; } resource_table.rscTypes = new TYPEINFO[2]; resource_table.rscTypes[0] = rscTypes_Group; resource_table.rscTypes[0].rtNameInfo = nameInfos_Group; resource_table.rscTypes[1] = rscTypes_Icon; resource_table.rscTypes[1].rtNameInfo = nameInfos_Icon; resource_table.rscResourceNames = resourceNames; // HERE WE GO TO THE FS... // Lets write the MS DOS header dos_header.Write(stream); // Lets write first Bin segment stream.Write(MSDOS_STUB, 0, MSDOS_STUB.Length); // Lets position over where the "NE" header will be stream.Seek(dos_header.e_lfanew, SeekOrigin.Begin); // Lets write the NE header os2_header.Write(stream); // Lets position over the "Resource Table" stream.Seek(os2_header.ne_rsrctab + dos_header.e_lfanew, SeekOrigin.Begin); // Lets write the "Resource Table" resource_table.Write(stream); // Now write the Icons Directory resource_table.SetGroupIcons(stream, groupIcons); // And the Images... resource_table.SetIcons(stream, icons); }
public static void ChangeIcon(string exeFilePath, string icoFilePath) { int len = Marshal.SizeOf(typeof(GRPICONDIR)); using (FileStream fs = new FileStream(icoFilePath, FileMode.Open, FileAccess.Read)) { // 读取图标目录 ICONDIR iconDir = fs.Read <ICONDIR>(); // 读取图标头列表 List <ICONDIRENTRY> iconDirEntrys = new List <ICONDIRENTRY>(); for (int i = 0; i < iconDir.idCount; i++) { ICONDIRENTRY iconDirEntry = fs.Read <ICONDIRENTRY>(); iconDirEntrys.Add(iconDirEntry); } // 读取图标数据列表 List <byte[]> iconDatas = new List <byte[]>(); for (int i = 0; i < iconDir.idCount; i++) { byte[] iconData = new byte[iconDirEntrys[i].dwBytesInRes]; fs.Seek(iconDirEntrys[i].dwImageOffset, SeekOrigin.Begin); fs.Read(iconData, 0, iconData.Length); iconDatas.Add(iconData); } // 生成GRPICONDIR GRPICONDIR grpIconDir = new GRPICONDIR(); grpIconDir.idCount = iconDir.idCount; grpIconDir.idReserved = 0; grpIconDir.idType = 1; // 1代表图标 // 生成List<GRPICONDIRENTRY> List <GRPICONDIRENTRY> grpIconDirEntrys = new List <GRPICONDIRENTRY>(); for (int i = 0; i < iconDirEntrys.Count; i++) { GRPICONDIRENTRY grpIconDirEntry = new GRPICONDIRENTRY(); grpIconDirEntry.CopyFrom(iconDirEntrys[i]); grpIconDirEntry.nID = (short)(i + 1); grpIconDirEntrys.Add(grpIconDirEntry); } List <byte> grpData = new List <byte>(); grpData.AddRange(grpIconDir.ToByteArray()); for (int i = 0; i < grpIconDirEntrys.Count; i++) { grpData.AddRange(grpIconDirEntrys[i].ToByteArray()); } IntPtr grpDataPtr = grpData.ToArray().ToPtr(); bool ret = false; IntPtr pUpdateRes = BeginUpdateResource(exeFilePath, false); for (int i = 0; i < grpIconDirEntrys.Count; i++) { // 更新图标数据 int id = grpIconDirEntrys[i].nID; ret = UpdateResource(pUpdateRes, new IntPtr((int)eResourceTypes.RT_ICON), new IntPtr(id), 0, iconDatas[i].ToPtr(), iconDatas[i].Length); } // 更新图标目录和图标头 // 32512为当前exe中Icon Group下id号 ret = UpdateResource(pUpdateRes, new IntPtr((int)eResourceTypes.RT_GROUP_ICON), new IntPtr(32512), 0, grpDataPtr, grpData.Count); ret = EndUpdateResource(pUpdateRes, false); } }