heres my image class:

class image
{
private:
    ULONG_PTR m_gdiplusToken;
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    HDC hdcimage=CreateCompatibleDC(NULL);
    HGDIOBJ obj=NULL;
    HBITMAP btBitmap=NULL;
    Image *img;
    bool isimgused=false;
    int imageheight=0;
    int imageweight=0;
    int framecount=0;
    int intSelectFrame=0;
    int framedelay=0;
    string strfilename="";
    Gdiplus::Color clrBackColor=Gdiplus::Color::Transparent;
    HDC hdcwindow;
    bool blnTransparent=true;

    HICON HICONFromHBITMAP(HBITMAP bitmap)
    {
        BITMAP bmp;
        GetObject(bitmap, sizeof(BITMAP), &bmp);
        HBITMAP hbmMask = CreateCompatibleBitmap(GetDC(NULL), bmp.bmWidth, bmp.bmHeight);

        ICONINFO ii = {0};
        ii.fIcon    = TRUE;
        ii.hbmColor = bitmap;
        ii.hbmMask  = hbmMask;

        HICON hIcon = CreateIconIndirect(&ii);
        DeleteObject(hbmMask);

        return hIcon;
    }

public:

    image()
    {
        Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
        btBitmap=CreateCompatibleBitmap(hdcimage,1,1);
        obj = SelectObject(hdcimage, btBitmap);
    }

    image(HDC hdcWindow)
    {
        Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
        hdcwindow=hdcWindow;
    }

    image(int width, int height)
    {
        Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
        hdcimage = CreateCompatibleDC(NULL);
        btBitmap = CreateBitmap(width,height,1,32,NULL);
        obj = SelectObject(hdcimage, btBitmap);
        framecount=1;
        imageheight=height;
        imageweight=width;
    }

    image( const string & filename)
    {
        Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
        strfilename=filename;
        if(toupper(filename[filename.size()-3])=='C' && toupper(filename[filename.size()-2])=='U' && toupper(filename[filename.size()-1])=='R')
        {
            //create the transparent icon handle
            HCURSOR hicon = (HCURSOR)LoadImage(NULL, filename.c_str(), IMAGE_CURSOR, imageweight, imageheight, LR_LOADFROMFILE|LR_SHARED|LR_DEFAULTSIZE|LR_LOADTRANSPARENT);
            ICONINFO ii;
            BOOL fResult = GetIconInfo(hicon, &ii);
            if (fResult)
            {
                BITMAP bm;
                fResult = GetObject(ii.hbmMask, sizeof(bm), &bm) == sizeof(bm);
                if (fResult)
                {
                    imageweight= bm.bmWidth;
                    imageheight= ii.hbmColor ? bm.bmHeight : bm.bmHeight / 2;
                }
                if (ii.hbmMask) DeleteObject(ii.hbmMask);
                if (ii.hbmColor) DeleteObject(ii.hbmColor);
            }
            btBitmap=CreateBitmap(imageweight,imageheight,1,32,NULL);//create the bitmap with icon size
            obj = SelectObject(hdcimage, btBitmap);//add the bitmap to memory DC
            DrawIconEx(hdcimage,0,0,hicon,imageweight,imageheight,0,0,DI_NORMAL);//draw the icon to DC with right size
            //seems the DrawIcon(), always, draw it with 32X32 size
            framecount=1;
            DestroyCursor(hicon);
        }
        else
        {
            isimgused=true;
            Gdiplus::Image img2(towstring(filename).c_str());
            btBitmap=CreateBitmap(img2.GetWidth(),img2.GetHeight(),1,32,NULL);
            obj = SelectObject(hdcimage, btBitmap);
            Gdiplus::Graphics graphics(hdcimage);
            graphics.DrawImage(&img2, 0, 0, img2.GetWidth(), img2.GetHeight());
            imageweight=img2.GetWidth();
            imageheight=img2.GetHeight();
            UINT count = 0;
            count = img2.GetFrameDimensionsCount();
            GUID* pDimensionIDs = (GUID*)malloc(sizeof(GUID)*count);
            img2.GetFrameDimensionsList(pDimensionIDs, count);
            framecount=img2.GetFrameCount(&pDimensionIDs[0]);
            if (framecount>1)
                framedelay =img2.GetPropertyItemSize(PropertyTagFrameDelay);
            else
                framedelay =0;
            img=new Image(towstring(filename).c_str());
            free(pDimensionIDs);
        }
    }

    image (const image &cSource)
    {
        Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
        framecount=cSource.framecount;
        framedelay=cSource.framedelay;
        clrBackColor=cSource.clrBackColor;
        img=cSource.img->Clone();
        imageweight=cSource.imageweight;
        imageheight=cSource.imageheight;
        strfilename=cSource.strfilename;
        btBitmap=CreateBitmap(imageweight,imageheight,1,32,NULL);
        obj = SelectObject(hdcimage, btBitmap);
        BitBlt(hdcimage,0,0,imageweight,imageheight,cSource.hdcimage,0,0,SRCCOPY);
    }

    image& operator= (const image &cSource)
    {
        framecount=cSource.framecount;
        framedelay=cSource.framedelay;
        clrBackColor=cSource.clrBackColor;
        img=cSource.img->Clone();
        imageweight=cSource.imageweight;
        imageheight=cSource.imageheight;
        strfilename=cSource.strfilename;
        btBitmap=CreateBitmap(imageweight,imageheight,1,32,NULL);
        obj = SelectObject(hdcimage, btBitmap);
        BitBlt(hdcimage,0,0,imageweight,imageheight,cSource.hdcimage,0,0,SRCCOPY);
        return *this;
    }

    property <int> SelectFrame
    {
        Get(int)
        {
            return intSelectFrame;
        },
        Set(int selectframe)
        {
            intSelectFrame=selectframe;
            UINT count = 0;
            count = img->GetFrameDimensionsCount();
            GUID* pDimensionIDs = (GUID*)malloc(sizeof(GUID)*count);
            img->GetFrameDimensionsList(pDimensionIDs, count);
            img->SelectActiveFrame(&pDimensionIDs[0],intSelectFrame);
            Gdiplus::Graphics graphics(hdcimage);
            graphics.Clear(clrBackColor);
            graphics.DrawImage(img, 0, 0, img->GetWidth(), img->GetHeight());
            free(pDimensionIDs);
        }
    };

    property<int> FramesCount
    {
        Get(int)
        {
            return framecount;
        }
    };

    property<string> FileName
    {
        Get(string)
        {
            return strfilename;
        },
        Set(string filename)
        {
            if(isimgused==true)
                delete img;

            DeleteObject(btBitmap);
            DeleteObject(obj);
            strfilename=filename;
            if(toupper(filename[filename.size()-3])=='C' && toupper(filename[filename.size()-2])=='U' && toupper(filename[filename.size()-1])=='R')
            {
                //create the transparent icon handle
                HCURSOR hicon = (HCURSOR)LoadImage(NULL, filename.c_str(), IMAGE_CURSOR, imageweight, imageheight, LR_LOADFROMFILE|LR_SHARED|LR_DEFAULTSIZE|LR_LOADTRANSPARENT);
                ICONINFO ii;
                BOOL fResult = GetIconInfo(hicon, &ii);
                if (fResult)
                {
                    BITMAP bm;
                    fResult = GetObject(ii.hbmMask, sizeof(bm), &bm) == sizeof(bm);
                    if (fResult)
                    {
                        imageweight= bm.bmWidth;
                        imageheight= ii.hbmColor ? bm.bmHeight : bm.bmHeight / 2;
                    }
                    if (ii.hbmMask) DeleteObject(ii.hbmMask);
                    if (ii.hbmColor) DeleteObject(ii.hbmColor);
                }
                btBitmap=CreateBitmap(imageweight,imageheight,1,32,NULL);//create the bitmap with icon size
                obj = SelectObject(hdcimage, btBitmap);//add the bitmap to memory DC
                DrawIconEx(hdcimage,0,0,hicon,imageweight,imageheight,0,0,DI_NORMAL);//draw the icon to DC with right size
                //seems the DrawIcon(), always, draw it with 32X32 size
                framecount=1;
                DestroyCursor(hicon);
            }
            else
            {
                Gdiplus::Image img2(towstring(filename).c_str());
                btBitmap=CreateBitmap(img2.GetWidth(),img2.GetHeight(),1,32,NULL);
                obj = SelectObject(hdcimage, btBitmap);
                Gdiplus::Graphics graphics(hdcimage);
                graphics.DrawImage(&img2, 0,0,img2.GetWidth(),img2.GetHeight());
                imageweight=img2.GetWidth();
                imageheight=img2.GetHeight();
                UINT count = 0;
                count = img2.GetFrameDimensionsCount();
                GUID* pDimensionIDs = (GUID*)malloc(sizeof(GUID)*count);
                img2.GetFrameDimensionsList(pDimensionIDs, count);
                framecount=img2.GetFrameCount(&pDimensionIDs[0]);
                framedelay =img2.GetPropertyItemSize(PropertyTagFrameDelay);
                img=new Image(towstring(filename).c_str());
                isimgused=true;
                free(pDimensionIDs);
            }
        }
    };

    property<Gdiplus::Color> Backcolor
    {
        Get(Gdiplus::Color)
        {
            return clrBackColor;
        },
        Set(Gdiplus::Color bkcolor)
        {
            clrBackColor = bkcolor;
            SetDCBrushColor(hdcimage,clrBackColor.ToCOLORREF());
        }
    };

    void draw(HDC control)
    {
        //if (clrBackColor.GetValue() == Gdiplus::Color::Transparent)
        if (clrBackColor.GetValue()== Gdiplus::Color::Transparent)
        {
            TransparentBlt(control, 0, 0,width(),height(),hdcimage, 0, 0,width(), height(), GetPixel(hdcimage,width()-1,height()-1));
        }
        else
        {
            BitBlt(control,0,0,width(),height(),hdcimage,0,0,SRCCOPY);
        }
        //InvalidateRect(WindowFromDC(control),NULL,false);
    }

    operator HICON()
    {
        return HICONFromHBITMAP(btBitmap);
    }

    operator HBITMAP()
    {
        return btBitmap;
    }

    int height()
    {
        return imageheight;
    }

    int width()
    {
        return imageweight;
    }

    operator HDC()
    {
        return hdcimage;
    }

    bool operator != ( nullptr_t ) const
    {
        return btBitmap != nullptr;
    }

    bool operator==(const image &other) const
    {
        return (other.btBitmap == this->btBitmap);
    }

    bool operator!=(const image &other) const
    {
        return !(*this == other);
    }

    bool haveimage()
    {
        if(btBitmap==NULL)
            return false;
        else
            return true;
    }

    ~image()
    {
        Gdiplus::GdiplusShutdown(m_gdiplusToken);
        SelectObject(hdcimage, obj);
        DeleteObject(btBitmap);
        DeleteDC(hdcimage);
        if(isimgused==true)
            delete img;
    }
};

using the constructor:

image(int width, int height)
    {
        Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
        hdcimage = CreateCompatibleDC(NULL);
        btBitmap = CreateBitmap(width,height,1,32,NULL);
        obj = SelectObject(hdcimage, btBitmap);
        framecount=1;
        imageheight=height;
        imageweight=width;
    }

i'm trying the double buffer.
but the button(the 1st button) is drawed with big size:

case WM_DRAWITEM:
            {
                DRAWITEMSTRUCT *test=(DRAWITEMSTRUCT*) lParam;
                image imgButton(test->rcItem.right-test->rcItem.left,test->rcItem.bottom-test->rcItem.top);

                if(test->CtlType==ODT_BUTTON)
                {
                    SelectObject(imgButton,CreateSolidBrush(GetSysColor(COLOR_BTNFACE)));

                    HTHEME hTheme = OpenThemeData(inst->hwnd, L"BUTTON");
                    //fill the background with button face color
                    if ( test->itemState & ODS_SELECTED) // If it is pressed
                    {
                        FillRect(imgButton, &test->rcItem,(HBRUSH) GetSysColor(COLOR_BTNFACE));
                        DrawEdge(imgButton,&test->rcItem,EDGE_SUNKEN,BF_RECT); // Draw a sunken face
                    }
                    else if ( test->itemState & ODS_DISABLED)
                    {
                        FillRect(imgButton, &test->rcItem,(HBRUSH) CreateSolidBrush(RGB(192,192,192)));
                        DrawEdge(imgButton, &test->rcItem,EDGE_RAISED,BF_RECT);
                    }
                    else
                    {
                        FillRect(imgButton, &test->rcItem,(HBRUSH) GetSysColor(COLOR_BTNFACE));
                        DrawEdge(imgButton, &test->rcItem,EDGE_RAISED,BF_RECT); // Draw a raised face
                    }

                    RECT rectext=test->rcItem;
                    rectext.top=3;
                    rectext.left=3;

                    if ( test->itemState & ODS_DISABLED)
                    {
                        DrawHICONtoHDC(imgButton,CreateGrayscaleIcon(inst->imgtest),1,1);
                        DrawThemeText(hTheme, imgButton, BP_PUSHBUTTON, PBS_DISABLED,towstring(inst->strCaption).c_str(),-1,DT_LEFT|DT_HIDEPREFIX,0,&rectext);
                    }
                    else
                    {
                        DrawHICONtoHDC(imgButton, inst->imgtest,1,1);
                        SetTextColor(imgButton,inst->clrTextColor);
                        SetBkMode(imgButton,TRANSPARENT);//text background transparent
                        DrawText(imgButton,inst->strCaption.c_str(),-1,&rectext,DT_LEFT);
                    }

                    HPEN pen = CreatePen(PS_DOT,1,RGB(0,0,0));//these is the must close to the rectangle focus style
                    SelectObject(imgButton,pen);

                    if ( (test->itemState & ODS_FOCUS ) || (inst->blnMouseEnter==true)) // If the button is focused
                    {
                        int iChange = 3;
                        test->rcItem.top += iChange;
                        test->rcItem.left += iChange;
                        test->rcItem.right -= iChange;
                        test->rcItem.bottom -= iChange;
                        SelectObject(imgButton,GetStockObject(NULL_BRUSH));//the rectangle will be transparent
                        Rectangle(imgButton, test->rcItem.left, test->rcItem.top, test->rcItem.right, test->rcItem.bottom);
                    }
                    BitBlt(test->hDC,0,0,imgButton.width(), imgButton.height(), imgButton,0,0,SRCCOPY);
                    CloseThemeData(hTheme);
                }
            }
            break;

//inst is the button object pointer

maybe i have another problem with image destructor because the program takes time to end.
what i'm doing wrong?

Dani AI

Generated

Short answer: the button looked “too big” because the text/label was measured with the wrong DC/font and the code then used that measured size as a full window size without adding the non‑client (border/title) area. Measure the text with the same font the control uses, release any DCs you acquire, and compute the outer size with either AdjustWindowRectEx or by comparing client vs window rectangles.

Measure the caption correctly (select the control font into the HDC, use DT_CALCRECT or GetTextExtentPoint32, then release the DC). Example pattern (not present in the thread) — select WM_GETFONT, measure, restore and ReleaseDC:

HFONT hFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);
HDC hdc = GetDC(hwnd);
HFONT hOld = (HFONT)SelectObject(hdc, hFont);
RECT rc = {0,0,0,0};
DrawText(hdc, caption.c_str(), -1, &rc, DT_CALCRECT | DT_SINGLELINE);
SelectObject(hdc, hOld);
ReleaseDC(hwnd, hdc);
int textW = rc.right - rc.left;
int textH = rc.bottom - rc.top;

Get the border/non‑client metrics two safe ways:

  • Use AdjustWindowRectEx to compute outer window size for a desired client size (respects the window styles and extended styles).
  • Or compute per‑edge values at runtime by mapping client coordinates to screen and subtracting from GetWindowRect:
RECT wr, cr;
GetWindowRect(hwnd, &wr);
GetClientRect(hwnd, &cr);
POINT tl = {cr.left, cr.top}, br = {cr.right, cr.bottom};
ClientToScreen(hwnd, &tl); ClientToScreen(hwnd, &br);
int left = tl.x - wr.left, top = tl.y - wr.top;
int right = wr.right - br.x, bottom = wr.bottom - br.y;

Extra notes for : manage GDI/GDI+ lifetimes (startup/shutdown once per process if possible), always reselect the previous GDI object before deleting your newly created objects, ReleaseDC after GetDC, and be DPI‑aware (system metrics and borders change on high DPI). These fixes will stop incorrect sizing and reduce shutdown delays caused by leaked DCs/objects.

the problem was not the button, but the label.
heres how i get the control size:

//getting the text rectangle size
            RECT textrect={0};            
            DrawText (GetDC(hwnd),strCaption.c_str(),strCaption.size(),&textrect,DT_CALCRECT);

            //testing the image size
            //i add more 7 for get size for draw a border or something
            if(imgtest.haveimage())
            {
                if((textrect.bottom-textrect.top)<imgtest.height())
                    intHeight=imgtest.height()+7;
                if((textrect.right-textrect.left)<imgtest.width())
                    intWidth=imgtest.width()+7;
            }
            else
            {
                intHeight=textrect.bottom+7;
                intWidth=textrect.right+7;
            }

            //resize the control
            SetWindowPos(hwnd, 0, 0, 0,intWidth,intHeight,
                SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOCOPYBITS);

i have 1 question: how can i get the border size?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.