Link to home
Start Free TrialLog in
Avatar of smegghead
smeggheadFlag for United Kingdom of Great Britain and Northern Ireland

asked on

ActiveX control - Hdc/CDC etc..

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 ??
Avatar of nietod
nietod

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.
Avatar of smegghead

ASKER

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

ASKER CERTIFIED SOLUTION
Avatar of nietod
nietod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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.
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).
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.
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.
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 nietod@journey.com
Actually, the error hasn't occurred today, so it may be fixed... (fingers crossed)

Thanks for all your help on this q.