Ejemplo n.º 1
0
        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);
                }
            }
        }