Updating resources files with BMP and PNG files (with managed code)

This only concerns Bitmap resources (RT_BITMAP). Updating others type of resources is easier and doesn’t require specific code.

  • BMP files

Inserting BMP files is quite easy. You only have to remove the header and you are good to go.

int BMP_HEADER_BYTES = 14;
FileStream stream = new FileStream(file, 

                                   FileMode.Open,
                                   FileAccess.Read,
                                   FileShare.Read);

BinaryReader reader = new BinaryReader(stream);
byte[] data = reader.ReadBytes((int)reader.BaseStream.Length);

GCHandle gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
IntPtr bitmap = Marshal.UnsafeAddrOfPinnedArrayElement(data, BMP_HEADER_BYTES);

uint size = (uint)(data.GetLength(0) - BMP_HEADER_BYTES);
UpdateResource(hDll, 2, resourceId, LANG_ENGLISH_ID, bitmap, size);

gcHandle.Free();
stream.Close();
  • PNG files

It’s a bit more difficult with PNG files. You must add a header before inserting the image data. You also need to rotate the image before inserting because of the bitmap is copied and read (alternatively, you can insert a negative height into the header, but I haven’t tested it).

Also note that we do not set the compression to BI_PNG (5) and the size of the image in the BITMAPINFO header.

[StructLayout(LayoutKind.Sequential)] 
class BITMAPINFO {
public int biSize;
public int biWidth;
public int biHeight;
public short biPlanes;
public short biBitCount;     
public int biCompression;     
public int biSizeImage;     
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant; }

// Load file 
Bitmap pngFile = new Bitmap(file);
// Required due to the way bitmap is copied and read
pngFile.RotateFlip(RotateFlipType.RotateNoneFlipY);
BitmapData data = pngFile.LockBits(new Rectangle(0, 0,
                                                 pngFile.Width, pngFile.Height),
                                   ImageLockMode.ReadOnly,
                                   PixelFormat.Format32bppArgb); 

byte[] dataBuffer = new byte[data.Height * data.Stride]; 
Marshal.Copy(data.Scan0, dataBuffer, 0, data.Height * data.Stride); 
pngFile.UnlockBits(data);
// Setup Header 
BITMAPINFO header = new BITMAPINFO(); 
header.biSize = Marshal.SizeOf(typeof(BITMAPINFO));
header.biWidth = pngFile.Width; 
header.biHeight = pngFile.Height;
header.biPlanes = 1; 
header.biBitCount = 32; 

// Convert the structure to an array 
byte[] buffer = new byte[header.biSize];
GCHandle h = GCHandle.Alloc(buffer , GCHandleType.Pinned); 
Marshal.StructureToPtr(header, h.AddrOfPinnedObject(), false);
h.Free(); 

// Write header + data 
MemoryStream memory = new MemoryStream(); 
memory.Write(buffer, 0, buffer.Length);
memory.Write(dataBuffer, 0, dataBuffer.Length);
byte[] imageData = memory.GetBuffer(); 
GCHandle gcHandle = GCHandle.Alloc(imageData, GCHandleType.Pinned); 
IntPtr firstCopyElement = Marshal.UnsafeAddrOfPinnedArrayElement(imageData, 0);

UpdateResource(hDll, 2, resourceId, LANG_ENGLISH_ID,                
	       firstCopyElement, (uint)imageData.GetLength(0));

gcHandle.Free(); 
memory.Close();