[Webinar] Streamline your web hosting managementRegister Today

x
?
Solved

LPPICTUREDISP resource/memory leak

Posted on 2000-01-07
5
Medium Priority
?
688 Views
Last Modified: 2013-11-20
I am trying to create an ActiveX control that returns a DIB wrapped in an LPPICTUREDISP, but I am having memory problems.

I have a DIB loaded into memory and I am using CreateDIBitmap() to create a DDB that I construct a CBitmap from. The CBitmap is then wrapped into a CPicture and returned out of the ActiveX control. All is fine, I get the image correctly however I get a memory or resource leak! This obviously after a few images have been returned causes a serious problem. The "rogue" memory is allocated when CreateDIBitmap is called (the image is about 2Mb) and then never released. I'm sure it is because I am not calling DeleteObject(), but if I call DeleteObject() the data in the LPPICTUREDISP becomes invalid. How can I return the image correctly, and allow the container app to clean up the memory when it is finished with the image.

I'm sure this is because I am not calling DeleteObject() on the HBITMAP returned from CreateDIBitmap(), but if I do call DeleteObject() no image is returned. Where do I go from here?

Code snippet:

LPPICTUREDISP CActiveXMember::GetPicture(long index)
{
      if (index >= 0 && index < picture_list.GetSize())
      {
            CPictureHolder PicHolder;

            CBitmap* data_bitmap = CBitmap::FromHandle(picture_list[index]->GetTrueDDB());
            PicHolder.CreateFromBitmap(data_bitmap, NULL, TRUE);
            LPPICTUREDISP disp = PicHolder.GetPictureDispatch();

            return disp;
      }

      return NULL;
}

HBITMAP CBMPThumbNail::GetTrueDDB()
{
      int width, height, size, stored_size;

      ASSERT(index >= 0);
      ASSERT(index < cache_index.GetSize());

      DWORD file_pos = cache_index[index];
      DWORD stored_file_pos = cache.GetPosition();

      cache.Seek(file_pos, CFile::begin);

      cache.Read(&size, 4);
      cache.Read(&width, 4);
      cache.Read(&height, 4);
      
      // allocate some memory and read in the bits. Then these can be
      // returned to the calling function.
      //
      LPBYTE bits = (LPBYTE)GlobalAllocPtr(GHND, size);
      cache.Read(bits, size);

      // create a valid DDB ready to attach to an LPPICTUREDISPATCH
      // OLE object.
      //
      // set the info header to be the size of the actual image, and load
      // the image bits into this temporary pointer so that we can create
      // a bitmap handle to be returned.
      //

      info->bmiHeader.biWidth = width;
      info->bmiHeader.biHeight = height;
      stored_size = info->bmiHeader.biSizeImage;
      info->bmiHeader.biSizeImage = size;

      CClientDC dc(NULL);
      CPalette pal;
      CPalette* old_pal;
      HBITMAP bitmap;

      if (num_colours && dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
      {
            UINT size = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * num_colours);
            LOGPALETTE *log_palette = (LOGPALETTE *) new BYTE[size];

            log_palette->palVersion = 0x300;
            log_palette->palNumEntries = num_colours;

            for (int i = 0; i < num_colours; i++)
            {
                  log_palette->palPalEntry[i].peRed = info->bmiColors[i].rgbRed;
                  log_palette->palPalEntry[i].peGreen = info->bmiColors[i].rgbGreen;
                  log_palette->palPalEntry[i].peBlue = info->bmiColors[i].rgbBlue;
                  log_palette->palPalEntry[i].peFlags = 0;
            }

            pal.CreatePalette(log_palette);
            delete [] log_palette;

            old_pal = dc.SelectPalette(&pal, FALSE);
            dc.RealizePalette();
      }

      bitmap = CreateDIBitmap(dc.GetSafeHdc(),
                        (LPBITMAPINFOHEADER)info,
                        (LONG)CBM_INIT,
                        bits,
                        info,
                        DIB_RGB_COLORS);

      if (pal.GetSafeHandle())
      {
            dc.SelectPalette(old_pal,FALSE);
      }

      // restore the thumbnails width, height and image size.
      //
      info->bmiHeader.biWidth = THUMBNAIL_WIDTH;
      info->bmiHeader.biHeight = THUMBNAIL_HEIGHT;
      info->bmiHeader.biSizeImage = stored_size;

      // free the allocated bits.
      //
      GlobalFreePtr(bits);

      // restore the correct file position ready for adding the next
      // record.
      //
      cache.Seek(stored_file_pos, CFile::begin);

      return bitmap;
}
0
Comment
Question by:sdj
  • 3
  • 2
5 Comments
 
LVL 23

Accepted Solution

by:
chensu earned 600 total points
ID: 2333918
You should call picture_list[index]->GetTrueDDB() only once. What you can do is probably

1. In the CActiveXMember constructor or OnCreate, call picture_list[index]->GetTrueDDB() and save the handles to an array (a member of CActiveXMember).

2. In the CActiveXMember::GetPicture, retrieve the handle from the array.

3. In the CActiveXMember destructor or OnDestroy, call DeleteObject on each handle in the array.
0
 

Author Comment

by:sdj
ID: 2334656
Am I correct in thinking that I can call DeleteObject() after I have passed the LPPICTUREDISP back to the parent APP?

For example, in GetTrueDDB() I make the returned HBITMAP a member variable and if it is not NULL I call DeleteObject() before getting the new HBITMAP?

I have tried this and it seems to work, I am assuming that after the LPPICTUREDISP has been returned the parent app will manage the memory and I can then Delete my instance of it.
0
 
LVL 23

Expert Comment

by:chensu
ID: 2334860
According to the documentation,

"If bTransferOwnership is TRUE, the caller should not use the bitmap or palette object in any way after this call returns. If bTransferOwnership is FALSE, the caller is responsible for ensuring that the bitmap and palette objects remain valid for the lifetime of the picture object."

You are using TRUE, in which case it seems that you even should not call DeleteObject on the bitmap. If you are using FALSE, you should call DeleteObject only after the picture object is destroyed.
0
 

Author Comment

by:sdj
ID: 2335291
It just seems that this is a bit shortsighted, and there should be some way of doing it.

Lets assume you have an ActiveX control that can return an LPPICTUREDISP. In VB you can do:

image1.Picture = activex1.GetPicture()

After this there is no way to know when the VB program will have finished with the image, and you won't want to hold up any memory as the image may well be several MB. In my case there is a whole list of images so I must free up the memory, in fact I cache all the images out to a file and load them into memory on demand.

The bases of my ActiveX control is it is a list of DIBs, the list displays a thumbnail and the actual data is only loaded when returning it to the container application.
0
 
LVL 23

Expert Comment

by:chensu
ID: 2335383
One place to free the images is the destructor of the ActiveX control as I said before. Another way is to provide a method to free the images. It is up to the caller (the VB program) to call the method.
0

Featured Post

Take Control of Web Hosting For Your Clients

As a web developer or IT admin, successfully managing multiple client accounts can be challenging. In this webinar we will look at the tools provided by Media Temple and Plesk to make managing your clients’ hosting easier.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
In this video I will demonstrate how to set up Nine, which I now consider the best alternative email app to Touchdown.
Suggested Courses

613 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question