Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Otros entornos y lenguajes > C++ Builder
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 16-11-2008
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Crear resource icon por código

Trato de crear por código un resource que contenga un icono, o modificar el de un ejecutable o dll para añadir un icono.

Las API:
FindResource
LoadResource
LockResource
BeginUpdateResource
UpdateResource
EndUpdateResource

Me permiten leer un resource de un archivo (.exe o dll) y modificarlo para luego escribir en disco el archivo modificado.

He conseguido esto, es decir, leer un resource de un archivo (.exe o dll), y añadírle el icodo del mismo a otro archivo. Lo que no consigo es añadir un icono extraido de un icono.ico, el HRSRC no se corresponde con el Handle de un icono. He escrito el mismo en un archivo y me he dado cuenta de que es "casi el icono", me falta conocer algo que podría ser la cabecera del .ico.

Lo que quiero, si no me explico bien es convertir un archivo.ico a una imagen en memoria del tipo HRSRC para incluirlo como un recurso en un ejecutable o dll. Intuyo que debe existir una API, pero no la encuentro.

Un poco de código:

Código:
HICON hIcon; //con mi icono leido del archivo.icon o de lo que sea.
HRSRC hResIcon;

hResIcon = Convertir_Icon_a_Recurso(hIcon); // Esta es la questión

BYTE *lpResLock = (BYTE*)LockResource(hResIcon);

// Abrir el fichero donde añadir el icono.
HANDLE hUpdateRes = BeginUpdateResource(DestinoExe, FALSE);
if (hUpdateRes != NULL){
    // Actualizar el resource destino
    if(UpdateResource(hUpdateRes,     
       RT_ICON,                 
       MAKEINTRESOURCE(1),
       MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 
       lpResLock,                   
       SizeofResource)!=FALSE)
       {
             // Escribir los cambios y cerrar.
             EndUpdateResource(hUpdateRes, FALSE);
      }
}
Gracias.

Saludos.
Responder Con Cita
  #2  
Antiguo 20-11-2008
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Pues lo he conseguido, pero como veo que el tema no tiene mucho interés, lo publicaré sólo si realmente le interesa a alguno.

Saludos.
Responder Con Cita
  #3  
Antiguo 21-11-2008
Avatar de cHackAll
[cHackAll] cHackAll is offline
Baneado?
 
Registrado: oct 2006
Posts: 2.159
Poder: 20
cHackAll Va por buen camino
Cita:
Empezado por escafandra Ver Mensaje
Pues lo he conseguido, pero como veo que el tema no tiene mucho interés, lo publicaré sólo si realmente le interesa a alguno.

Saludos.
Tenía planeado responder (despues de entender lo que querias y averiguar como conseguirlo ), pero como en algun lugar del ciber-espacio comenté mi PC quedo en estado de coma y pues lo olvide por completo... ésto no es una escusa ni disculpa pero creo que el echo que no aparente "importar" no deberia ser un motivo para no compartir tus conocimientos con la comunidad y las "nuevas generaciones" de desarrollo.

Saludos
__________________
RTFM > STFW > Foro > Truco > Post > cHackAll > KeBugCheckEx
Responder Con Cita
  #4  
Antiguo 21-11-2008
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Cita:
Empezado por cHackAll Ver Mensaje
Tenía planeado responder (despues de entender lo que querias y averiguar como conseguirlo ), pero como en algun lugar del ciber-espacio comenté mi PC quedo en estado de coma y pues lo olvide por completo... ésto no es una escusa ni disculpa pero creo que el echo que no aparente "importar" no deberia ser un motivo para no compartir tus conocimientos con la comunidad y las "nuevas generaciones" de desarrollo.

Saludos
Nadie debe disculparse por no responder, ni nadie tiene que darse por ofendido por no obtener respuesta. No es este mi caso. Si comenté falta de interés sobre el tema, no fue por las respuestas sino por las lecturas, 11 hasta ese momento. Entiendo, además, que es un tema demasiado específico y sin demasiada utilidad.

En cualquier caso, pensando en que tienes razón, y sin ánimo de no compartir, siempre lejos de mi intención, publico mi humilde código, siempre mejorable. Pero el post va a ser un poco largo.....

Me di cuenta que un archivo.ico tiene una cabecera de al menos 22 bytes, al compararlo con lo obtenido al leer un icono del recurso de un .exe o .dll. Despreciándolos se llega a un código funcional pero no universal, pues un .ico puede albergar mas de un icono, y en ese caso la cabecera es mas grande. La información necesaria está aquí.

Y ahora un poco de código:

Por si el tema de las definiciones de las cabeceras no queda claro, publico cómo lo he usado. Archivo Iconos.h
Código:
#ifndef iconosH
#define iconosH
//---------------------------------------------------------------------------
// Tomado de http://msdn.microsoft.com/en-us/library/ms997538.aspx 
// Modificado del original. 

// Necesario para alinear a Word el compilador
#pragma pack( push )
#pragma pack( 2 )

// These first two structs represent how the icon information is stored
// when it is bound into a EXE or DLL file. Structure members are WORD
// aligned and the last member of the structure is the ID instead of
// the imageoffset.

typedef struct
{
    BYTE    bWidth;               // Width of the image
    BYTE    bHeight;              // Height of the image (times 2)
    BYTE    bColorCount;          // Number of colors in image (0 if >=8bpp)
    BYTE    bReserved;            // Reserved
    WORD    wPlanes;              // Color Planes
    WORD    wBitCount;            // Bits per pixel
    DWORD    dwBytesInRes;         // how many bytes in this resource?
    WORD    nID;                  // the ID
} MEMICONDIRENTRY, *LPMEMICONDIRENTRY, GRPICONDIRENTRY, *LPGRPICONDIRENTRY;

typedef struct
{
    WORD            idReserved;   // Reserved
    WORD            idType;       // resource type (1 for icons)
    WORD            idCount;      // how many images?
    MEMICONDIRENTRY    idEntries[1]; // the entries for each image
} MEMICONDIR, *LPMEMICONDIR, GRPICONDIR, *LPGRPICONDIR;


// These next two structs represent how the icon information is stored
// in an ICO file.
typedef struct
{
    BYTE    bWidth;               // Width of the image
    BYTE    bHeight;              // Height of the image (times 2)
    BYTE    bColorCount;          // Number of colors in image (0 if >=8bpp)
    BYTE    bReserved;            // Reserved
    WORD    wPlanes;              // Color Planes
    WORD    wBitCount;            // Bits per pixel
    DWORD    dwBytesInRes;         // how many bytes in this resource?
    DWORD    dwImageOffset;        // where in the file is this image
} ICONDIRENTRY, *LPICONDIRENTRY;

typedef struct
{
    WORD             idReserved;   // Reserved
    WORD             idType;       // resource type (1 for icons)
    WORD             idCount;      // how many images?
    ICONDIRENTRY idEntries[1]; // the entries for each image
} ICONDIR, *LPICONDIR;

#pragma pack( pop )

//---------------------------------------------------------------------------
#endif
Función CambiaIcono:
Código:
bool CambiaIcono(char *FuenteICO, char *DestinoExe, char* ResName, bool BorraIconResPrevio=false)
{
   HANDLE hFile;
   LPBYTE lpBuffer;
   LPBYTE lpResLock;
   bool Result = true;

   if(!FuenteICO || !*FuenteICO) return false;
   if(!DestinoExe || !*DestinoExe) return false;
   if(!ResName || !*ResName) return false;
   
   // Abrimos el icono.ico en modo de lectura binaria
   DWORD dwFileSize, dwBytesRead;

   hFile = CreateFile(FuenteICO, GENERIC_READ, 0, NULL, OPEN_EXISTING,
                   FILE_ATTRIBUTE_NORMAL, NULL);

   if (INVALID_HANDLE_VALUE != hFile){
       dwFileSize = GetFileSize(hFile, NULL);
       lpBuffer = new BYTE[dwFileSize];
       ReadFile(hFile, lpBuffer, dwFileSize, &dwBytesRead, NULL);
       CloseHandle(hFile);
   }

   // Convierto el Buffer a formato Icono con cabeceras...
   ICONDIR *IconDir = (ICONDIR*)lpBuffer;
   // Crea espacio para las cabeceras del icono recurso
   int HeaderSize = sizeof(GRPICONDIR)+(sizeof(GRPICONDIRENTRY)*(IconDir->idCount-1));
   BYTE *HeaderIconRec = new BYTE[HeaderSize];
   GRPICONDIR *grpIconDir = (GRPICONDIR*)HeaderIconRec;
   // Copio la cabecera común con IconDir
   memcpy(grpIconDir, IconDir, sizeof(GRPICONDIR)-sizeof(GRPICONDIRENTRY));

   // Abrimos el recurso del exe
   HANDLE hUpdateRes = BeginUpdateResource(DestinoExe, BorraIconResPrevio);
   for(int n=0; n<grpIconDir->idCount; n++){
     // Copio las cabeceras
     memcpy(&grpIconDir->idEntries[n], &IconDir->idEntries[n], sizeof(GRPICONDIRENTRY));
     grpIconDir->idEntries[n].nID = n + 100;
     // Localizo la imagen del icono a pasar al .exe
     BYTE *IconImage = (BYTE*)IconDir + IconDir->idEntries[n].dwImageOffset;
     lpResLock = (BYTE*)LockResource(IconImage);
     // Abrir el fichero donde añadir el icono.
     if (hUpdateRes != NULL){
       // Actualizar el resource destino
       Result &= UpdateResource(hUpdateRes,
         RT_ICON,
         MAKEINTRESOURCE(grpIconDir->idEntries[n].nID),
         MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
         lpResLock,
         IconDir->idEntries[n].dwBytesInRes);
     }
   }

   // Y la grabamos comp "indice" o cabecera de grupo de iconos
   Result &= UpdateResource(hUpdateRes, RT_GROUP_ICON,
        ResName,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
        grpIconDir, HeaderSize);

   // Escribir los cambios y cerrar.
   EndUpdateResource(hUpdateRes, FALSE);

   delete lpBuffer;
   delete HeaderIconRec;

   return Result;
}
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 hechar mano de las VCL:

Código:
TIcon Icon = new TIcon;
Icon->Handle = hIcon;    // el HICON a convertir
TMemoryStream  *Stream = new TMemoryStream;
Icon->SaveToStream(Stream);  // Stream->Memory sería el buffer a usar
// Convierto el Buffer a formato Icono con cabeceras...
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. De forma que puede merecer la pena desarollar la idea del mapeo en memoria de un HICON. Yo, de momento no he tenido tiempo de hacerlo.

Bueno no me extiendo mas, y espero, como señala cHackAll, que sirva de ayuda o ejemplo de partida.

Saludos.

Última edición por escafandra fecha: 21-11-2008 a las 23:19:11.
Responder Con Cita
  #5  
Antiguo 25-11-2008
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Talking

Bueno, 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 refencia 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 temdremos 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.

El código:

Código:
# include "Iconos.h"

//---------------------------------------------------------------------------
long
NColors(WORD bitCount)
{
  if (bitCount == 1 || bitCount == 4 || bitCount == 8)
    return 1 << bitCount;
  else if (bitCount >= 24)
    return 0;
  return -1;
}

//---------------------------------------------------------------------------
void* hIconToMem(HICON hIcon, int BitCountPerPixel, int* Size)
{
    // Localizo la información del icono y su tamaño en un fichero.ico
    HDC hDC = ::GetDC(NULL);    // ScreenDC
    ICONINFO IconInfo;
    BITMAP bmpAND={0};
    BITMAP bmpXOR={0};
    bool I = GetIconInfo(hIcon, &IconInfo);
    GetObject(IconInfo.hbmMask, sizeof(BITMAP), &bmpAND);
    GetObject(IconInfo.hbmColor, sizeof(BITMAP), &bmpXOR);
    if(BitCountPerPixel==0) BitCountPerPixel=bmpXOR.bmPlanes*bmpXOR.bmBitsPixel;
    
    int RawDataSizeAND=((((bmpAND.bmWidth*bmpAND.bmBitsPixel)+31) & ~31) >> 3)*bmpAND.bmHeight;
    int RawDataSizeXOR=((((bmpXOR.bmWidth*BitCountPerPixel)+31) & ~31) >> 3)*bmpXOR.bmHeight;
    int RawDataSize = RawDataSizeAND + RawDataSizeXOR;
    int PalSize=(BitCountPerPixel>8 ? 0 :1 << BitCountPerPixel)<<2; // RGBQUAD 4
    int AllSize= sizeof(ICONDIR)+sizeof(BITMAPINFOHEADER)+PalSize+RawDataSizeAND+RawDataSizeXOR;
    int Width  = bmpAND.bmWidth;
    int Height = bmpAND.bmHeight;

    // Reservo memoria para el fichero
    BYTE* FileIconMem = (BYTE*)VirtualAlloc(0, AllSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    ICONDIR* IconDir  = (ICONDIR*)FileIconMem;
    
    // Obtengo los Bits de cada parte Mask y Color
    BITMAPINFO bmiAND;
    BITMAPINFO *bmiICON = (BITMAPINFO*)(FileIconMem + sizeof(ICONDIR));
    LPVOID lpBitsXOR =    (BITMAPINFO*)(FileIconMem + sizeof(ICONDIR) + sizeof(BITMAPINFOHEADER) + PalSize);
    LPVOID lpBitsAND =    (BITMAPINFO*)((BYTE*)lpBitsXOR + RawDataSizeXOR);

    // Preparo la cabecera del Icon
    IconDir->idReserved = 0;
    IconDir->idType = 1;
    IconDir->idCount = 1;
    IconDir->idEntries[0].bWidth = Width;
    IconDir->idEntries[0].bHeight = Height;
    IconDir->idEntries[0].bColorCount = NColors(BitCountPerPixel);
    IconDir->idEntries[0].bReserved = 0;
    IconDir->idEntries[0].wPlanes = 0;         // Color Planes
    IconDir->idEntries[0].wBitCount = 0;//BitCountPerPixel;       // Bits per pixel
    IconDir->idEntries[0].dwBytesInRes = AllSize-sizeof(ICONDIR);    // How many bytes in this resource?
    IconDir->idEntries[0].dwImageOffset = sizeof(ICONDIR);   // Where in the file is this image?
    LPICONIMAGE IconImage = (LPICONIMAGE)(bmiICON);
    memset(IconImage, 0, sizeof(BITMAPINFOHEADER));
    IconImage->icHeader.biSize = sizeof(BITMAPINFOHEADER);
    IconImage->icHeader.biWidth = Width;
    IconImage->icHeader.biHeight = Height;
    IconImage->icHeader.biPlanes = 1;
    IconImage->icHeader.biBitCount = BitCountPerPixel;
    IconImage->icHeader.biSizeImage = RawDataSize;

    // Preparo BITMAPINFOHEADER para la Mascara (bmiAND)
    memcpy(&bmiAND, bmiICON, sizeof(BITMAPINFOHEADER));
    bmiAND.bmiHeader.biSizeImage = RawDataSizeAND;
    bmiAND.bmiHeader.biBitCount = 1;
    bmiAND.bmiHeader.biClrUsed = 1;
    bmiAND.bmiHeader.biClrImportant = 1;

    // Recupero los bits de cada hBitmap del icono
    GetDIBits(hDC, IconInfo.hbmColor, 0, Height, lpBitsXOR, bmiICON, DIB_RGB_COLORS);
    GetDIBits(hDC, IconInfo.hbmMask, 0, Height, lpBitsAND, &bmiAND, DIB_RGB_COLORS);

    // Sumo las dos alturas de ambos bitmaps del icono
    IconImage->icHeader.biHeight = Height*2;

    ReleaseDC(0, hDC);

    // Salida
    *Size = AllSize;
    return FileIconMem;
}
Un ejemplo de uso y para ver como funciona:
Código:
   TIcon *Icon = new TIcon;
   Icon->LoadFromFile("MI_ICONO.ICO");
   
   // Convertimos el HICON a imagen en memoria del .ico
   int Size;
   void* FileIconMem = hIconToMem(Icon->Handle, 32, &Size);

   HANDLE hFile=CreateFile("FilePath.ico", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
   DWORD dwWritten;
   WriteFile(hFile, FileIconMem, Size, &dwWritten, 0);
   CloseHandle(hFile);
   VirtualFree(FileIconMem, 0, MEM_RELEASE);

   // Lo recuperamos para ver que ha pasado
   Icon->LoadFromFile("FilePath.ico");
   Image1->Picture->Icon->Assign(Icon);
Sirva esto para animar a indagar en los entresijos gráficos de windows...

Saludos.

Última edición por escafandra fecha: 26-11-2008 a las 02:08:03.
Responder Con Cita
  #6  
Antiguo 22-12-2008
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Dado el interés que se mostró por este tema y que he ido publicando de forma desordenada código para el manejo de iconos en la sección de recursos de un ejecutable o dll, voy a tratar de ordenar ese código siguiendo este hilo. Como esto será un poco largo, lo voy a dividir en varios post.

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 están publicadas aquí y su fuente está aquí.

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.

Código:
//------------------------------------------------------------------------------
// Funciones de enumeración de resources
//------------------------------------------------------------------------------
// Declaraciones previas

// Definición del tipo de función que acepta EnumResourceNames
typedef BOOL (__stdcall *TP)();

// Estructura para comunicar EnumResourceNames con la función de enumeración
struct TResInGr{
   int Count;
   int Id;
   char* ResName;
   HRSRC hRes;
};

// Función de enumeración para la cuenta de elementos y encontrar un Resource
BOOL CALLBACK
EnumResGrProc(HANDLE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG lParam)
{
   TResInGr *Res =  (TResInGr*)lParam;
   if(Res->Count == Res->Id){
     Res->ResName = lpszName;
     Res->hRes = ::FindResource(hModule, lpszName, lpszType);
   }
   Res->Count++;
   return true;
}

// Devuelve el Nº de Elementos de lpszType
int GetCountRes(HANDLE hModule, LPCTSTR lpszType)
{
   TResInGr Res = {0};
   Res.Id = -1;
   EnumResourceNames(hModule, lpszType, (TP)EnumResGrProc, LPARAM(&Res));
   return Res.Count;
}

// Devuelve el Nº de Elementos de lpszType
int GetCountRes(char *FuenteExe, LPCTSTR lpszType)
{
   HANDLE hModule = LoadLibrary(FuenteExe);
   int Count = GetCountRes(hModule, lpszType);
   FreeLibrary(hModule);
   return Count;
}

// Devuelve un HRSRC con Indice Id del array de resources lpszType
// Id: Indice del Grupo de iconos como si fuese una array comenzando por 0
HRSRC FindResource(HMODULE hModule, int Id, LPCTSTR lpszType)
{
   TResInGr Res = {0};
   Res.Id = Id;
   EnumResourceNames(hModule, lpszType, (TP)EnumResGrProc, LPARAM(&Res));
   return Res.hRes;
}
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 primera parte.

Saludos.

Última edición por escafandra fecha: 23-12-2008 a las 01:24:12.
Responder Con Cita
  #7  
Antiguo 22-12-2008
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Aprovecho un ratito para publicar otra parte:

Explicado los conceptos preliminares y la enumeración de recursos, pasamos a la extracción se 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.
Código:
//---------------------------------------------------------------------------
// Devuelve un ResMem Icon de un hModule
// Devuelve el tamaño de la memoria de archivo de un Icono de un hModule
// Retorna 0 si falla, 1 si Buffer==0 y AllSize si devuelve el valor en Buffer.
// Precisa de un Buffer previo de tamaño Size
// Si Buffer = NULL, devuelve en Size el tamaño necesario para el buffer
// Id: Indice del Grupo de iconos como si fuese una array comenzando por 0
int ExtractResIconFromModule(HMODULE hModule, int Id, void* Buffer, int* Size)
{
    LPGRPICONDIR grpIconDir;
    LPGRPICONDIR grpSrcIconDir;
    LPGRPICONDIRENTRY grpIconEntry;
    LPICONIMAGE  grpIconImage;
    LPICONIMAGE  resIconImage;
    BYTE* IconMem = (BYTE*)Buffer;
    HRSRC hRes;        // handle para res. info. del ejecutable
    int IconsCount = 0;
    int AllSize = sizeof(GRPICONDIR);

    // Cuento cuantos iconos tiene el recurso y la memoria necesaria
    hRes = FindResource(hModule, Id, RT_GROUP_ICON);
    if(hRes){
       grpSrcIconDir = (LPGRPICONDIR)LoadResource(hModule, hRes);
       IconsCount = grpSrcIconDir->idCount;
       for(int n=0; n<IconsCount; n++)
         AllSize += sizeof(ICONDIRENTRY) + grpSrcIconDir->idEntries[n].dwBytesInRes;
    }
    if(IconsCount==0){
      *Size = 0;
      return 0;
    }
    if(Buffer == 0 ){
      *Size = AllSize;
      return 1;
    }
    if(*Size < AllSize) return 0;

    // Preparo la Cabecera General grpIconDir
    grpIconDir  = (LPGRPICONDIR)IconMem;
    grpIconDir->idReserved = 0;
    grpIconDir->idType = 1;
    grpIconDir->idCount = IconsCount;
    grpIconEntry = LPMEMICONDIRENTRY(IconMem + sizeof(GRPICONDIR) - sizeof(GRPICONDIRENTRY));

    // Localizar el ICON resource en el ejecutable y sus Imagenes.
    hRes = NULL;
    grpIconImage = (LPICONIMAGE)((BYTE*)grpIconDir + sizeof(GRPICONDIR) + (sizeof(GRPICONDIRENTRY)*(grpIconDir->idCount-1)));
    for(int n=0; n<IconsCount; n++){
      int nID = grpSrcIconDir->idEntries[n].nID;
      hRes = ::FindResource(hModule, MAKEINTRESOURCE(nID), RT_ICON);
      // Preparo las cabeceras Entrada de cada Imagen
      resIconImage = (ICONIMAGE*)LoadResource(hModule, hRes);
      grpIconEntry[n].bWidth = resIconImage->icHeader.biWidth;
      grpIconEntry[n].bHeight = resIconImage->icHeader.biHeight/2;
      grpIconEntry[n].bColorCount =  NColors(resIconImage->icHeader.biBitCount);
      grpIconEntry[n].bReserved = 0;
      grpIconEntry[n].wPlanes = 1;
      grpIconEntry[n].wBitCount = resIconImage->icHeader.biBitCount;
      grpIconEntry[n].dwBytesInRes = SizeofResource(hModule, hRes);
      grpIconEntry[n].nID = n;
      // Copio la imagen
      memcpy(grpIconImage, resIconImage, grpIconEntry[n].dwBytesInRes);
//      grpIconImage = (LPICONIMAGE)((BYTE*)grpIconImage + grpIconDir->idEntries[n-1].dwBytesInRes);
      grpIconImage = (LPICONIMAGE)((BYTE*)grpIconImage + grpIconEntry[n].dwBytesInRes);
    }

    return AllSize;
}
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:

Código:
//---------------------------------------------------------------------------
// grpIconDir : Buffer con el recurso del tipo RT_GROUP_ICON extraido con  ExtractResIconFromModule
// DestinoExe: Nombre de un ejecutable o dll
// ResName: Nombre que se le dará al recurso añadico al ejecutable o dll
// BorraIconResPrevio: Si es trae se borraran los recursos previos RT_GROUP_ICON del ejecutable o dll
bool  AddIconToExe(LPGRPICONDIR grpIconDir, char *DestinoExe, char* ResName, bool BorraIconResPrevio)
{
   LPGRPICONDIRENTRY IconEntry;
   LPICONIMAGE  IconImage;
   LPBYTE lpResLock;
   bool Result = true;

   if(!grpIconDir) return false;
   if(!DestinoExe || !*DestinoExe) return false;
   if(!ResName || !*ResName) return false;

   // Crea espacio para las cabeceras del icono recurso
   int HeaderSize = sizeof(GRPICONDIR)+(sizeof(GRPICONDIRENTRY)*(grpIconDir->idCount-1));

   HANDLE hUpdateRes = BeginUpdateResource(DestinoExe, BorraIconResPrevio);
   // Localizo la primera imagen del icono a pasar al .exe
   IconImage = (LPICONIMAGE)((BYTE*)grpIconDir + sizeof(GRPICONDIR) + (sizeof(GRPICONDIRENTRY)*(grpIconDir->idCount-1)));
   for(int n=0; n<grpIconDir->idCount; n++){
     lpResLock = (BYTE*)LockResource(IconImage);
     // Abrir el fichero donde añadir el icono.
     if (hUpdateRes != NULL){
       // Actualizar el resource destino
       Result &= UpdateResource(hUpdateRes,
         RT_ICON,
         MAKEINTRESOURCE(grpIconDir->idEntries[n].nID),
         MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
         lpResLock,
         grpIconDir->idEntries[n].dwBytesInRes);
     }
     // Localizo la siguiente Imagen
     IconImage = (LPICONIMAGE)((BYTE*)IconImage + grpIconDir->idEntries[n].dwBytesInRes);
   }

   // Y la grabamos como "indice" o cabecera de grupo de iconos
   Result &= UpdateResource(hUpdateRes, RT_GROUP_ICON,
        ResName,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
        grpIconDir, HeaderSize);

   // Escribir los cambios y cerrar.
   Result &= EndUpdateResource(hUpdateRes, FALSE);

   return Result;
}
Y para terminar de ilustrar estas funciones, propongo esta otra que hace uso de las dos:
Código:
//------------------------------------------------------------------------------
// Pasa todas las imágenes del primer Grupo Icono (principal por defecto del ejecutable)
// de un exe (o dll) a otro
bool AddIconToExe(char *FuenteExe, char *DestinoExe, char* ResName, bool BorraIconResPrevio)
{
    bool Result;
    BYTE* Buffer;
    int Size;    
    LPGRPICONDIR grpIconDir;
    HMODULE hModule;
    hModule = LoadLibrary(FuenteExe);

    // Calculo el tamaño necesario para el Buffer
    ExtractResIconFromModule (hModule, 0, 0, &Size);
    Buffer = new BYTE[Size];

    // Extraigo del ejecutable (hModule) el primer grupo de iconos que obtengo en el Buffer
    ExtractResIconFromModule(hModule, 0, Buffer, &Size);
    grpIconDir  = (LPGRPICONDIR)Buffer;
    
    FreeLibrary(hModule);
    if(grpIconDir)  // Si el Buffer es válido
       Result = AddIconToExe(grpIconDir, DestinoExe, ResName, BorraIconResPrevio);
    else   // Si el buffer no es válido
       Result = false;

    // Libero la memoria del Grupo de iconos antes de retornar.
    delete [] Buffer;
   
    return Result;
}
En el siguiente post publicaré el modo de guardar en un archivo, un Grupo de Iconos con las funciones GetFileMemIconFromModule y SaveResIconToFile.

Saludos.

PD: Edito para añadir otro ejemplo: AddIconToExe

Última edición por escafandra fecha: 23-12-2008 a las 01:47:11.
Responder Con Cita
  #8  
Antiguo 23-12-2008
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
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.

Código:
//------------------------------------------------------------------------------
// Devuelve en Buffer Una imagen de archivo.ico para poder guardar en disco.
// Devuelve el tamaño de la memoria de archivo de un Icono de un hModule
// Retorna 0 si falla, 1 si Buffer==0 y AllSize si devuelve el valor en Buffer.
// Si Buffer = NULL, devuelve en Size el tamaño necesario para el buffer
// Id: Indice del Grupo de iconos como si fuese una array comenzando por 0
int GetFileMemIconFromModule(HMODULE hModule, int Id, void* Buffer, int *Size)
{
    LPICONDIR IconDir;
    LPICONDIRENTRY IconEntry;
    LPICONIMAGE  resIconImage;
    LPGRPICONDIR grpIconDir;
    HRSRC hRes;        // handle para res. info. del ejecutable
    int IconsCount = 0;
    int AllSize = sizeof(ICONDIR)-sizeof(ICONDIRENTRY);
    DWORD ImageOffset;

    // Cuento cuantos iconos tiene el recurso y la memoria necesaria
    hRes = FindResource(hModule, Id, RT_GROUP_ICON);
    if(hRes){
       grpIconDir = (LPGRPICONDIR)LoadResource(hModule, hRes);
       IconsCount = grpIconDir->idCount;
       for(int n=0; n<IconsCount; n++)
         AllSize += sizeof(ICONDIRENTRY) + grpIconDir->idEntries[n].dwBytesInRes;
    }

    if(IconsCount==0){
      *Size = 0;
      return 0;
    }
    if(Buffer == 0 ){
      *Size = AllSize;
      return 1;
    }
    if(*Size < AllSize) return 0;

    // Preparo la Cabecera General grpIconDir
    setmem(Buffer, 0, AllSize);
    IconDir  = (LPICONDIR)Buffer;
    IconDir->idReserved = 0;
    IconDir->idType = 1;
    IconDir->idCount = IconsCount;
    // IconEntry apunta a la entrada del primer icono IconDir->idEntries[0]
//    IconEntry = LPICONDIRENTRY(Buffer + sizeof(ICONDIR) - sizeof(ICONDIRENTRY));
    IconEntry = &IconDir->idEntries[0];

    // Localizar el ICON resource en el ejecutable y sus Imagenes.
    hRes = NULL;
    ImageOffset = sizeof(ICONDIR) + (sizeof(ICONDIRENTRY)*(IconDir->idCount-1));
    // Recorro las imágenes del recurso y preparo las entradas y las imágenes en el Buffer
    for(int n=0; n<IconsCount; n++){
      int nID = grpIconDir->idEntries[n].nID;
      hRes = ::FindResource(hModule, MAKEINTRESOURCE(nID), RT_ICON);
      resIconImage = (ICONIMAGE*)LoadResource(hModule, hRes);
      IconEntry[n].bWidth = resIconImage->icHeader.biWidth;
      IconEntry[n].bHeight = resIconImage->icHeader.biHeight/2;
      IconEntry[n].bColorCount =  NColors(resIconImage->icHeader.biBitCount);
      IconEntry[n].bReserved = 0;
      IconEntry[n].wPlanes = 1;
      IconEntry[n].wBitCount = resIconImage->icHeader.biBitCount;
      IconEntry[n].dwBytesInRes = SizeofResource(hModule, hRes);
      IconEntry[n].dwImageOffset = ImageOffset;
      // Copio la imagen
      memcpy((BYTE*)Buffer+ImageOffset, resIconImage, IconEntry[n].dwBytesInRes);
      ImageOffset += IconEntry[n].dwBytesInRes;
    }

    return AllSize;
}
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:
Código:
//------------------------------------------------------------------------------
// Guarda en disco File.ico el grupo de Icono de un exe o dll
// Id: Indice del Grupo de iconos como si fuese una array comenzando por 0
bool SaveResIconToFile(char *FuenteExe, char* DestFileName, int Id)
{
   BYTE* Buffer;
   int Size;
   HMODULE hModule;
   hModule = LoadLibrary(FuenteExe);

   // Calculo el tamaño necesario para el Buffer
   GetFileMemIconFromModule(hModule, Id, 0, &Size);
   Buffer = new BYTE[Size];
   
   // Extraigo del ejecutable (hModule) el grupo de iconos que obtengo en el Buffer
   // Este Buffer es una imagen en memoria de un archivo.ico que se puede guardar directamente.
   int S = GetFileMemIconFromModule(hModule, Id, Buffer, &Size);
   if(S==0) return false;

   // Creo y guardo el archivo de iconos
   HANDLE hFile = CreateFile(DestFileName, GENERIC_WRITE, 0, NULL,
                   CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

   unsigned long dwBytesWritten;

   if (INVALID_HANDLE_VALUE != hFile){
       WriteFile(hFile, Buffer, Size, &dwBytesWritten, NULL);
       CloseHandle(hFile);
   }

   FreeLibrary(hModule);
   delete [] Buffer;
   return (dwBytesWritten == Size);
}
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.
Responder Con Cita
  #9  
Antiguo 29-12-2008
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
En el truco 575 publico la función hIconToMem de este post y un ejemplo de como usarla para guardar en un archivo un HICON y para pasar el HICON a un componente TIcon.

Saludos.
Responder Con Cita
  #10  
Antiguo 04-01-2010
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Vista la demanda para guardar correctamente un Icono dado por su Handle (HICON) desde delphi, publico una implementación en delphi:

Estructuras necesarias:
Código Delphi [-]
// These next two structs represent how the icon information is stored
// in an ICO file.
{$ALIGN 1}
//{$ALIGN ON}
type TICONDIRENTRY = record
   bWidth: BYTE;               // Width of the image
   bHeight: BYTE;              // Height of the image (times 2)
   bColorCount: BYTE;          // Number of colors in image (0 if >=8bpp)
   bReserved: BYTE;            // Reserved
   wPlanes: WORD;              // Color Planes
   wBitCount: WORD;            // Bits per pixel
   dwBytesInRes: DWORD;        // how many bytes in this resource?
   dwImageOffset: DWORD;       // where in the file is this image
end; PICONDIRENTRY = ^TICONDIRENTRY;

type TICONDIR = record
   idReserved: WORD; // Reserved
   idType: WORD;     // resource type (1 for icons)
   idCount: WORD;    // how many images?
   idEntries: array[0..0] of TICONDIRENTRY; // the entries for each image
end; PICONDIR = ^TICONDIR;

type tagICONIMAGE= record
   icHeader: BITMAPINFOHEADER;       // DIB header
   icColors: array[0..0] of RGBQUAD; // Color table
   icXORarray: array[0..0] of BYTE;         // DIB bits for XOR mask
   icANDarray: array[0..0] of BYTE;         // DIB bits for AND mask
end; PICONIMAGE = ^tagICONIMAGE;
{$ALIGN OFF}

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

Código Delphi [-]
//---------------------------------------------------------------------------
// Convierte bitCount a número de colores
function NColors(bitCount: WORD): integer;
begin
  Result:= -1;
  if (bitCount = 1) or (bitCount = 4) or (bitCount = 8) then
    Result:= 1 shl bitCount
  else if (bitCount >= 24) then
    Result:= 0;
end;
Ahora la función hIconToMem que se encargará de conseguir una imagen en memoria de un fichero.ico a partir de un Handle HICON. El código está lo suficientemente comentado:

Código Delphi [-]
//---------------------------------------------------------------------------
// Devuelve un puntero con la imagen de archivo.ico para poder guardar en disco.
// Devuelve el tamaño de la memoria de archivo de un Icono en Size
// Es necesario liberar el puntero devuelto tras usarlo: usar VirtualFree
function hIconToMem(hIcon: Thandle; BitCountPerPixel: integer; var Size: integer): Pointer;
var
  hDC: Thandle;
  IconInfo: TICONINFO;
  bmpAND, bmpXOR: BITMAP;
  FileIconMem: Pointer;
  RawDataSizeAND, RawDataSizeXOR, RawDataSize, PalSize, AllSize, Width, Height: integer;
  IconDir: PICONDIR;
  bmiAND: BITMAPINFO;
  bmiICON: PBITMAPINFO;
  lpBitsXOR, lpBitsAND: Pointer;
  IconImage: PICONIMAGE;

begin
  // Localizo la información del icono y su tamaño en un fichero.ico
  hDC:= GetDC(0);    // ScreenDC
  ZeroMemory(@bmpAND, sizeof(BITMAP));
  ZeroMemory(@bmpXOR, sizeof(BITMAP));
  GetIconInfo(hIcon, IconInfo);
  GetObject(IconInfo.hbmMask, sizeof(BITMAP), @bmpAND);
  GetObject(IconInfo.hbmColor, sizeof(BITMAP), @bmpXOR);
  if(BitCountPerPixel=0) then BitCountPerPixel:= bmpXOR.bmPlanes*bmpXOR.bmBitsPixel;
  RawDataSizeAND:= ((((bmpAND.bmWidth*bmpAND.bmBitsPixel)+31) and not 31) shr 3)*bmpAND.bmHeight;
  RawDataSizeXOR:= ((((bmpXOR.bmWidth*BitCountPerPixel)+31) and not 31) shr 3)*bmpXOR.bmHeight;
  RawDataSize:= RawDataSizeAND + RawDataSizeXOR;
  PalSize:=0; if(BitCountPerPixel<=8) then PalSize:= (1 shl BitCountPerPixel) shl 2; // RGBQUAD 4
  AllSize:= sizeof(TICONDIR)+sizeof(BITMAPINFOHEADER)+PalSize+RawDataSizeAND+RawDataSizeXOR;
  Width  := bmpAND.bmWidth;
  Height := bmpAND.bmHeight;

  // Reservo memoria para el fichero
  FileIconMem:= VirtualAlloc(0, AllSize, MEM_COMMIT, PAGE_READWRITE);
  IconDir:=     PICONDIR(FileIconMem);

  // Obtengo los Bits de cada parte Mask y Color
  bmiICON:=   PBITMAPINFO(DWORD(FileIconMem) + sizeof(TICONDIR));
  lpBitsXOR:= PBITMAPINFO(DWORD(FileIconMem) + sizeof(TICONDIR) + sizeof(BITMAPINFOHEADER) + PalSize);
  lpBitsAND:= PBITMAPINFO(DWORD(lpBitsXOR) + RawDataSizeXOR);

  // Preparo la cabecera del Icon
  IconDir.idReserved:= 0;
  IconDir.idType:= 1;
  IconDir.idCount:= 1;
  IconDir.idEntries[0].bWidth:= Width;
  IconDir.idEntries[0].bHeight:= Height;
  IconDir.idEntries[0].bColorCount:= NColors(BitCountPerPixel);
  IconDir.idEntries[0].bReserved:= 0;
  IconDir.idEntries[0].wPlanes:= 0;         // Color Planes
  IconDir.idEntries[0].wBitCount:= 0;//BitCountPerPixel;       // Bits per pixel
  IconDir.idEntries[0].dwBytesInRes:= AllSize-sizeof(TICONDIR);    // How many bytes in this resource?
  IconDir.idEntries[0].dwImageOffset:= sizeof(TICONDIR);   // Where in the file is this image?
  IconImage:= PICONIMAGE(bmiICON);
  ZeroMemory(IconImage, sizeof(BITMAPINFOHEADER));
  IconImage.icHeader.biSize:= sizeof(BITMAPINFOHEADER);
  IconImage.icHeader.biWidth:= Width;
  IconImage.icHeader.biHeight:= Height;
  IconImage.icHeader.biPlanes:= 1;
  IconImage.icHeader.biBitCount:= BitCountPerPixel;
  IconImage.icHeader.biSizeImage:= RawDataSize;

  // Preparo BITMAPINFOHEADER para la Mascara (bmiAND)
  CopyMemory(@bmiAND, bmiICON, sizeof(BITMAPINFOHEADER));
  bmiAND.bmiHeader.biSizeImage:= RawDataSizeAND;
  bmiAND.bmiHeader.biBitCount:= 1;
  bmiAND.bmiHeader.biClrUsed:= 1;
  bmiAND.bmiHeader.biClrImportant:= 1;

  // Recupero los bits de cada hBitmap del icono
  GetDIBits(hDC, IconInfo.hbmColor, 0, Height, lpBitsXOR, bmiICON^, DIB_RGB_COLORS);
  GetDIBits(hDC, IconInfo.hbmMask, 0, Height, lpBitsAND, bmiAND, DIB_RGB_COLORS);

  // Sumo las dos alturas de ambos bitmaps del icono
  IconImage.icHeader.biHeight:= Height*2;

  // Salida
  Size:= AllSize;
  Result:= FileIconMem;
end;

Un ejemplo para guardar en un archivo nuestro icono dado por su Handle:

Código Delphi [-]
function HIconToFile(hIcon: THandle; FileName: String): Boolean;
var
  FileIconMem: Pointer;
  Size: integer;
  hFile: Cardinal;
begin
  Result:= false;
  FileIconMem:= hIconToMem(hIcon, 32, Size);
  hFile := CreateFile(PCHAR(FileName), GENERIC_WRITE, 0, nil, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0);
  if(_lwrite(hFile, FileIconMem, Size) = Size) then
      Result:= true;
  CloseHandle(hFile);
  VirtualFree(FileIconMem, 0, MEM_RELEASE);
end;

Más información aquí.

Saludos.

Última edición por escafandra fecha: 04-01-2010 a las 18:15:28.
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
Border Icon pmcastilla Varios 2 10-09-2007 16:49:29
Componente Tray Icon marceloalegre Varios 1 06-06-2005 13:51:21
Try Icon narvaez.om Varios 1 03-05-2005 22:13:06
Tray Icon con abcTrayIcon Alexander Varios 0 29-09-2004 21:22:28
mouse Icon en un TQuery jymy788 Varios 4 17-09-2004 13:31:16


La franja horaria es GMT +2. Ahora son las 20:18:28.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi