Search code examples
c++arraysimagegdi+cautoptr

C++ Trouble accessing CAutoPtr array


I'm working on a windows program whose's goal is to display image buttons using GDIPlus and the Windows header file.

The images are attached to an global CAutoPtr array. Inside the button callback, I handle the WM_PAINT message by searching the image array (imageList) using the button's identifier (GetDlgCtrlID(hWnd)).

I am able to paint with the first image in imageList, however, when I try to paint the next button with imageList[2], it does not show any image.

Where exactly is the problem and why can I not display any image besides whatever's in the first slot of imageList?

Thank you!

This handles all of the button messages.

CAutoPtr<Gdiplus::Image> typedef GdiplusImagePtr;
GdiplusImagePtr imageList[50];
Rect imagePositions[50];

LRESULT CALLBACK CustomButtonProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg) {
    case WM_CREATE:
    {
        // Same as WM_PAINT
        break;
    }
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC newDC = BeginPaint(hWnd, &ps);

        Gdiplus::Graphics newGraphics(hWnd);
        newGraphics.DrawImage(imageList[GetDlgCtrlID(hWnd)], imagePositions[GetDlgCtrlID(hWnd)]);

        EndPaint(hWnd, &ps);
        ReleaseDC(hWnd, GetDC(hWnd));
        DeleteDC(newDC);
        break;
    }
    return CallWindowProc(customButtonProc, hWnd, msg, wp, lp);
}

I use this line of code to attach an image to imageList. I confirmed that imageList does hold the other images; I just can not display them.

imageList[1].Attach(new Gdiplus::Bitmap(L"TestImage.png"));

Solution

  • CAutoPtr<Gdiplus::Image> typedef GdiplusImagePtr;
    GdiplusImagePtr imageList[50];
    imageList[1].Attach(new Gdiplus::Bitmap(L"TestImage.png"));
    

    You can't use Gdiplus::Image in one place and Gdiplus::Bitmap in another palce.

    You can't use CAutoPtr for creating arrays in that way. Furthermore it seems you are declaring array of pointers as global values. This doesn't get freed until the end of the program. There is no point to do it this way, even if it works. Note that when the program exits the system will free all the memory. The goal should be to minimize memory usage while program is running.

    Use CAutoPtr for one object and use CAutoPtrArray for an array. But it's easier and more efficient to handle allocation and cleanup yourself. Example:

    std::vector<Gdiplus::Image*> list;
    LRESULT CALLBACK MyProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM)
    {
        switch (msg)
        {
    
        case WM_CREATE:
            list.push_back(new Gdiplus::Image(L"pic1.bmp"));
            list.push_back(new Gdiplus::Image(L"pic2.bmp"));
            return TRUE;
    
        case WM_NCDESTROY:
            //free memory here:
            for (auto e : list) delete e;
            break;
        ...
        }
    }
    

    For drawing

    case WM_PAINT:
    {
        PAINTSTRUCT ps = { 0 };
        HDC hdc = BeginPaint(hWnd, &ps);
        Gdiplus::Graphics graphics(hWnd); 
        graphics.DrawImage(list[0], 0, 0); 
        EndPaint(hWnd, &ps); 
        return 0; 
    }
    

    The cleanup is only one line. If you have older VS versions then use

    for (size_t i = 0; i < list.size(); i++) 
        delete list[i];
    

    If making a custom draw button you should handle WM_DRAWITEM in button's parent. Otherwise WM_PAINT in subclass will not work if theme is disabled (this applies to XP/Vista/Win7).

    You might also consider saving only image name, and then load/draw/unload in order to save memory.

    Also note:

    ReleaseDC(hWnd, GetDC(hWnd));
    DeleteDC(newDC);
    

    You should remove the above 2 lines. Please read the documentation on their usage.