Inno Setup does not provide a straigth way to do it. You will find some workarounds that use several bitmaps and replace them using a timer. But doing some research here and there I was able to implement Windows GDI+ support from Inno Setup!
These are the functions to implement:
function SetTimer( hWnd, nIDEvent, uElapse, lpTimerFunc: Longword ): Longword;
external 'SetTimer@user32.dll stdcall';
function KillTimer( hWnd: HWND; uIDEvent: UINT ): BOOL;
external 'KillTimer@user32.dll stdcall';
function GdiplusStartup( var token: Longword; var inputbuf: GdiPlusStartupInput; var outputbuf: GdiplusStartupOutput ): GpStatus;
external 'GdiplusStartup@GdiPlus.dll stdcall';
function GdipCreateFromHWND( hWnd: HWND; var graphics: GpGraphics ): GpStatus;
external 'GdipCreateFromHWND@GdiPlus.dll stdcall';
function GdipLoadImageFromFile( filename: string; var image: GpImage ): GpStatus;
external 'GdipLoadImageFromFile@GdiPlus.dll stdcall';
function GdipDrawImageRect( graphics: GpGraphics; image: GpImage; x,y: single; width, height: single ): GpStatus;
external 'GdipDrawImageRect@GdiPlus.dll stdcall';
function GdipImageGetFrameDimensionsCount( image: GpImage; var count: Integer ): GpStatus;
external 'GdipImageGetFrameDimensionsCount@GdiPlus.dll stdcall';
function GdipImageGetFrameCount( image: GpImage; var dimensionID: TGuid; var count: Integer ): GpStatus;
external 'GdipImageGetFrameCount@GdiPlus.dll stdcall';
function GdipImageGetFrameDimensionsList( image: GpImage; var dimensionID: TGuid; count: Integer ): GpStatus;
external 'GdipImageGetFrameDimensionsList@GdiPlus.dll stdcall';
function GdipImageSelectActiveFrame( image: GpImage; dimensionID: TGuid; frameIndex: Integer ): GpStatus;
external 'GdipImageSelectActiveFrame@GdiPlus.dll stdcall';
You have to use these types:
type
TTimerProc = procedure( Wnd: HWND; Msg: UINT; TimerID: UINT_PTR; SysTime: DWORD );
GpGraphics = Longword;
GpImage = Longword;
Status = (
Ok, { 0 }
GenericError, { 1 }
InvalidParameter, { 2 }
OutOfMemory, { 3 }
ObjectBusy, { 4 }
InsufficientBuffer, { 5 }
NotImplemented, { 6 }
Win32Error, { 7 }
WrongState, { 8 }
Aborted, { 9 }
FileNotFound, { 10 }
ValueOverflow, { 11 }
AccessDenied, { 12 }
UnknownImageFormat, { 13 }
FontFamilyNotFound, { 14 }
FontStyleNotFound, { 15 }
NotTrueTypeFont, { 16 }
UnsupportedGdiplusVersion, { 17 }
GdiplusNotInitialized, { 18 }
PropertyNotFound, { 19 }
PropertyNotSupported { 20 }
);
GpStatus = Status;
GdiplusStartupInput = record
GdiplusVersion : Cardinal;
DebugEventCallback : Longword;
SuppressBackgroundThread: BOOL;
SuppressExternalCodecs : BOOL;
end;
GdiplusStartupOutput = record
NotificationHook : Longword;
NotificationUnhook: Longword;
end;
And these vars:
ClockImage: TPanel;
TimerID: Integer;
token: Longword;
inputbuf: GdiplusStartupInput;
outputbuf: GdiplusStartupOutput;
graphics: GpGraphics;
image: GpImage;
status: GpStatus;
count: Integer;
dimensionID: array[ 0..1 ] of TGuid;
iFrame: Integer;
Using this code, GDI+ gets initialized and we load and analyze the animated GIF:
inputbuf.GdiplusVersion := 1;
status := GdiplusStartup( token, inputbuf, outputbuf );
{ Assert( status ); }
status := GdipCreateFromHWND( ClockImage.Handle, graphics );
{ Assert( status ); }
status := GdipLoadImageFromFile( ExpandConstant( '{tmp}' ) + '\sand-clock-dribbble.gif', image );
{ Assert( status ); }
{ MsgBox( intToStr( image ), mbInformation, 1 ); }
status := GdipDrawImageRect( graphics, image, 0, 0, ClockImage.Width, ClockImage.Height );
{ Assert( status ); }
status := GdipImageGetFrameDimensionsCount( image, count );
{ Assert( status ); }
{ MsgBox( intToStr( count ), mbInformation, 1 ); }
status := GdipImageGetFrameDimensionsList( image, dimensionID[ 0 ], count );
{ Assert( status ); }
{ MsgBox( intToStr( count ), mbInformation, 1 ); }
status := GdipImageGetFrameCount( image, dimensionID[ 0 ], count );
{ Assert( status ); }
{ MsgBox( intToStr( count ), mbInformation, 1 ); }
iFrame := 1
status := GdipImageSelectActiveFrame( image, dimensionID[ 0 ], iFrame );
A timer is used to change the aimated GIFs frames using standard GDI+ functions:
procedure MyTimerProc( Arg1, Arg2, Arg3, Arg4: Longword );
begin
if iFrame < count then begin
iFrame := iFrame + 1;
end else begin
iFrame := 0
end;
status := GdipImageSelectActiveFrame( image, dimensionID[ 0 ], iFrame );
status := GdipDrawImageRect( graphics, image, 0, 0, ClockImage.Width, ClockImage.Height );
end;
Full source code is available from here:
https://github.com/FiveTechSoft/mod_harbour/blob/master/windows/win64/setup/modharbour.iss
To test it you can directly download the resulting EXE built from GitHub actions:
https://github.com/FiveTechSoft/mod_harbour/actions/runs/102906695
Enjoy it!