Jump to content


Photo

Estudio de Iconos en los recursos de los ejecutables y mas...


  • Please log in to reply
12 replies to this topic

#1 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 11 December 2009 - 05:11 PM

Hace ya dos años escribí un código para modificar los iconos de la sección de recursos de un ejecutable .exe o .dll. Diversas funciones los extraían o los cambiaban. También escribí código para asignar esos iconos desde un TIcon. Todo ese código lo publiqué en CD. El caso es que desde el último gran terremoto que ocurrió en el club vecino he empezado a temer que se pierda y he decidido traer el tema aquí, esta vez en forma de tutorial.

El código es todo C/C++ con API y algo de la VCL en lo que se refiere a la asignación de un TIcon.

Saludos.


PD: Añado código y ejemplos en delphi de como guardar un HICON en un archivo.ico y de como asignarlo a un objeto TIcon sin perder colores.

Attached Files


  • 0

#2 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 11 December 2009 - 05:12 PM

En primer lugar, he de aclarar que los Iconos de le sección de recursos están agrupados en grupos con una cabecera de tipo GRPICONDIR. En esa cabecera tenemos, entre otras cosas, el número de imágenes que contiene y un array de entradas a cada imagen. Cada entrada es del tipo MEMICONDIRENTRY o GRPICONDIRENTRY (son lo mismo).

Los archivos de iconos se comportan igual pero únicamente contienen un sólo Grupo con una o varias imágenes. Su cabecera es del tipo ICONDIR y este contiene, entre otras cosas, el número de iconos y las entradas correspondientes a cada uno del tipo ICONDIRENTRY.

Las definiciones tienen su fuente aquí.

Por si el tema de las definiciones de las cabeceras no queda claro, publico cómo lo he usado. Archivo Iconos.h:



cpp
  1. #ifndef iconosH
  2. #define iconosH
  3. //---------------------------------------------------------------------------
  4. // Tomado de [url]http://msdn.microsoft.com/en-us/library/ms997538.aspx[/url]
  5. // Modificado del original.
  6.  
  7. // Necesario para alinear a Word el compilador
  8. #pragma pack( push )
  9. #pragma pack( 2 )
  10.  
  11. // These first two structs represent how the icon information is stored
  12. // when it is bound into a EXE or DLL file. Structure members are WORD
  13. // aligned and the last member of the structure is the ID instead of
  14. // the imageoffset.
  15.  
  16. typedef struct
  17. {
  18.     BYTE    bWidth;              // Width of the image
  19.     BYTE    bHeight;              // Height of the image (times 2)
  20.     BYTE    bColorCount;          // Number of colors in image (0 if >=8bpp)
  21.     BYTE    bReserved;            // Reserved
  22.     WORD    wPlanes;              // Color Planes
  23.     WORD    wBitCount;            // Bits per pixel
  24.     DWORD    dwBytesInRes;        // how many bytes in this resource?
  25.     WORD    nID;                  // the ID
  26. } MEMICONDIRENTRY, *LPMEMICONDIRENTRY, GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
  27.  
  28. typedef struct
  29. {
  30.     WORD            idReserved;  // Reserved
  31.     WORD            idType;      // resource type (1 for icons)
  32.     WORD            idCount;      // how many images?
  33.     MEMICONDIRENTRY    idEntries[1]; // the entries for each image
  34. } MEMICONDIR, *LPMEMICONDIR, GRPICONDIR, *LPGRPICONDIR;
  35.  
  36.  
  37. // These next two structs represent how the icon information is stored
  38. // in an ICO file.
  39. typedef struct
  40. {
  41.     BYTE    bWidth;              // Width of the image
  42.     BYTE    bHeight;              // Height of the image (times 2)
  43.     BYTE    bColorCount;          // Number of colors in image (0 if >=8bpp)
  44.     BYTE    bReserved;            // Reserved
  45.     WORD    wPlanes;              // Color Planes
  46.     WORD    wBitCount;            // Bits per pixel
  47.     DWORD    dwBytesInRes;        // how many bytes in this resource?
  48.     DWORD    dwImageOffset;        // where in the file is this image
  49. } ICONDIRENTRY, *LPICONDIRENTRY;
  50.  
  51. typedef struct
  52. {
  53.     WORD            idReserved;  // Reserved
  54.     WORD            idType;      // resource type (1 for icons)
  55.     WORD            idCount;      // how many images?
  56.     ICONDIRENTRY idEntries[1]; // the entries for each image
  57. } ICONDIR, *LPICONDIR;
  58.  
  59. #pragma pack( pop )
  60.  
  61. //---------------------------------------------------------------------------
  62. #endif



Dicho esto, lo primero que tenemos que conseguir es enumerar los recursos por su tipo, lo que nos interesa es el tipo RT_GROUP_ICON que son los grupos de iconos, es decir enumeraremos esos grupos sabiendo que cada uno puede tener varias imágenes de iconos de distinto tamaño, resolución y número de colores.



cpp
  1. //------------------------------------------------------------------------------
  2. // Funciones de enumeración de resources
  3. //------------------------------------------------------------------------------
  4. // Declaraciones previas
  5.  
  6. // Definición del tipo de función que acepta EnumResourceNames
  7. typedef BOOL (__stdcall *TP)();
  8.  
  9. // Estructura para comunicar EnumResourceNames con la función de enumeración
  10. struct TResInGr{
  11.   int Count;
  12.   int Id;
  13.   char* ResName;
  14.   HRSRC hRes;
  15. };
  16.  
  17. // Función de enumeración para la cuenta de elementos y encontrar un Resource
  18. BOOL CALLBACK
  19. EnumResGrProc(HANDLE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG lParam)
  20. {
  21.   TResInGr *Res =  (TResInGr*)lParam;
  22.   if(Res->Count == Res->Id){
  23.     Res->ResName = lpszName;
  24.     Res->hRes = ::FindResource(hModule, lpszName, lpszType);
  25.   }
  26.   Res->Count++;
  27.   return true;
  28. }
  29.  
  30. // Devuelve el Nº de Elementos de lpszType
  31. int GetCountRes(HANDLE hModule, LPCTSTR lpszType)
  32. {
  33.   TResInGr Res = {0};
  34.   Res.Id = -1;
  35.   EnumResourceNames(hModule, lpszType, (TP)EnumResGrProc, LPARAM(&Res));
  36.   return Res.Count;
  37. }
  38.  
  39. // Devuelve el Nº de Elementos de lpszType
  40. int GetCountRes(char *FuenteExe, LPCTSTR lpszType)
  41. {
  42.   HANDLE hModule = LoadLibrary(FuenteExe);
  43.   int Count = GetCountRes(hModule, lpszType);
  44.   FreeLibrary(hModule);
  45.   return Count;
  46. }
  47.  
  48. // Devuelve un HRSRC con Indice Id del array de resources lpszType
  49. // Id: Indice del Grupo de iconos como si fuese una array comenzando por 0
  50. HRSRC FindResource(HMODULE hModule, int Id, LPCTSTR lpszType)
  51. {
  52.   TResInGr Res = {0};
  53.   Res.Id = Id;
  54.   EnumResourceNames(hModule, lpszType, (TP)EnumResGrProc, LPARAM(&Res));
  55.   return Res.hRes;
  56. }



Estas funciones enumeran la sección de recursos con el propósito de contarlos con GetCountRes y encontrar un recurso determinado por su íncice, comenzando desde 0, con FindResource. Esta última función, a diferencia de la API FindResource, toma los índices como un int. La API FindResource toma los índices como una cadena formada por el carácter # seguida de un número que comienza por el 1. El primer recurso es el “#1”.

Por el momento doy por terminada esta parte.

Saludos.

  • 0

#3 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 11 December 2009 - 05:14 PM

Explicados los conceptos preliminares y la enumeración de recursos, pasamos a la extracción de los recursos. He escrito dos funciones bastante similares.

La primera, ExtractResIconFromModule, extrae un grupo de imágenes de la sección de recursos de un ejecutable o dll. La extracción es un buffer que contiene el binario del resource. La segunda, GetFileMemIconFromModule, hace lo propio pero en un buffer imagen de un archivo.ico. De cada uno se estos buffer se podrá extraer, entonces, una imagen de un icono individual, pero esto lo realizarán otras funciones.

ExtractResIconFromModule, extrae un grupo de imágenes en un buffer de memoria con cabecera GRPICONDIR (o MEMICONDIR) con las entradas a cada imagen y estas. Este buffer puede usarse para pasar el recurso entero tipo RT_GROUP_ICON a una dll o ejecutable. Esto lo realizará otra función, AddIconToExe.



cpp
  1. //---------------------------------------------------------------------------
  2. // Devuelve un ResMem Icon de un hModule
  3. // Devuelve el tamaño de la memoria de archivo de un Icono de un hModule
  4. // Retorna 0 si falla, 1 si Buffer==0 y AllSize si devuelve el valor en Buffer.
  5. // Precisa de un Buffer previo de tamaño Size
  6. // Si Buffer = NULL, devuelve en Size el tamaño necesario para el buffer
  7. // Id: Indice del Grupo de iconos como si fuese una array comenzando por 0
  8. int ExtractResIconFromModule(HMODULE hModule, int Id, void* Buffer, int* Size)
  9. {
  10.     LPGRPICONDIR grpIconDir;
  11.     LPGRPICONDIR grpSrcIconDir;
  12.     LPGRPICONDIRENTRY grpIconEntry;
  13.     LPICONIMAGE  grpIconImage;
  14.     LPICONIMAGE  resIconImage;
  15.     BYTE* IconMem = (BYTE*)Buffer;
  16.     HRSRC hRes;        // handle para res. info. del ejecutable
  17.     int IconsCount = 0;
  18.     int AllSize = sizeof(GRPICONDIR);
  19.  
  20.     // Cuento cuantos iconos tiene el recurso y la memoria necesaria
  21.     hRes = FindResource(hModule, Id, RT_GROUP_ICON);
  22.     if(hRes){
  23.       grpSrcIconDir = (LPGRPICONDIR)LoadResource(hModule, hRes);
  24.       IconsCount = grpSrcIconDir->idCount;
  25.       for(int n=0; n<IconsCount; n++)
  26.         AllSize += sizeof(ICONDIRENTRY) + grpSrcIconDir->idEntries[n].dwBytesInRes;
  27.     }
  28.     if(IconsCount==0){
  29.       *Size = 0;
  30.       return 0;
  31.     }
  32.     if(Buffer == 0 ){
  33.       *Size = AllSize;
  34.       return 1;
  35.     }
  36.     if(*Size < AllSize) return 0;
  37.  
  38.     // Preparo la Cabecera General grpIconDir
  39.     grpIconDir  = (LPGRPICONDIR)IconMem;
  40.     grpIconDir->idReserved = 0;
  41.     grpIconDir->idType = 1;
  42.     grpIconDir->idCount = IconsCount;
  43.     grpIconEntry = LPMEMICONDIRENTRY(IconMem + sizeof(GRPICONDIR) - sizeof(GRPICONDIRENTRY));
  44.  
  45.     // Localizar el ICON resource en el ejecutable y sus Imagenes.
  46.     hRes = NULL;
  47.     grpIconImage = (LPICONIMAGE)((BYTE*)grpIconDir + sizeof(GRPICONDIR) + (sizeof(GRPICONDIRENTRY)*(grpIconDir->idCount-1)));
  48.     for(int n=0; n<IconsCount; n++){
  49.       int nID = grpSrcIconDir->idEntries[n].nID;
  50.       hRes = ::FindResource(hModule, MAKEINTRESOURCE(nID), RT_ICON);
  51.       // Preparo las cabeceras Entrada de cada Imagen
  52.       resIconImage = (ICONIMAGE*)LoadResource(hModule, hRes);
  53.       grpIconEntry[n].bWidth = resIconImage->icHeader.biWidth;
  54.       grpIconEntry[n].bHeight = resIconImage->icHeader.biHeight/2;
  55.       grpIconEntry[n].bColorCount =  NColors(resIconImage->icHeader.biBitCount);
  56.       grpIconEntry[n].bReserved = 0;
  57.       grpIconEntry[n].wPlanes = 1;
  58.       grpIconEntry[n].wBitCount = resIconImage->icHeader.biBitCount;
  59.       grpIconEntry[n].dwBytesInRes = SizeofResource(hModule, hRes);
  60.       grpIconEntry[n].nID = n;
  61.       // Copio la imagen
  62.       memcpy(grpIconImage, resIconImage, grpIconEntry[n].dwBytesInRes);
  63. //      grpIconImage = (LPICONIMAGE)((BYTE*)grpIconImage + grpIconDir->idEntries[n-1].dwBytesInRes);
  64.       grpIconImage = (LPICONIMAGE)((BYTE*)grpIconImage + grpIconEntry[n].dwBytesInRes);
  65.     }
  66.  
  67.     return AllSize;
  68. }



Y para ilustrar el uso de esta función voy a colocar el código de AddIconToExe, que añade el recurso extraído con ExtractResIconFromModule, en un ejecutable o una dll:



cpp
  1. //---------------------------------------------------------------------------
  2. // grpIconDir : Buffer con el recurso del tipo RT_GROUP_ICON extraido con  ExtractResIconFromModule
  3. // DestinoExe: Nombre de un ejecutable o dll
  4. // ResName: Nombre que se le dará al recurso añadico al ejecutable o dll
  5. // BorraIconResPrevio: Si es trae se borraran los recursos previos RT_GROUP_ICON del ejecutable o dll
  6. bool  AddIconToExe(LPGRPICONDIR grpIconDir, char *DestinoExe, char* ResName, bool BorraIconResPrevio)
  7. {
  8.   LPGRPICONDIRENTRY IconEntry;
  9.   LPICONIMAGE  IconImage;
  10.   LPBYTE lpResLock;
  11.   bool Result = true;
  12.  
  13.   if(!grpIconDir) return false;
  14.   if(!DestinoExe || !*DestinoExe) return false;
  15.   if(!ResName || !*ResName) return false;
  16.  
  17.   // Crea espacio para las cabeceras del icono recurso
  18.   int HeaderSize = sizeof(GRPICONDIR)+(sizeof(GRPICONDIRENTRY)*(grpIconDir->idCount-1));
  19.  
  20.   HANDLE hUpdateRes = BeginUpdateResource(DestinoExe, BorraIconResPrevio);
  21.   // Localizo la primera imagen del icono a pasar al .exe
  22.   IconImage = (LPICONIMAGE)((BYTE*)grpIconDir + sizeof(GRPICONDIR) + (sizeof(GRPICONDIRENTRY)*(grpIconDir->idCount-1)));
  23.   for(int n=0; n<grpIconDir->idCount; n++){
  24.     lpResLock = (BYTE*)LockResource(IconImage);
  25.     // Abrir el fichero donde añadir el icono.
  26.     if (hUpdateRes != NULL){
  27.       // Actualizar el resource destino
  28.       Result &= UpdateResource(hUpdateRes,
  29.         RT_ICON,
  30.         MAKEINTRESOURCE(grpIconDir->idEntries[n].nID),
  31.         MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
  32.         lpResLock,
  33.         grpIconDir->idEntries[n].dwBytesInRes);
  34.     }
  35.     // Localizo la siguiente Imagen
  36.     IconImage = (LPICONIMAGE)((BYTE*)IconImage + grpIconDir->idEntries[n].dwBytesInRes);
  37.   }
  38.  
  39.   // Y la grabamos como "indice" o cabecera de grupo de iconos
  40.   Result &= UpdateResource(hUpdateRes, RT_GROUP_ICON,
  41.         ResName,
  42.         MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
  43.         grpIconDir, HeaderSize);
  44.  
  45.   // Escribir los cambios y cerrar.
  46.   Result &= EndUpdateResource(hUpdateRes, FALSE);
  47.  
  48.   return Result;
  49. }



Y para terminar de ilustrar estas funciones, propongo esta otra que hace uso de las dos:



cpp
  1. //------------------------------------------------------------------------------
  2. // Pasa todas las imágenes del primer Grupo Icono (principal por defecto del ejecutable)
  3. // de un exe (o dll) a otro
  4. bool AddIconToExe(char *FuenteExe, char *DestinoExe, char* ResName, bool BorraIconResPrevio)
  5. {
  6.     bool Result;
  7.     BYTE* Buffer;
  8.     int Size;   
  9.     LPGRPICONDIR grpIconDir;
  10.     HMODULE hModule;
  11.     hModule = LoadLibrary(FuenteExe);
  12.  
  13.     // Calculo el tamaño necesario para el Buffer
  14.     ExtractResIconFromModule (hModule, 0, 0, &Size);
  15.     Buffer = new BYTE[Size];
  16.  
  17.     // Extraigo del ejecutable (hModule) el primer grupo de iconos que obtengo en el Buffer
  18.     ExtractResIconFromModule(hModule, 0, Buffer, &Size);
  19.     grpIconDir  = (LPGRPICONDIR)Buffer;
  20.    
  21.     FreeLibrary(hModule);
  22.     if(grpIconDir)  // Si el Buffer es válido
  23.       Result = AddIconToExe(grpIconDir, DestinoExe, ResName, BorraIconResPrevio);
  24.     else  // Si el buffer no es válido
  25.       Result = false;
  26.  
  27.     // Libero la memoria del Grupo de iconos antes de retornar.
  28.     delete [] Buffer;
  29.  
  30.     return Result;
  31. }



En el siguiente post publicaré el modo de guardar en un archivo, un Grupo de Iconos con las funciones GetFileMemIconFromModule y SaveResIconToFile.

Saludos.
  • 0

#4 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 11 December 2009 - 05:15 PM

Continúo con la extracción de iconos de la sección de recursos de un ejecutable.

GetFileMemIconFromModule está diseñada para extraer un grupo de imágenes del recurso en un buffer que pueda ser escrito directamente a un archivo.ico. El formato es un buffer con una cabecera ICONDIR, sus entradas a las imágenes y éstas mismas. Un archivo de este tipo, a diferencia con la sección de recursos de un ejecutable, sólo tiene un grupo de iconos definido en su cabecera, aunque lo habitual es que, ese grupo sólo tenga una imagen, nada impide que tenga muchas más de diferente resolución y número de colores.



cpp
  1. //------------------------------------------------------------------------------
  2. // Devuelve en Buffer Una imagen de archivo.ico para poder guardar en disco.
  3. // Devuelve el tamaño de la memoria de archivo de un Icono de un hModule
  4. // Retorna 0 si falla, 1 si Buffer==0 y AllSize si devuelve el valor en Buffer.
  5. // Si Buffer = NULL, devuelve en Size el tamaño necesario para el buffer
  6. // Id: Indice del Grupo de iconos como si fuese una array comenzando por 0
  7. int GetFileMemIconFromModule(HMODULE hModule, int Id, void* Buffer, int *Size)
  8. {
  9.     LPICONDIR IconDir;
  10.     LPICONDIRENTRY IconEntry;
  11.     LPICONIMAGE  resIconImage;
  12.     LPGRPICONDIR grpIconDir;
  13.     HRSRC hRes;        // handle para res. info. del ejecutable
  14.     int IconsCount = 0;
  15.     int AllSize = sizeof(ICONDIR)-sizeof(ICONDIRENTRY);
  16.     DWORD ImageOffset;
  17.  
  18.     // Cuento cuantos iconos tiene el recurso y la memoria necesaria
  19.     hRes = FindResource(hModule, Id, RT_GROUP_ICON);
  20.     if(hRes){
  21.       grpIconDir = (LPGRPICONDIR)LoadResource(hModule, hRes);
  22.       IconsCount = grpIconDir->idCount;
  23.       for(int n=0; n<IconsCount; n++)
  24.         AllSize += sizeof(ICONDIRENTRY) + grpIconDir->idEntries[n].dwBytesInRes;
  25.     }
  26.  
  27.     if(IconsCount==0){
  28.       *Size = 0;
  29.       return 0;
  30.     }
  31.     if(Buffer == 0 ){
  32.       *Size = AllSize;
  33.       return 1;
  34.     }
  35.     if(*Size < AllSize) return 0;
  36.  
  37.     // Preparo la Cabecera General grpIconDir
  38.     setmem(Buffer, 0, AllSize);
  39.     IconDir  = (LPICONDIR)Buffer;
  40.     IconDir->idReserved = 0;
  41.     IconDir->idType = 1;
  42.     IconDir->idCount = IconsCount;
  43.     // IconEntry apunta a la entrada del primer icono IconDir->idEntries[0]
  44. //    IconEntry = LPICONDIRENTRY(Buffer + sizeof(ICONDIR) - sizeof(ICONDIRENTRY));
  45.     IconEntry = &IconDir->idEntries[0];
  46.  
  47.     // Localizar el ICON resource en el ejecutable y sus Imagenes.
  48.     hRes = NULL;
  49.     ImageOffset = sizeof(ICONDIR) + (sizeof(ICONDIRENTRY)*(IconDir->idCount-1));
  50.     // Recorro las imágenes del recurso y preparo las entradas y las imágenes en el Buffer
  51.     for(int n=0; n<IconsCount; n++){
  52.       int nID = grpIconDir->idEntries[n].nID;
  53.       hRes = ::FindResource(hModule, MAKEINTRESOURCE(nID), RT_ICON);
  54.       resIconImage = (ICONIMAGE*)LoadResource(hModule, hRes);
  55.       IconEntry[n].bWidth = resIconImage->icHeader.biWidth;
  56.       IconEntry[n].bHeight = resIconImage->icHeader.biHeight/2;
  57.       IconEntry[n].bColorCount =  NColors(resIconImage->icHeader.biBitCount);
  58.       IconEntry[n].bReserved = 0;
  59.       IconEntry[n].wPlanes = 1;
  60.       IconEntry[n].wBitCount = resIconImage->icHeader.biBitCount;
  61.       IconEntry[n].dwBytesInRes = SizeofResource(hModule, hRes);
  62.       IconEntry[n].dwImageOffset = ImageOffset;
  63.       // Copio la imagen
  64.       memcpy((BYTE*)Buffer+ImageOffset, resIconImage, IconEntry[n].dwBytesInRes);
  65.       ImageOffset += IconEntry[n].dwBytesInRes;
  66.     }
  67.  
  68.     return AllSize;
  69. }



Como puede observarse, al igual que la función ExtractResIconFromModule, se recorren todas las imágenes del grupo. El código puede ser base para otro que obtenga uno o varios iconos en particular, según determinado criterio, y proporcionar un buffer sólo con esas imágenes. Para eso será necesario calcular previamente el tamaño del buffer y luego rellenarlo cuidadosamente.

Para ilustrar el uso de esta función propongo un código que guarda el grupo de iconos en un archivo de iconos.ico, SaveResIconToFile:



cpp
  1. //------------------------------------------------------------------------------
  2. // Guarda en disco File.ico el grupo de Icono de un exe o dll
  3. // Id: Indice del Grupo de iconos como si fuese una array comenzando por 0
  4. bool SaveResIconToFile(char *FuenteExe, char* DestFileName, int Id)
  5. {
  6.   BYTE* Buffer;
  7.   int Size;
  8.   HMODULE hModule;
  9.   hModule = LoadLibrary(FuenteExe);
  10.  
  11.   // Calculo el tamaño necesario para el Buffer
  12.   GetFileMemIconFromModule(hModule, Id, 0, &Size);
  13.   Buffer = new BYTE[Size];
  14.  
  15.   // Extraigo del ejecutable (hModule) el grupo de iconos que obtengo en el Buffer
  16.   // Este Buffer es una imagen en memoria de un archivo.ico que se puede guardar directamente.
  17.   int S = GetFileMemIconFromModule(hModule, Id, Buffer, &Size);
  18.   if(S==0) return false;
  19.  
  20.   // Creo y guardo el archivo de iconos
  21.   HANDLE hFile = CreateFile(DestFileName, GENERIC_WRITE, 0, NULL,
  22.                   CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  23.  
  24.   unsigned long dwBytesWritten;
  25.  
  26.   if (INVALID_HANDLE_VALUE != hFile){
  27.       WriteFile(hFile, Buffer, Size, &dwBytesWritten, NULL);
  28.       CloseHandle(hFile);
  29.   }
  30.  
  31.   FreeLibrary(hModule);
  32.   delete [] Buffer;
  33.   return (dwBytesWritten == Size);
  34. }



Como comentaba mas arriba, pueden crearse mas funciones para escojer iconos concretos y to todo el grupo, las bases están puestas en esta serie de post.

Saludos.

  • 0

#5 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 11 December 2009 - 05:16 PM

Ahora vamos a cambiar el icono de un ejecutable, para ello escribimos la función CambiaIcono, que se encargará de cambiar el Icono de un ejecutable tomándolo de un archivo.ico


cpp
  1. bool CambiaIcono(char *FuenteICO, char *DestinoExe, char* ResName, bool BorraIconResPrevio=false)
  2. {
  3.   HANDLE hFile;
  4.   LPBYTE lpBuffer;
  5.   LPBYTE lpResLock;
  6.   bool Result = true;
  7.  
  8.   if(!FuenteICO || !*FuenteICO) return false;
  9.   if(!DestinoExe || !*DestinoExe) return false;
  10.   if(!ResName || !*ResName) return false;
  11.  
  12.   // Abrimos el icono.ico en modo de lectura binaria
  13.   DWORD dwFileSize, dwBytesRead;
  14.  
  15.   hFile = CreateFile(FuenteICO, GENERIC_READ, 0, NULL, OPEN_EXISTING,
  16.                   FILE_ATTRIBUTE_NORMAL, NULL);
  17.  
  18.   if (INVALID_HANDLE_VALUE != hFile){
  19.       dwFileSize = GetFileSize(hFile, NULL);
  20.       lpBuffer = new BYTE[dwFileSize];
  21.       ReadFile(hFile, lpBuffer, dwFileSize, &dwBytesRead, NULL);
  22.       CloseHandle(hFile);
  23.   }
  24.  
  25.   // Convierto el Buffer a formato Icono con cabeceras...
  26.   ICONDIR *IconDir = (ICONDIR*)lpBuffer;
  27.   // Crea espacio para las cabeceras del icono recurso
  28.   int HeaderSize = sizeof(GRPICONDIR)+(sizeof(GRPICONDIRENTRY)*(IconDir->idCount-1));
  29.   BYTE *HeaderIconRec = new BYTE[HeaderSize];
  30.   GRPICONDIR *grpIconDir = (GRPICONDIR*)HeaderIconRec;
  31.   // Copio la cabecera común con IconDir
  32.   memcpy(grpIconDir, IconDir, sizeof(GRPICONDIR)-sizeof(GRPICONDIRENTRY));
  33.  
  34.   // Abrimos el recurso del exe
  35.   HANDLE hUpdateRes = BeginUpdateResource(DestinoExe, BorraIconResPrevio);
  36.   for(int n=0; n<grpIconDir->idCount; n++){
  37.     // Copio las cabeceras
  38.     memcpy(&grpIconDir->idEntries[n], &IconDir->idEntries[n], sizeof(GRPICONDIRENTRY));
  39.     grpIconDir->idEntries[n].nID = n + 100;
  40.     // Localizo la imagen del icono a pasar al .exe
  41.     BYTE *IconImage = (BYTE*)IconDir + IconDir->idEntries[n].dwImageOffset;
  42.     lpResLock = (BYTE*)LockResource(IconImage);
  43.     // Abrir el fichero donde añadir el icono.
  44.     if (hUpdateRes != NULL){
  45.       // Actualizar el resource destino
  46.       Result &= UpdateResource(hUpdateRes,
  47.         RT_ICON,
  48.         MAKEINTRESOURCE(grpIconDir->idEntries[n].nID),
  49.         MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
  50.         lpResLock,
  51.         IconDir->idEntries[n].dwBytesInRes);
  52.     }
  53.   }
  54.  
  55.   // Y la grabamos comp "indice" o cabecera de grupo de iconos
  56.   Result &= UpdateResource(hUpdateRes, RT_GROUP_ICON,
  57.         ResName,
  58.         MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
  59.         grpIconDir, HeaderSize);
  60.  
  61.   // Escribir los cambios y cerrar.
  62.   EndUpdateResource(hUpdateRes, FALSE);
  63.  
  64.   delete lpBuffer;
  65.   delete HeaderIconRec;
  66.  
  67.   return Result;
  68. }

El código añade un icono al ejecutable que queramos, o puede sustituir los iconos del mismo por el nuestro.

Otra forma de hacerlo puede ser mapeando en memoria un HICON utilizando las mismas estructuras y las API GetIconInfo y GetDIBits. Claro que si es engorroso utilizar GetDIBits, siempre se puede echar mano de las VCL:

cpp
  1. TIcon Icon = new TIcon;
  2. Icon->Handle = hIcon;    // el HICON a convertir
  3. TMemoryStream  *Stream = new TMemoryStream;
  4. Icon->SaveToStream(Stream);  // Stream->Memory sería el buffer a usar
  5. // Convierto el Buffer a formato Icono con cabeceras...
  6. ICONDIR *IconDir = (ICONDIR*)Stream->Memory;

La desventaja de este cómodo método es que sólo es satisfactorio para iconos de 16 colores, si tiene mas se reducen, al menos en BCB 5 y 6. Me imagino que en delphi 5 y 6 pasará lo mismo. Creo que las versiones de 2008 no tienen este problema.

Saludos.
  • 0

#6 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 11 December 2009 - 05:22 PM

En el post anterior comentaba la forma de conseguir incluir en un ejecutable o dll un nuevo icono como un recurso. Comentaba también ciertas limitaciones del Builder y el delphi, en ediciones algo antiguas, para el manejo de los colores de un icono pasado por su Handle HICON. Hice referencia a la posibilidad de mapear el HICON en memoria.

Aquí dejo un ejemplo de como pasar un HICON a memoria, en este caso un bloque con el formato del archivo del icono.ico que puede ser volcado al disco como un .ico.

Nos sirve también para crear un recurso en un ejecutable o dll. Para esto tendremos que pasar, como en el post anterior, un puntero al ICONIMAGE para así poder crear el recurso.

El archivo de definiciones es el mismo que en el post anterior.



cpp
  1. # include "Iconos.h"
  2.  
  3. //---------------------------------------------------------------------------
  4. long
  5. NColors(WORD bitCount)
  6. {
  7.   if (bitCount == 1 || bitCount == 4 || bitCount == 8)
  8.     return 1 << bitCount;
  9.   else if (bitCount >= 24)
  10.     return 0;
  11.   return -1;
  12. }
  13.  
  14. //---------------------------------------------------------------------------
  15. // Devuelve una imagen de archivo.ico para poder guardar en disco....
  16. // Reserva la memoria necesaria devolviendo en Size el Tamaño
  17. // Requiere liberar luego la memoria con VirtualFree(Mem, 0, MEM_RELEASE);
  18. void* hIconToMem(HICON hIcon, int BitCountPerPixel, int* Size)
  19. {
  20.     // Localizo la información del icono y su tamaño en un fichero.ico
  21.     HDC hDC = ::GetDC(NULL);    // ScreenDC
  22.     ICONINFO IconInfo;
  23.     BITMAP bmpAND={0};
  24.     BITMAP bmpXOR={0};
  25.     bool I = GetIconInfo(hIcon, &IconInfo);
  26.     GetObject(IconInfo.hbmMask, sizeof(BITMAP), &bmpAND);
  27.     GetObject(IconInfo.hbmColor, sizeof(BITMAP), &bmpXOR);
  28.     if(BitCountPerPixel==0) BitCountPerPixel=bmpXOR.bmPlanes*bmpXOR.bmBitsPixel;
  29.    
  30.     int RawDataSizeAND=((((bmpAND.bmWidth*bmpAND.bmBitsPixel)+31) & ~31) >> 3)*bmpAND.bmHeight;
  31.     int RawDataSizeXOR=((((bmpXOR.bmWidth*BitCountPerPixel)+31) & ~31) >> 3)*bmpXOR.bmHeight;
  32.     int RawDataSize = RawDataSizeAND + RawDataSizeXOR;
  33.     int PalSize=(BitCountPerPixel>8 ? 0 :1 << BitCountPerPixel)<<2; // RGBQUAD 4
  34.     int AllSize= sizeof(ICONDIR)+sizeof(BITMAPINFOHEADER)+PalSize+RawDataSizeAND+RawDataSizeXOR;
  35.     int Width  = bmpAND.bmWidth;
  36.     int Height = bmpAND.bmHeight;
  37.  
  38.     // Reservo memoria para el fichero
  39.     BYTE* FileIconMem = (BYTE*)VirtualAlloc(0, AllSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  40.     ICONDIR* IconDir  = (ICONDIR*)FileIconMem;
  41.    
  42.     // Obtengo los Bits de cada parte Mask y Color
  43.     BITMAPINFO bmiAND;
  44.     BITMAPINFO *bmiICON = (BITMAPINFO*)(FileIconMem + sizeof(ICONDIR));
  45.     LPVOID lpBitsXOR =    (BITMAPINFO*)(FileIconMem + sizeof(ICONDIR) + sizeof(BITMAPINFOHEADER) + PalSize);
  46.     LPVOID lpBitsAND =    (BITMAPINFO*)((BYTE*)lpBitsXOR + RawDataSizeXOR);
  47.  
  48.     // Preparo la cabecera del Icon
  49.     IconDir->idReserved = 0;
  50.     IconDir->idType = 1;
  51.     IconDir->idCount = 1;
  52.     IconDir->idEntries[0].bWidth = Width;
  53.     IconDir->idEntries[0].bHeight = Height;
  54.     IconDir->idEntries[0].bColorCount = NColors(BitCountPerPixel);
  55.     IconDir->idEntries[0].bReserved = 0;
  56.     IconDir->idEntries[0].wPlanes = 0;        // Color Planes
  57.     IconDir->idEntries[0].wBitCount = 0;//BitCountPerPixel;      // Bits per pixel
  58.     IconDir->idEntries[0].dwBytesInRes = AllSize-sizeof(ICONDIR);    // How many bytes in this resource?
  59.     IconDir->idEntries[0].dwImageOffset = sizeof(ICONDIR);  // Where in the file is this image?
  60.     LPICONIMAGE IconImage = (LPICONIMAGE)(bmiICON);
  61.     memset(IconImage, 0, sizeof(BITMAPINFOHEADER));
  62.     IconImage->icHeader.biSize = sizeof(BITMAPINFOHEADER);
  63.     IconImage->icHeader.biWidth = Width;
  64.     IconImage->icHeader.biHeight = Height;
  65.     IconImage->icHeader.biPlanes = 1;
  66.     IconImage->icHeader.biBitCount = BitCountPerPixel;
  67.     IconImage->icHeader.biSizeImage = RawDataSize;
  68.  
  69.     // Preparo BITMAPINFOHEADER para la Mascara (bmiAND)
  70.     memcpy(&bmiAND, bmiICON, sizeof(BITMAPINFOHEADER));
  71.     bmiAND.bmiHeader.biSizeImage = RawDataSizeAND;
  72.     bmiAND.bmiHeader.biBitCount = 1;
  73.     bmiAND.bmiHeader.biClrUsed = 1;
  74.     bmiAND.bmiHeader.biClrImportant = 1;
  75.  
  76.     // Recupero los bits de cada hBitmap del icono
  77.     GetDIBits(hDC, IconInfo.hbmColor, 0, Height, lpBitsXOR, bmiICON, DIB_RGB_COLORS);
  78.     GetDIBits(hDC, IconInfo.hbmMask, 0, Height, lpBitsAND, &bmiAND, DIB_RGB_COLORS);
  79.  
  80.     // Sumo las dos alturas de ambos bitmaps del icono
  81.     IconImage->icHeader.biHeight = Height*2;
  82.  
  83.     ReleaseDC(0, hDC);
  84.  
  85.     // Salida
  86.     *Size = AllSize;
  87.     return FileIconMem;
  88. }



Para ilustrar el uso voy a colocar dos ejemplos prácticos. En el primero guardo un hIcon en un archivo y lo vuelvo a abrir para ver lo que sucede:



cpp
  1.   TIcon *Icon = new TIcon;
  2.   Icon->LoadFromFile("MI_ICONO.ICO");
  3.  
  4.   // Convertimos el HICON a imagen en memoria del .ico
  5.   int Size;
  6.   void* FileIconMem = hIconToMem(Icon->Handle, 32, &Size);
  7.  
  8.   HANDLE hFile=CreateFile("FilePath.ico", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  9.   DWORD dwWritten;
  10.   WriteFile(hFile, FileIconMem, Size, &dwWritten, 0);
  11.   CloseHandle(hFile);
  12.   VirtualFree(FileIconMem, 0, MEM_RELEASE);
  13.  
  14.   // Lo recuperamos para ver que ha pasado
  15.   Icon->LoadFromFile("FilePath.ico");
  16.   Image1->Picture->Icon->Assign(Icon);



Ahora vamos a pasar la Imagen de un hIcon a un TIcon. Para ello utilizamos un TMemoryStream como paso intermedio. Creo que su uso es bastante intuitivo:



cpp
  1. // Creo un MemoryStream
  2. TMemoryStream* Stream = new TMemoryStream;
  3. // Leo el mapa del hIcon deseado
  4. int Size;
  5. void* FileIconMem = hIconToMem(hIcon, 32, &Size);
  6. // lo paso al MemoryStream
  7. Stream->Write(FileIconMem, Size);
  8. Stream->Position = 0;
  9. // Libero el mapa
  10. VirtualFree(FileIconMem, 0, MEM_RELEASE);
  11. // Creo el icono con la imagen del hIcon
  12. TIcon *Icon = new TIcon;
  13. Stream->Position = 0;  // Para situar le puntero de lectura al principio del Stream
  14. Icon->LoadFromStream(Stream); // Cargo la imagen del Stream




Con esta serie intensiva dedicada a los iconos de la sección de recursos de los ejecutables, espero haber contribuido a esclarecer algunos asuntos un tanto oscuros de Windows, o al menos a aportar código que pudiera ser útil a alguien en un momento dado.

Saludos.

  • 0

#7 enecumene

enecumene

    Webmaster

  • Administrador
  • 7419 posts
  • LocationRepública Dominicana

Posted 11 December 2009 - 05:36 PM

wow, amigo te envío una reverencia, no mejor 2, no mejor 3...mmm...todas las que sea necesarias :p, excelente tuto amigo (y).
  • 0

#8 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2092 posts
  • LocationRepública Dominicana

Posted 11 December 2009 - 07:52 PM

Saludos.

Excelente tutorial, solo que el código esta en C/C++ pero la lógica esta bien. 

Gracias de todos modos por el aporte.
  • 0

#9 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 12 December 2009 - 02:24 PM

Muchas gracias a vosotros por la acogida tan generosa que brindáis a este tutorial.  :D

Me vais a permitir que añada algo mas:

La función


cpp
  1. void* hIconToMem(HICON hIcon, int BitCountPerPixel, int* Size);


Puede resultar muy útil para el manejo de iconos de mas de 16 colores cuando tratamos de asimilar a un TIcon el Handle extraido con las funciones dadas o con las APIs, como ExtractAssociatedIcon, que devuelven un manejador HICON. La función descrita reserva memoria con VirtualAlloc y requiere liberarla después de la llamada como podeis ver en los ejemplos publicados. Voy a proponer otra implementación con una técnica que se usa mucho en algunas API Win32. Se trata de realizar primero una consulta a la función, para saber el tamaño del buffer necesario para, una vez reservado, volverla a llamar de forma definitiva:



cpp
  1. //---------------------------------------------------------------------------
  2. // Devuelve en Buffer Una imagen de archivo.ico para poder guardar en disco.
  3. // Devuelve el tamaño de la memoria de archivo de un Icono
  4. // Retorna 0 si falla, 1 si Buffer==0 y AllSize si devuelbe el valor en Buffer.
  5. // Si Buffer = NULL, devuelve en Size el tamaño necesario para el buffer
  6. int hIconToMem(HICON hIcon, int BitCountPerPixel, void* Buffer, int* Size)
  7. {
  8.     // Localizo la información del icono y su tamaño en un fichero.ico
  9.     HDC hDC = ::GetDC(NULL);    // ScreenDC
  10.     ICONINFO IconInfo;
  11.     BITMAP bmpAND={0};
  12.     BITMAP bmpXOR={0};
  13.     BYTE* FileIconMem = (BYTE*)Buffer;
  14.     bool I = GetIconInfo(hIcon, &IconInfo);
  15.     GetObject(IconInfo.hbmMask, sizeof(BITMAP), &bmpAND);
  16.     GetObject(IconInfo.hbmColor, sizeof(BITMAP), &bmpXOR);
  17.     if(BitCountPerPixel==0) BitCountPerPixel=bmpXOR.bmPlanes*bmpXOR.bmBitsPixel;
  18.    
  19.     int RawDataSizeAND=((((bmpAND.bmWidth*bmpAND.bmBitsPixel)+31) & ~31) >> 3)*bmpAND.bmHeight;
  20.     int RawDataSizeXOR=((((bmpXOR.bmWidth*BitCountPerPixel)+31) & ~31) >> 3)*bmpXOR.bmHeight;
  21.     int RawDataSize = RawDataSizeAND + RawDataSizeXOR;
  22.     int PalSize=(BitCountPerPixel>8 ? 0 :1 << BitCountPerPixel)<<2; // RGBQUAD 4
  23.     int AllSize= sizeof(ICONDIR)+sizeof(BITMAPINFOHEADER)+PalSize+RawDataSizeAND+RawDataSizeXOR;
  24.     int Width  = bmpAND.bmWidth;
  25.     int Height = bmpAND.bmHeight;
  26.  
  27.     // Si Buffer es nulo, termino retornando el tamaño necesario del buffer en Bytes
  28.     if(Buffer == 0){
  29.       *Size = AllSize;
  30.       return 1;
  31.     }
  32.     if(*Size < AllSize) return 0;
  33.  
  34.     // Convierto Buffer a ICONDIR
  35.     ICONDIR* IconDir  = (ICONDIR*)Buffer;
  36.  
  37.     // Obtengo los Bits de cada parte Mask y Color
  38.     BITMAPINFO bmiAND;
  39.     BITMAPINFO *bmiICON = (BITMAPINFO*)(FileIconMem + sizeof(ICONDIR));
  40.     LPVOID lpBitsXOR =    (BITMAPINFO*)(FileIconMem + sizeof(ICONDIR) + sizeof(BITMAPINFOHEADER) + PalSize);
  41.     LPVOID lpBitsAND =    (BITMAPINFO*)((BYTE*)lpBitsXOR + RawDataSizeXOR);
  42.  
  43.     // Preparo la cabecera del Icon
  44.     IconDir->idReserved = 0;
  45.     IconDir->idType = 1;
  46.     IconDir->idCount = 1;
  47.     IconDir->idEntries[0].bWidth = Width;
  48.     IconDir->idEntries[0].bHeight = Height;
  49.     IconDir->idEntries[0].bColorCount = NColors(BitCountPerPixel);
  50.     IconDir->idEntries[0].bReserved = 0;
  51.     IconDir->idEntries[0].wPlanes = 0;        // Color Planes
  52.     IconDir->idEntries[0].wBitCount = 0;//BitCountPerPixel;      // Bits per pixel
  53.     IconDir->idEntries[0].dwBytesInRes = AllSize-sizeof(ICONDIR);    // How many bytes in this resource?
  54.     IconDir->idEntries[0].dwImageOffset = sizeof(ICONDIR);  // Where in the file is this image?
  55.     LPICONIMAGE IconImage = (LPICONIMAGE)(bmiICON);
  56.     memset(IconImage, 0, sizeof(BITMAPINFOHEADER));
  57.     IconImage->icHeader.biSize = sizeof(BITMAPINFOHEADER);
  58.     IconImage->icHeader.biWidth = Width;
  59.     IconImage->icHeader.biHeight = Height;
  60.     IconImage->icHeader.biPlanes = 1;
  61.     IconImage->icHeader.biBitCount = BitCountPerPixel;
  62.     IconImage->icHeader.biSizeImage = RawDataSize;
  63.  
  64.     // Preparo BITMAPINFOHEADER para la Mascara (bmiAND)
  65.     memcpy(&bmiAND, bmiICON, sizeof(BITMAPINFOHEADER));
  66.     bmiAND.bmiHeader.biSizeImage = RawDataSizeAND;
  67.     bmiAND.bmiHeader.biBitCount = 1;
  68.     bmiAND.bmiHeader.biClrUsed = 1;
  69.     bmiAND.bmiHeader.biClrImportant = 1;
  70.  
  71.     // Recupero los bits de cada hBitmap del icono
  72.     GetDIBits(hDC, IconInfo.hbmColor, 0, Height, lpBitsXOR, bmiICON, DIB_RGB_COLORS);
  73.     GetDIBits(hDC, IconInfo.hbmMask, 0, Height, lpBitsAND, &bmiAND, DIB_RGB_COLORS);
  74.  
  75.     // Sumo las dos alturas de ambos bitmaps del icono
  76.     IconImage->icHeader.biHeight = Height*2;
  77.  
  78.     ReleaseDC(0, hDC);
  79.  
  80.     // Salida
  81.     return AllSize;;
  82. }



Ilustro la función creando un TIcon a partir de un Handle:



cpp
  1. // Creo un MemoryStream
  2. TMemoryStream* Stream = new TMemoryStream;
  3. // Leo el mapa del hIcon deseado
  4. void* FileIconMem;
  5. int Size;
  6.  
  7. if(hIconToMem(hIcon, 32, 0, &Size)){
  8.   // Reservo un buffer.
  9.   FileIconMem = (void*)new char[Size];
  10. }else return;
  11.  
  12. hIconToMem(hIcon, 32, FileIconMem, &Size);
  13.  
  14. // lo paso al MemoryStream
  15. Stream->Write(FileIconMem, Size);
  16. Stream->Position = 0;
  17. // Libero el mapa
  18. delete [] FileIconMem;
  19. // Creo el icono con la imagen del hIcon
  20. TIcon *Icon = new TIcon;
  21. Stream->Position = 0;  // Para situar le puntero de lectura al principio del Stream
  22. Icon->LoadFromStream(Stream); // Cargo la imagen del Stream en el TIcon



Cada desarrollador encontrará uno u otro sistema mas cómodo o mas adecuado. Las ideas están sobre la mesa. Gracias por vuestro interés.

Saludos.
  • 0

#10 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 12 December 2009 - 08:26 PM

¡Excelente material! (y)
Me quedo chiquito de tan semejante muestra de conocimiento.

Muchas gracias escafandra por compartirlo.

Saludos,
  • 0

#11 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 04 January 2010 - 05:14 AM

Me he dado cuenta de cierta demanda para poder escribir un archivo.ico a partir de un HICON. Esa misma demanda se refiere a convertir un HICON en un  TIcon. Claro que lo mas fácil es hacer TIcon.Handle:= hIcon, pero como ya expliqué esto sólo funciona correctamente en iconos de 16 colores o menos.  Muy posiblemente el error de colores esté corregido en las nuevas versiones de delphi, pero no en la 7 que está muy extendida. Parece ser que este tema no está resuelto para muchos usuarios tanto de delphi como de otros lenguajes. El tutorial presentado en este hilo está escrito en C/C++ pero es perfectamente trasladable a delphi. El presente mensaje viene a mostrar, con código y ejemplos, como resolver el problema en delphi.

Primeramente debemos definir las estructuras necesarias para trabajar con iconos a bajo nivel desde la API:



delphi
  1. // These next two structs represent how the icon information is stored
  2. // in an ICO file.
  3. {$ALIGN 1}
  4. //{$ALIGN ON}
  5. type TICONDIRENTRY = record
  6.   bWidth: BYTE;              // Width of the image
  7.   bHeight: BYTE;              // Height of the image (times 2)
  8.   bColorCount: BYTE;          // Number of colors in image (0 if >=8bpp)
  9.   bReserved: BYTE;            // Reserved
  10.   wPlanes: WORD;              // Color Planes
  11.   wBitCount: WORD;            // Bits per pixel
  12.   dwBytesInRes: DWORD;        // how many bytes in this resource?
  13.   dwImageOffset: DWORD;      // where in the file is this image
  14. end; PICONDIRENTRY = ^TICONDIRENTRY;
  15.  
  16. type TICONDIR = record
  17.   idReserved: WORD; // Reserved
  18.   idType: WORD;    // resource type (1 for icons)
  19.   idCount: WORD;    // how many images?
  20.   idEntries: array[0..0] of TICONDIRENTRY; // the entries for each image
  21. end; PICONDIR = ^TICONDIR;
  22.  
  23. type tagICONIMAGE= record
  24.   icHeader: BITMAPINFOHEADER;      // DIB header
  25.   icColors: array[0..0] of RGBQUAD; // Color table
  26.   icXORarray: array[0..0] of BYTE;        // DIB bits for XOR mask
  27.   icANDarray: array[0..0] of BYTE;        // DIB bits for AND mask
  28. end; PICONIMAGE = ^tagICONIMAGE;
  29. {$ALIGN OFF}



Seguidamente vamos a implementar la función que permite pasar de un HICON a una imagen en memoria del correspondiente archivo.ico. Como hice en  C/C++, voy a proponer dos funciones casi idénticas. La primera devuelve un puntero a la imagen solicitada y el programador será responsable de liberar  dicha memoria. La segunda implementación permite encontrar el tamaño necesitado para que el programador localice él mismo la memoria necesaria. Con  una segunda llamada recibirá la imagen deseada.

Ambas funciones reciben como parámetros principales el Handle del Icono y los Bits por pixel de la imagen, que se relaciona directamente con el número de colores con los que queremos representar la imagen. Windows utilizará en el HICON tantos colores como sea capaz e representar en el modo gráfico con el que estemos trabajando, normalmente un valor de BitCountPerPixel de 32. Nosotros podremos reducir el valor si así lo consideramos necesario.

La primera función auxiliar, NColors, necesaria se dedica a conseguir el número de colores según el valor de BitCountPerPixel. Observar que para valores de 24 y 32 el resultado es cero:


delphi
  1. //---------------------------------------------------------------------------
  2. // Convierte bitCount a número de colores
  3. function NColors(bitCount: WORD): integer;
  4. begin
  5.   Result:= -1;
  6.   if (bitCount = 1) or (bitCount = 4) or (bitCount = 8) then
  7.     Result:= 1 shl bitCount
  8.   else if (bitCount >= 24) then
  9.     Result:= 0;
  10. end;



Ahora la función hIconToMem que se encargará de conseguir una imagen en memoria de un fichero.ico a par´ir de un Handle HICON. El código está lo suficientemente comentado:



delphi
  1. //---------------------------------------------------------------------------
  2. // Devuelve un puntero con la imagen de archivo.ico para poder guardar en disco.
  3. // Devuelve el tamaño de la memoria de archivo de un Icono en Size
  4. // Es necesario liberar el puntero devuelto tras usarlo: usar VirtualFree
  5. function hIconToMem(hIcon: Thandle; BitCountPerPixel: integer; var Size: integer): Pointer;
  6. var
  7.   hDC: Thandle;
  8.   IconInfo: TICONINFO;
  9.   bmpAND, bmpXOR: BITMAP;
  10.   FileIconMem: Pointer;
  11.   RawDataSizeAND, RawDataSizeXOR, RawDataSize, PalSize, AllSize, Width, Height: integer;
  12.   IconDir: PICONDIR;
  13.   bmiAND: BITMAPINFO;
  14.   bmiICON: PBITMAPINFO;
  15.   lpBitsXOR, lpBitsAND: Pointer;
  16.   IconImage: PICONIMAGE;
  17.  
  18. begin
  19.   // Localizo la información del icono y su tamaño en un fichero.ico
  20.   hDC:= GetDC(0);    // ScreenDC
  21.   ZeroMemory(@bmpAND, sizeof(BITMAP));
  22.   ZeroMemory(@bmpXOR, sizeof(BITMAP));
  23.   GetIconInfo(hIcon, IconInfo);
  24.   GetObject(IconInfo.hbmMask, sizeof(BITMAP), @bmpAND);
  25.   GetObject(IconInfo.hbmColor, sizeof(BITMAP), @bmpXOR);
  26.   if(BitCountPerPixel=0) then BitCountPerPixel:= bmpXOR.bmPlanes*bmpXOR.bmBitsPixel;
  27.   RawDataSizeAND:= ((((bmpAND.bmWidth*bmpAND.bmBitsPixel)+31) and not 31) shr 3)*bmpAND.bmHeight;
  28.   RawDataSizeXOR:= ((((bmpXOR.bmWidth*BitCountPerPixel)+31) and not 31) shr 3)*bmpXOR.bmHeight;
  29.   RawDataSize:= RawDataSizeAND + RawDataSizeXOR;
  30.   PalSize:=0; if(BitCountPerPixel<=8) then PalSize:= (1 shl BitCountPerPixel) shl 2; // RGBQUAD 4
  31.   AllSize:= sizeof(TICONDIR)+sizeof(BITMAPINFOHEADER)+PalSize+RawDataSizeAND+RawDataSizeXOR;
  32.   Width  := bmpAND.bmWidth;
  33.   Height := bmpAND.bmHeight;
  34.  
  35.   // Reservo memoria para el fichero
  36.   FileIconMem:= VirtualAlloc(nil, AllSize, MEM_COMMIT, PAGE_READWRITE);
  37.   IconDir:=    PICONDIR(FileIconMem);
  38.  
  39.   // Obtengo los Bits de cada parte Mask y Color
  40.   bmiICON:=  PBITMAPINFO(DWORD(FileIconMem) + sizeof(TICONDIR));
  41.   lpBitsXOR:= PBITMAPINFO(DWORD(FileIconMem) + sizeof(TICONDIR) + sizeof(BITMAPINFOHEADER) + PalSize);
  42.   lpBitsAND:= PBITMAPINFO(DWORD(lpBitsXOR) + RawDataSizeXOR);
  43.  
  44.   // Preparo la cabecera del Icon
  45.   IconDir.idReserved:= 0;
  46.   IconDir.idType:= 1;
  47.   IconDir.idCount:= 1;
  48.   IconDir.idEntries[0].bWidth:= Width;
  49.   IconDir.idEntries[0].bHeight:= Height;
  50.   IconDir.idEntries[0].bColorCount:= NColors(BitCountPerPixel);
  51.   IconDir.idEntries[0].bReserved:= 0;
  52.   IconDir.idEntries[0].wPlanes:= 0;        // Color Planes
  53.   IconDir.idEntries[0].wBitCount:= 0;//BitCountPerPixel;      // Bits per pixel
  54.   IconDir.idEntries[0].dwBytesInRes:= AllSize-sizeof(TICONDIR);    // How many bytes in this resource?
  55.   IconDir.idEntries[0].dwImageOffset:= sizeof(TICONDIR);  // Where in the file is this image?
  56.   IconImage:= PICONIMAGE(bmiICON);
  57.   ZeroMemory(IconImage, sizeof(BITMAPINFOHEADER));
  58.   IconImage.icHeader.biSize:= sizeof(BITMAPINFOHEADER);
  59.   IconImage.icHeader.biWidth:= Width;
  60.   IconImage.icHeader.biHeight:= Height;
  61.   IconImage.icHeader.biPlanes:= 1;
  62.   IconImage.icHeader.biBitCount:= BitCountPerPixel;
  63.   IconImage.icHeader.biSizeImage:= RawDataSize;
  64.  
  65.   // Preparo BITMAPINFOHEADER para la Mascara (bmiAND)
  66.   CopyMemory(@bmiAND, bmiICON, sizeof(BITMAPINFOHEADER));
  67.   bmiAND.bmiHeader.biSizeImage:= RawDataSizeAND;
  68.   bmiAND.bmiHeader.biBitCount:= 1;
  69.   bmiAND.bmiHeader.biClrUsed:= 1;
  70.   bmiAND.bmiHeader.biClrImportant:= 1;
  71.  
  72.   // Recupero los bits de cada hBitmap del icono
  73.   GetDIBits(hDC, IconInfo.hbmColor, 0, Height, lpBitsXOR, bmiICON^, DIB_RGB_COLORS);
  74.   GetDIBits(hDC, IconInfo.hbmMask, 0, Height, lpBitsAND, bmiAND, DIB_RGB_COLORS);
  75.  
  76.   // Sumo las dos alturas de ambos bitmaps del icono
  77.   IconImage.icHeader.biHeight:= Height*2;
  78.  
  79.   // Salida
  80.   Size:= AllSize;
  81.   Result:= FileIconMem;
  82. end;



Seguidamente muestro una función similar, la diferencia está en que será en desarrollador el responsable de localizar y liberar la memoria necesaria, teniendo libertad en el modo de hacerlo. Para elle necesitaremos saber el tamaño necesario. Nuestra nueva función tiene dos usos, el primero informar del tamaño necesario para el buffer. Esto se consigue pasando un nulo como valor del buffer. El segundo uso es el verdadero propósito de la función, es decir llenar el bufer con la imagen en memoria del archivo.ico deseado.



delphi
  1. //---------------------------------------------------------------------------
  2. // Devuelve en Buffer Una imagen de archivo.ico para poder guardar en disco.
  3. // Devuelve el tamaño de la memoria de archivo de un Icono.
  4. // Retorna 0 si falla, 1 si Buffer==0 y AllSize si devuelbe el valor en Buffer.
  5. // Si Buffer = NULL, devuelve en Size el tamaño necesario para el buffer
  6. function _hIconToMem(hIcon: Thandle; BitCountPerPixel: integer; Buffer: pointer; var Size: integer): integer;
  7. var
  8.   hDC: Thandle;
  9.   IconInfo: TICONINFO;
  10.   bmpAND, bmpXOR: BITMAP;
  11.   FileIconMem: PBYTE;
  12.   RawDataSizeAND, RawDataSizeXOR, RawDataSize, PalSize, AllSize, Width, Height: integer;
  13.   IconDir: PICONDIR;
  14.   bmiAND: BITMAPINFO;
  15.   bmiICON: PBITMAPINFO;
  16.   lpBitsXOR, lpBitsAND: Pointer;
  17.   IconImage: PICONIMAGE;
  18.  
  19. begin
  20.   // Localizo la información del icono y su tamaño en un fichero.ico
  21.   hDC:= GetDC(0);    // ScreenDC
  22.   ZeroMemory(@bmpAND, sizeof(BITMAP));
  23.   ZeroMemory(@bmpXOR, sizeof(BITMAP));
  24.   FileIconMem:= PBYTE(Buffer);
  25.   GetIconInfo(hIcon, IconInfo);
  26.   GetObject(IconInfo.hbmMask, sizeof(BITMAP), @bmpAND);
  27.   GetObject(IconInfo.hbmColor, sizeof(BITMAP), @bmpXOR);
  28.   if(BitCountPerPixel=0) then BitCountPerPixel:= bmpXOR.bmPlanes*bmpXOR.bmBitsPixel;
  29.   RawDataSizeAND:= ((((bmpAND.bmWidth*bmpAND.bmBitsPixel)+31) and not 31) shr 3)*bmpAND.bmHeight;
  30.   RawDataSizeXOR:= ((((bmpXOR.bmWidth*BitCountPerPixel)+31) and not 31) shr 3)*bmpXOR.bmHeight;
  31.   RawDataSize:= RawDataSizeAND + RawDataSizeXOR;
  32.   PalSize:=0; if(BitCountPerPixel<=8) then PalSize:= (1 shl BitCountPerPixel) shl 2; // RGBQUAD 4
  33.   AllSize:= sizeof(TICONDIR)+sizeof(BITMAPINFOHEADER)+PalSize+RawDataSizeAND+RawDataSizeXOR;
  34.   Width  := bmpAND.bmWidth;
  35.   Height := bmpAND.bmHeight;
  36.  
  37.   // Si Buffer es nulo, termino retornando el tamaño necesario del buffer en Bytes
  38.   // en caso contrario ejecuto...
  39.   if(Buffer <> nil) then
  40.   begin
  41.     Result:= 0;
  42.     if Size >= AllSize then
  43.     begin
  44.         // Convierto Buffer a ICONDIR
  45.         IconDir:= PICONDIR(Buffer);
  46.  
  47.         // Obtengo los Bits de cada parte Mask y Color
  48.         bmiICON:=  PBITMAPINFO(DWORD(FileIconMem) + sizeof(TICONDIR));
  49.         lpBitsXOR:= PBITMAPINFO(DWORD(FileIconMem) + sizeof(TICONDIR) + sizeof(BITMAPINFOHEADER) + PalSize);
  50.         lpBitsAND:= PBITMAPINFO(DWORD(lpBitsXOR) + RawDataSizeXOR);
  51.  
  52.         // Preparo la cabecera del Icon
  53.         IconDir.idReserved:= 0;
  54.         IconDir.idType:= 1;
  55.         IconDir.idCount:= 1;
  56.         IconDir.idEntries[0].bWidth:= Width;
  57.         IconDir.idEntries[0].bHeight:= Height;
  58.         IconDir.idEntries[0].bColorCount:= NColors(BitCountPerPixel);
  59.         IconDir.idEntries[0].bReserved:= 0;
  60.         IconDir.idEntries[0].wPlanes:= 0;        // Color Planes
  61.         IconDir.idEntries[0].wBitCount:= 0;//BitCountPerPixel;      // Bits per pixel
  62.         IconDir.idEntries[0].dwBytesInRes:= AllSize-sizeof(TICONDIR);    // How many bytes in this resource?
  63.         IconDir.idEntries[0].dwImageOffset:= sizeof(TICONDIR);  // Where in the file is this image?
  64.         IconImage:= PICONIMAGE(bmiICON);
  65.         ZeroMemory(IconImage, sizeof(BITMAPINFOHEADER));
  66.         IconImage.icHeader.biSize:= sizeof(BITMAPINFOHEADER);
  67.         IconImage.icHeader.biWidth:= Width;
  68.         IconImage.icHeader.biHeight:= Height;
  69.         IconImage.icHeader.biPlanes:= 1;
  70.         IconImage.icHeader.biBitCount:= BitCountPerPixel;
  71.         IconImage.icHeader.biSizeImage:= RawDataSize;
  72.  
  73.         // Preparo BITMAPINFOHEADER para la Mascara (bmiAND)
  74.         CopyMemory(@bmiAND, bmiICON, sizeof(BITMAPINFOHEADER));
  75.         bmiAND.bmiHeader.biSizeImage:= RawDataSizeAND;
  76.         bmiAND.bmiHeader.biBitCount:= 1;
  77.         bmiAND.bmiHeader.biClrUsed:= 1;
  78.         bmiAND.bmiHeader.biClrImportant:= 1;
  79.  
  80.         // Recupero los bits de cada hBitmap del icono
  81.         GetDIBits(hDC, IconInfo.hbmColor, 0, Height, lpBitsXOR, bmiICON^, DIB_RGB_COLORS);
  82.         GetDIBits(hDC, IconInfo.hbmMask, 0, Height, lpBitsAND, bmiAND, DIB_RGB_COLORS);
  83.  
  84.         // Sumo las dos alturas de ambos bitmaps del icono
  85.         IconImage.icHeader.biHeight:= Height*2;
  86.  
  87.         // Salida
  88.         Result:= AllSize;
  89.     end;
  90.   end else
  91.   begin
  92.     Size:= AllSize;
  93.     Result:= 1;
  94.   end;
  95.  
  96.   ReleaseDC(0, hDC);
  97. end;



Por no extenderme demasiado termino aquí y dejo para el siguiente mensaje los sencillos ejemplos de uso de estas funciones.

Saludos.
  • 0

#12 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 04 January 2010 - 05:20 AM

Bien, toca ahora mostrar el uso de las funciones anteriores. Voy a dar dos ejemplos básicos. El primero muestra como guardar un archivo de icono a partir de un Handle, mientras que el segundo lo asigna a un objeto TIcon con el que podremos trabajar tranquilamente en delphi:



delphi
  1. function HIconToFile(hIcon: THandle; FileName: String): Boolean;
  2. var
  3.   FileIconMem: Pointer;
  4.   Size: integer;
  5.   hFile: Cardinal;
  6. begin
  7.   Result:= false;
  8.   FileIconMem:= hIconToMem(hIcon, 32, Size);
  9.   hFile := CreateFile(PCHAR(FileName), GENERIC_WRITE, 0, nil, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0);
  10.   if(_lwrite(hFile, FileIconMem, Size) = Size) then
  11.       Result:= true;
  12.   CloseHandle(hFile);
  13.   VirtualFree(FileIconMem, 0, MEM_RELEASE);
  14. end;



Y su homóloga:


delphi
  1. function HIconToFile2(hIcon: THandle; FileName: String): Boolean;
  2. var
  3.   FileIconMem: Pointer;
  4.   Size: integer;
  5.   hFile: Cardinal;
  6. begin
  7.   Result:= false;
  8.   if _hIconToMem(hIcon, 32, nil, Size)<>0 then
  9.   begin
  10.     // Reservo un buffer.
  11.     FileIconMem:= AllocMem(Size);
  12.     _hIconToMem(hIcon, 32, FileIconMem, Size);
  13.     hFile := CreateFile(PCHAR(FileName), GENERIC_WRITE, 0, nil, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0);
  14.     if(_lwrite(hFile, FileIconMem, Size) = Size) then
  15.       Result:= true;
  16.     CloseHandle(hFile);
  17.   end;
  18. end;





delphi
  1. procedure HIconToTicon(Hicon: THandle; Icon: TIcon);
  2. var
  3.   Stream: TMemoryStream;
  4.   FileIconMem: Pointer;
  5.   Size: integer;
  6. begin
  7.   // Creo un MemoryStream
  8.   Stream:= TMemoryStream.Create;
  9.   // Leo el mapa del hIcon deseado
  10.   FileIconMem:= hIconToMem(hIcon, 32, Size);
  11.  
  12.   // Lo paso al MemoryStream
  13.   Stream.WriteBuffer(FileIconMem^, Size);
  14.   // Libero el mapa
  15.   VirtualFree(FileIconMem, 0, MEM_RELEASE);
  16.   Stream.Position:= 0;  // Para situar le puntero de lectura al principio del Stream
  17.   Icon.LoadFromStream(Stream); // Cargo la imagen del Stream en el TIcon
  18.   Stream.Free;  // Libero el Stream
  19. end;



Y su homóloga:


delphi
  1. procedure HIconToTIcon2(Hicon: THandle; Icon: TIcon);
  2. var
  3.   Stream: TMemoryStream;
  4.   FileIconMem: Pointer;
  5.   Size: integer;
  6. begin
  7.   // Creo un MemoryStream
  8.   Stream:= TMemoryStream.Create;
  9.   // Leo el mapa del hIcon deseado
  10.   if _hIconToMem(hIcon, 32, nil, Size)<>0 then
  11.   begin
  12.     // Reservo un buffer.
  13.     FileIconMem:= AllocMem(Size);
  14.  
  15.     _hIconToMem(hIcon, 32, FileIconMem, Size);
  16.     // lo paso al MemoryStream
  17.     Stream.WriteBuffer(FileIconMem^, Size);
  18.     // Libero el mapa
  19.     FreeMem(FileIconMem);
  20.  
  21.     // Creo el icono con la imagen del hIcon
  22.     Stream.Position:= 0;  // Para situar le puntero de lectura al principio del Stream
  23.     Icon.LoadFromStream(Stream); // Cargo la imagen del Stream en el TIcon
  24.     Stream.Free;  // Libero el Stream
  25.   end;
  26. end;



Todo el código lo recojo en una unit que cuelgo del primer mensaje del hilo.

Espero haber sido de utilidad.

Saludos.
  • 0

#13 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 04 January 2010 - 09:45 AM

Muchas gracias por el tutorial amigo escafandra, muy interesante y muy ilustrativo.

Salud OS
  • 0




IP.Board spam blocked by CleanTalk.