Solved

ActiveX control - Hdc/CDC etc..

Posted on 2001-08-03
13
664 Views
Last Modified: 2010-05-18
Hi,

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->CreateCompatibleDC(pdc);
        firstrun=0;
        }
     TempCDC->BeginPath();
     TempCDC->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));    
     TempCDC->Ellipse(rcBounds);
     TempCDC->EndPath();
     pdc->BitBlt(1,1,rcBounds.bottom,rcBounds.right,TempCDC,1,1,SRCCOPY);



Can someone please tell me what I'm doing wrong ??
0
Comment
Question by:smegghead
  • 7
  • 6
13 Comments
 
LVL 22

Expert Comment

by:nietod
ID: 6350625
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.
0
 
LVL 10

Author Comment

by:smegghead
ID: 6351285
Thanks,

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.
0
 
LVL 22

Expert Comment

by:nietod
ID: 6351344
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.
0
 
LVL 10

Author Comment

by:smegghead
ID: 6387619
Hi,

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)
     {
          UseBMP=CBitmap::FromHandle(MemoryBitmap);
          UseBMP->DeleteObject();
     }
     if (IsWindowVisible())
     {
          CDC *ScreenCDC=GetDC();
          UseBMP->CreateCompatibleBitmap(ScreenCDC,cx,cy);
          ReleaseDC(ScreenCDC);
          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
          IsFirst=false;
     }


where MemoryBitmap is a global HBITMAP.

Then, within the draw event..

     MemCDC=new CDC;
     MemCDC->CreateCompatibleDC(pdc);
     MemCDC->SelectObject(CBitmap::FromHandle(MemoryBitmap));
     pdc->BitBlt(0,0,rcBounds.right,rcBounds.bottom,MemCDC,0,0,SRCCOPY);
     ReleaseDC(MemCDC);
     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.

0
 
LVL 22

Accepted Solution

by:
nietod earned 150 total points
ID: 6387731
>> 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

MemCDC->SelectObject(CBitmap::FromHandle(MemoryBitmap));

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));
pdc->BitBlt(0,0,rcBounds.right,rcBounds.bottom,MemCDC,0,0,SRCCOPY);
MemCDC->SelectObject(OldBmp);
ReleaseDC(MemCDC);

That should fix your memory leak.

Then you should be able to use global bitmap objects, which would be much nicer.
0
 
LVL 10

Author Comment

by:smegghead
ID: 6387789
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..

Smg...

PS. thanks for your help.
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 
LVL 10

Author Comment

by:smegghead
ID: 6387802
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
0
 
LVL 22

Expert Comment

by:nietod
ID: 6387874
>> 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).
0
 
LVL 10

Author Comment

by:smegghead
ID: 6388832
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...

Thanks..

Smg.
0
 
LVL 22

Expert Comment

by:nietod
ID: 6389358
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.
0
 
LVL 10

Author Comment

by:smegghead
ID: 6393899
Can I email you the code tomorrow... I'm not in the office at the moment. Can I use your address on your profile ?
0
 
LVL 22

Expert Comment

by:nietod
ID: 6393919
You should be able to post it here unless it is very long.

Otherwise you can e-mail me at nietod@journey.com
0
 
LVL 10

Author Comment

by:smegghead
ID: 6393961
Actually, the error hasn't occurred today, so it may be fixed... (fingers crossed)

Thanks for all your help on this q.
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Unlike C#, C++ doesn't have native support for sealing classes (so they cannot be sub-classed). At the cost of a virtual base class pointer it is possible to implement a pseudo sealing mechanism The trick is to virtually inherit from a base class…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

746 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

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now