Solved

ActiveX control - Hdc/CDC etc..

Posted on 2001-08-03
13
675 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
Windows Server 2016: All you need to know

Learn about Hyper-V features that increase functionality and usability of Microsoft Windows Server 2016. Also, throughout this eBook, you’ll find some basic PowerShell examples that will help you leverage the scripts in your environments!

 
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
 
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

Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
c++ syntax question 9 49
Eclipse IDE - Cannot copy/paste from console output 8 193
Which Linux flavors will this run on? 6 88
VS2015 Redefinition errors 4 52
What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
C++ Properties One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property (http://www.experts-exchange.com/Programming/Languages/CPP/A_3912-Object-Properties-in-C.ht…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

778 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