How to display animated GIF?

-1

I have an animated GIF, I'm trying to display it but I couldn't make it work. It won't run and throwing exception 0xC0000005: Access violation reading location 0x80000B00. at the line SetTimer(hWnd, ID_TIMER, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, (TIMERPROC)NULL);.

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    Image* m_pImage = new Image(L"spinner.gif");
    UINT m_iCurrentFrame = 0;
    UINT m_FrameCount = 0;
    PropertyItem* m_pItem = 0;

    switch(message) {
        ...
        case WM_TIMER: {
            //Because there will be a new delay value
            KillTimer(hWnd, ID_TIMER);
            //Change Active frame
            GUID Guid = FrameDimensionTime;
            m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);
            //New timer
            SetTimer(hWnd, ID_TIMER, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, NULL);
            //Again move to the next
            m_iCurrentFrame = (++m_iCurrentFrame) % m_FrameCount;
            InvalidateRect(hWnd, NULL, FALSE);
            break;
        }
        case WM_PAINT:{
            HDC hdc;
            PAINTSTRUCT ps;
            hdc = BeginPaint(hWnd, &ps);

            Graphics graphics(hdc); 
            
            //First of all we should get the number of frame dimensions
            //Images considered by GDI+ as:
            //frames[animation_frame_index][how_many_animation];
            UINT count = m_pImage->GetFrameDimensionsCount();

            //Now we should get the identifiers for the frame dimensions 
            GUID* m_pDimensionIDs = new GUID[count];
            m_pImage->GetFrameDimensionsList(m_pDimensionIDs, count);

            //For gif image , we only care about animation set#0
            WCHAR strGuid[40];
            StringFromGUID2(m_pDimensionIDs[0], strGuid, 40);
            m_FrameCount = m_pImage->GetFrameCount(&m_pDimensionIDs[0]);

            //PropertyTagFrameDelay is a pre-defined identifier 
            //to present frame-delays by GDI+
            UINT TotalBuffer = m_pImage->GetPropertyItemSize(PropertyTagFrameDelay);
            m_pItem = (PropertyItem*)malloc(TotalBuffer);
            m_pImage->GetPropertyItem(PropertyTagFrameDelay, TotalBuffer, m_pItem);

            //Set Current Frame at #0
            m_iCurrentFrame = 0;
            GUID Guid = FrameDimensionTime;
            m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);

            //Use Timer
            //NOTE HERE: frame-delay values should be multiply by 10
            SetTimer(hWnd, ID_TIMER, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, (TIMERPROC)NULL);
          
            //Move to the next frame
            ++m_iCurrentFrame;
            InvalidateRect(hWnd, NULL, FALSE);

            //Finally simply draw
            graphics.DrawImage(m_pImage, 120, 120, 100, 100);

            EndPaint(hWnd, &ps);
            break;
        }
        ...
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

Most of the example I found is WCF or the functions are depreciated.

c++
winapi
visual-studio-2019
gdi+
asked on Stack Overflow Feb 26, 2021 by Papilion • edited Feb 26, 2021 by Ken White

1 Answer

0

Solution:

You can set m_pItem to global. In addition to this error, you have some other errors. For example, m_iCurrentFrame is always initialized to 0 in WM_PAINT.

And we only need to load the GIF image file once, and don't need to put it in WM_PAINT.

I modified your code and it works now.

#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
#define ID_TIMER 101


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
{
    HWND                hWnd;
    MSG                 msg;
    WNDCLASS            wndClass;
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;

    // Initialize GDI+.
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    wndClass.style = CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = WndProc;
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;
    wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.lpszMenuName = NULL;
    wndClass.lpszClassName = TEXT("GettingStarted");

    RegisterClass(&wndClass);

    hWnd = CreateWindow(
        TEXT("GettingStarted"),   // window class name
        TEXT("Getting Started"),  // window caption
        WS_OVERLAPPEDWINDOW,      // window style
        CW_USEDEFAULT,            // initial x position
        CW_USEDEFAULT,            // initial y position
        CW_USEDEFAULT,            // initial x size
        CW_USEDEFAULT,            // initial y size
        NULL,                     // parent window handle
        NULL,                     // window menu handle
        hInstance,                // program instance handle
        NULL);                    // creation parameters

    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    GdiplusShutdown(gdiplusToken);
    return msg.wParam;
}  // WinMain


PropertyItem* m_pItem = 0;
UINT m_iCurrentFrame = 0;
UINT m_FrameCount = 0;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
    WPARAM wParam, LPARAM lParam)
{
    HDC          hdc;
    PAINTSTRUCT  ps;
    Image* m_pImage = new Image(L"spinner.gif");
   
    switch (message)
    {
    case WM_CREATE:
    {
        //First of all we should get the number of frame dimensions
        //Images considered by GDI+ as:
        //frames[animation_frame_index][how_many_animation];
        UINT count = m_pImage->GetFrameDimensionsCount();

        //Now we should get the identifiers for the frame dimensions 
        GUID* m_pDimensionIDs = new GUID[count];
        m_pImage->GetFrameDimensionsList(m_pDimensionIDs, count);

        //For gif image , we only care about animation set#0
        WCHAR strGuid[40];
        StringFromGUID2(m_pDimensionIDs[0], strGuid, 40);
        m_FrameCount = m_pImage->GetFrameCount(&m_pDimensionIDs[0]);

        //PropertyTagFrameDelay is a pre-defined identifier 
        //to present frame-delays by GDI+
        UINT TotalBuffer = m_pImage->GetPropertyItemSize(PropertyTagFrameDelay);
        m_pItem = (PropertyItem*)malloc(TotalBuffer);
        m_pImage->GetPropertyItem(PropertyTagFrameDelay, TotalBuffer, m_pItem);
    }
    break;
    case WM_TIMER: {
        //Because there will be a new delay value
        KillTimer(hWnd, ID_TIMER);
        //Change Active frame
        GUID Guid = FrameDimensionTime;
        m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);
        //New timer
        SetTimer(hWnd, ID_TIMER, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, NULL);
        //Again move to the next
        m_iCurrentFrame = (++m_iCurrentFrame) % m_FrameCount;
        InvalidateRect(hWnd, NULL, FALSE);
        break;
    }
    case WM_PAINT:
    {
        HDC hdc;
        PAINTSTRUCT ps;
        hdc = BeginPaint(hWnd, &ps);

        Graphics graphics(hdc);

        //Set Current Frame at #0
 //       m_iCurrentFrame = 0;
        GUID Guid = FrameDimensionTime;
        m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);

        //Use Timer
        //NOTE HERE: frame-delay values should be multiply by 10
        SetTimer(hWnd, ID_TIMER, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, (TIMERPROC)NULL);

        //Move to the next frame
        ++m_iCurrentFrame;
        InvalidateRect(hWnd, NULL, FALSE);

        //Finally simply draw
        graphics.DrawImage(m_pImage, 120, 120, 800, 600);

        EndPaint(hWnd, &ps);
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
} // WndProc
answered on Stack Overflow Feb 26, 2021 by Strive Sun - MSFT

User contributions licensed under CC BY-SA 3.0