ActiveX control - Hdc/CDC etc..


I'm currently writing an ActiveX control which will respond to various methods by drawing on the screen.

As there could potentially be thousands of methods run on one control, I don't want to draw directly onto the window.

What I'd like to do is create a compatible bitmap when the control is first displayed, then all my methods will draw to this CDC, rather than the main one for the form.

After calling all my methods, I then want to just bitblt the control onto the main window from the DC held in memory.

I've been trying to do this for the past day, but still haven't solved it. My code is as follows in the ondraw event...

void CAcxCtrl::OnDraw(
               CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
     if (firstrun==1)
     TempCDC->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));    

Can someone please tell me what I'm doing wrong ??
LVL 10
Who is Participating?
>> I've recently read that GDI
>> objects are automatically deleted
>> by an idle monitor
Definitely not in windows 95 and 98.  I don't think this happens in windows NT and 2k either, however they are not so adversely affeted by GDI memory leaks as they have much larger GDI heaps.   Windows 95 and 98 can be quickly kiiled by a program with a GDI leak.

>>  I should store
>> a safe handle rather than a GDI object.
If you mean use a class to automate the release of the GDO object, yes I strongly agree.

Wait a second.  I think you are confused.

MFC allows you to create MFC objects, that is C++ classes, that are temporary objects.  It deletes these temporary objects when it returns to processing windows messsages.  This has no effect on the Windows OS.  This has no effect on the objects created by Windows, like a window handle or a GDI handle.  Code like

HBITMAP BmpHnd = CreateCompatibleBitmap( ... );
CBitmap *UseBMP = CBitmap::FromHandle(BmpHnd);

has a memory leak.   MFC will automatically delete the CBitmap object that it created, the one that UseBMP points to.  But the windows GDI bitmap object, that BmpHnd referrs to, is never deleted.

Be sure you keep this straight.

>> I initially had it so that it stored the
>> CBitmap and CDC objects globally,
That is definitely what I would do.

>> on Win98 / NT, the memory was being eaten away,
I don't think that was the cause fo the memory leak.  The memory leak cause is that you never select back the original bitmap.

Unless MFC does something special to prevent this from beign a problem, in the windows API this definitely is a problem.  When every you select a new object (pen, brush, bitmap etc) into a DC you must save the handle/pointer to the original object.  (This is returned by the Selectobject() function).  You must save this handle/pointer and then select back the original object before you delete or release the DC.  If you do not do this, you will have a memory leak--if you are lucky.  Worse, if you are not lucky.

You do


but do not save the pointer to the original bitmap.  You need to do this and then select it back in before releasing the dC, like

CBitmap *OldBMP = MemCDC->SelectObject(CBitmap::FromHandle(MemoryBitmap));

That should fix your memory leak.

Then you should be able to use global bitmap objects, which would be much nicer.
Unless there is more code that you didn't post I only see a portion of the necessary code here.

When you create a compatible DC it starts out with a 1 pixel "square" monochrome bitmap.  That is not a very big bitmap.

You must create a new bitmap of the right dimensions and color format.  (usualy using CreateCompatibleBitmap() with the DC you will copy into)  Then select this new bitmap into the memory DC AND be sure to save the handle returned by this operationn.  This is the handle to the old bitmap.

When you are done with the memory DC, you must select in the old bitmap.  then you can safely delete the new bitmap you created.
smeggheadAuthor Commented:

I've been reading up on this and found a few things I'm omitting.. I'll be trying these tomorrow and will get back to you.
Cloud Class® Course: Microsoft Office 2010

This course will introduce you to the interfaces and features of Microsoft Office 2010 Word, Excel, PowerPoint, Outlook, and Access. You will learn about the features that are shared between all products in the Office suite, as well as the new features that are product specific.

Even if it works, be sure to post the new code.  There is lots of room for mistakes in this and soemtimes they don't show up right away.  This is especially true of GDI memory leaks.
smeggheadAuthor Commented:

I've recently read that GDI objects are automatically deleted by an idle monitor and that I should store a safe handle rather than a GDI object.

So, what I've done is...

On the resize event..

     if (IsFirst==false)
     if (IsWindowVisible())
          CDC *ScreenCDC=GetDC();
          MemoryBitmap=(HBITMAP) UseBMP->GetSafeHandle();
          DrawAllCells; // this function creates a temporary CDC, compatible with the screen, selects the 'MemoryBitmap' into it, then does all the drawing, it then unselects the bitmap, and releases itself

where MemoryBitmap is a global HBITMAP.

Then, within the draw event..

     MemCDC=new CDC;
     delete MemCDC;

Does this seem right ???

I initially had it so that it stored the CBitmap and CDC objects globally, which resulted in no memory leaks in win2k, but on Win98 / NT, the memory was being eaten away, but invisibly to the IDE. I still can't determine what was wrong.. but this method seems to have sorted it.

smeggheadAuthor Commented:
ahhh, just when I thought I understood it all...

So, does the following make sense ??

1. I can declare a CDC and CBitmap object globally

2. In the class (usercontrol) create event, I set the CDC and CBitmap objects as compatible to the screen CDC. Then I select the new CBitmap object into the CDC, and store the old CBitmap object's handle for later destruction

3. When the user re-sizes the control, I create a new bitmap object (with the new dimensions), select it into the global CDC and delete the previous one.

4. In the class destructor, I get the old CBitmap object from the handle, and delete it.

I hope this is right..


PS. thanks for your help.
smeggheadAuthor Commented:
also.... the article I read, reads as follows

The complexity results from the temporary nature of GDI C++ object pointers returned by the selectobject function. (The temporary "object" will be destroyed by the application framework during the idle loop processing of the application, sometime after the handler function returns the call. See MFC Technical Note #3 in the online documentation.)

"Inside Visual C++" fourth edition - p.83
>> 1. I can declare a CDC and CBitmap object globally
Yes.  As far as I know.  I don't use MFC, but I am pretty familar with it.

Actually, if you need to maintain windows 95/98 compatibility store the bitmap globally.  Don't store the DC globally.  Windows 9x can create only a limited number of DC objects.  If programs "hold onto" them and use them up programs will not be able to get DCs needed for other drawign tasks and the system will by unable to redraw.  so I rcommend you store bitmap only and create the DC as needed and destroy it when no longer needed.  This is the standard practice.

>> Then I select the new CBitmap object into the
>> CDC, and store the old CBitmap object's handle
>> for later destruction
For 9x compatiblity.  Create the DC here.  Select in the global bitmap and save the old bitmap.  use the DC+bitmap.  select in the saved old bitmap and then destroy the DC.

>> When the user re-sizes the control, I create a
>> new bitmap object (with the new dimensions),
>> select it into the global CDC and delete the
>> previous one.
again for 9x, just resizes the global bitmap, there won't be a global DC to concern yourself with.

>> 4. In the class destructor, I get the old CBitmap object >> from the handle, and delete it.
Does MFC require you to do that?  If you created the bitnmap with CBitmap::CreateCompatibleBitmap() I would think that it would take care of deleting it.  (In the windows API you do have to delete it, so it must be deleted, but usually MFC takes care of things it creates.)

>> the temporary nature of GDI C++ object pointers
>> returned by the selectobject function. (The temporary
>>"object" will be destroyed by the application
>>framework during the idle loop processing of the
>> application, sometime after the handler function
>> returns the call.
Thsi is what I was saying.  The temporary objects are the C++ (MFC) objects.  NOT the windows GDI objects.  if you "give" MFC a windows object to "work with", like by using CBitmap::FromHandle() then MFC will create an MFC object for working with that windows object.  That MFC object is temporary and will be automatically deleted.  But the windows object is not under MFC control.  MFC doesn't know where it came from and doesn't know when or how to destroy it, so it does not destroy it, only the MFC object that it created.  

However if you use MFC to create an windows object.  i.e. if you use Cbitmap to create a windows bitmap object, then that bitmap is recorded in the MFC bitmap object and will be managed by MFc and deleted by MFC (as far as I know).
smeggheadAuthor Commented:
I've re-written the control.... but I'm still getting errors on a win98 PC, the error is

Object invoked has disconnected from it's client..

This error occurred a few times while I was debugging the control, like when I tried to select a NULL pointer into a DC.

Have you ever come across this error. I can't consistantly re-create it.. in fact it's very difficult to re-create, usually takes about 1/2 hr of just playing with the window / resizing it etc...


Post the code.

>> Object invoked has disconnected from it's client..
I suspect that is not talking about GDI objects, but instead your axtiveX object.   That is not my area of expertise.
smeggheadAuthor Commented:
Can I email you the code tomorrow... I'm not in the office at the moment. Can I use your address on your profile ?
You should be able to post it here unless it is very long.

Otherwise you can e-mail me at
smeggheadAuthor Commented:
Actually, the error hasn't occurred today, so it may be fixed... (fingers crossed)

Thanks for all your help on this q.
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.