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); } } }