Example #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);
                }
            }
        }
Example #2
0
        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);
        }
Example #3
0
        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);
        }
Example #4
0
        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);
            }
        }