This namespace contains the GDI resource management classes, as the alternative to one of the directly GDI32 APIs' calls. Note that the usage of the classes/functions in this namespace is very simple.
This solution handles GetDC/ReleaseDC, BeginPaint/EndPaint, SelectObject, GDI resource creators/ DeleteObject. It has solved the management holes that the librarys like MFC unsolved yet(i hope one day this part of the library could become the part of MFC). Whatever you organize your code for painting, the GDI-res management result is positive, no leaks, no exceptions, no wrong-placed resources(if incorrectly calls SelectObject, for example.). In worst case, your code will become uncompiledable until you fixed the problems left.

Components:

CGdiObjMgr:

Maintains a GDI objects. You can define your object in the form of:
CGdiObjMgr<[Handle]> obj;
the [Handle] could be one of HBITMAP / HPEN / HBRUSH / HFONT (without brackets).

CDCMgrs:

Maintains a DC handle. As the replacement of the direct calls' to DC APIs. Used to define your object like this:
CDCMgrs<[old gdi object types' lists]> oYourDc;

CSelectGoToDc:

The replacement of the direct calls' to SelectObject. Used to define your object like this:
CSelectGoToDc<[The gdi object types that will be proceeded], [old gdi object types' lists*]> oseler(odc);
* The lists must be exactly the same to those for declaring the odc.

Usages/Sample:

Assumes that there is code here you would have written:
{
    PAINTSTRUCT ps;
    HPEN hpn = ::CreatePen(PS_SOLID, 1, 0xff00ff);
    HDC hdc = ::BeginPaint(hwnd, &ps);

    if(hpn != NULL) // N1.
    {
        HPEN hpnOld = (HPEN)::SelectObject(hdc, hpn); // N3.

        if(hpnOld != NULL) // N2.
        {
            ::MoveToEx(hdc, 0, 0, NULL);
            ::LineTo(hdc, 400, 300, NULL);
            ::SelectObject(hdc, hpnOld); // N7.
        }
        else
            return;// ** N5.
    }
    ::EndPaint(hwnd, &ps); // N6.
    ::DeleteObject(hpn); // N4.
}

I would say that the Line N1, N2 would be discarded by many coders. The Line N4, N6 and N7 is always forgotten like in Line N5, which causes the resource leaking. Still, It's possible that someone may wrong place the Line N7 after N6.
When the painting code is large, the coders couldn't provide everything right in a short while. I used the MFC and found that it can't fix user's wrong use of SelectObject calls in some special cases.

Now we can write the code like this:
{
    ::nsAssistantEnhX::nsAutoGdiProcessor::CPaintClientDCMgr<a> odc(hwnd);
    ::nsAssistantEnhX::nsAutoGdiProcessor::CGdiObjMgr<HPEN> opn;
    ::nsAssistantEnhX::nsAutoGdiProcessor::CSelectGoToDc<HPEN, a> opnsel(odc);

    opn = ::CreatePen(PS_SOLID, 1, 0xff00ff);
    if(opn != NULL) // N1.
    {
        if(opnsel.ReSelectGoToDc(opn)) // N2.
        {
            ::MoveToEx(odc, 0, 0, NULL);
            ::LineTo(odc, 400, 300, NULL);
        }
        else
            return;// N5.
    }
}
The 'a's shall be replaced by 'HPEN's there. If there are more than one GDI object types were used, all the 'a's in that code must be identical.
{
    ::nsAssistantEnhX::nsAutoGdiProcessor::CPaintClientDCMgr<HPEN,HBRUSH> odc(hwnd);
    ::nsAssistantEnhX::nsAutoGdiProcessor::CGdiObjMgr<HPEN> opn;
    ::nsAssistantEnhX::nsAutoGdiProcessor::CGdiObjMgr<HBRUSH> obsh;
    ::nsAssistantEnhX::nsAutoGdiProcessor::CSelectGoToDc<HPEN, HPEN,HBRUSH> opnsel(odc);
    ::nsAssistantEnhX::nsAutoGdiProcessor::CSelectGoToDc<HBRUSH, HPEN,HBRUSH> obshsel(odc);

    obsh = ::CreateSolidBrush(0x00ffff);
    if(obsh != NULL)
    {
        if(obshsel.ReSelectGoToDc(obsh))
        {
            ::Rectangle(odc, 0, 0, 400, 400);
        }
        //else
            //return;
    }
    opn = ::CreatePen(PS_SOLID, 1, 0xff00ff);
    if(opn != NULL)
    {
        if(opnsel.ReSelectGoToDc(opn))
        {
            ::MoveToEx(odc, 0, 0, NULL);
            ::LineTo(odc, 400, 300, NULL);
        }
        //else
            //return;
    }
}
Wait a second: Maybe you would ask why the DC classes in the library take GDI objects' bodies? Is that rigorous in concept? Just remember that a DC object takes several(default) GDI objects itself, you can swap them out by passing other valid GDI objects you created to the SelectObject and then you have to return all the default objects back before the DC object being unlocked.

general cases

Remarks:

1. CSelectGoToDc for HRGN is disabled:

Regions are also gdi objects. However they would never be 'selected' in to a device-context realy, An HRGN object could be deleted as long as you feel you won't need it again. Normatively, An HRGN should be 'select into/out from' a DC by calling SelectClipRgn function instead of SelectObject.

2. Gain control access to another GDI object with a CGdiObjMgr object:

Recommended and supported for the new versions only: There are several ways to make this happen:
- Use the operator '=' to bind current CGdiObjMgr object to the handle to new GDI object. This will cause the GDI object bound previously being deleted by the CGdiObjMgr if the second template-parameter of the CGdiObjMgr for CGdiObjMgr object is not nsGdiObjMgrCategory::id_LockOnly.
- Use the SetNewObject() to bind current CGdiObjMgr object to the handle to new GDI object. The operation will not be happen realy if NULL is passed to SetNewObject.
- Calling SwapOut() to reset current CGdiObjMgr object to the empty state and unlink any selection to the CGdiObjMgr, which causes the GDI object decontroled by. At last the method returns the handle to the last boundled GDI object. That GDI object has no relation to the CGdiObjMgr object and will be proceeded on your own risk.

3. A CGdiObjMgr object can be created before and after the painting procedure being called(even when not be called), But CDCMgr objects can't (except for creating CResCompatibleDCMgr::CDeploy objects).

4. A CGdiObjMgr object can be defined before or after its CSelectGoToDc selecter. However, BEFORE its CSelectGoToDc selector is recommended because the performance will not be down by looping obj-selection-chain.

5. If you don't like GDI object deleted by CGdiObjMgr's destructor(which generaly shouldn't be in a well-designed projects if the painting procedure were fully controled by your code; but may be necessary in special cases. For example, a dialog-box may take its own font resource that is neither system-default nor explicitly created by user), Specify the second template-parameter of the CGdiObjMgr the nsGdiObjMgrCategory::id_LockOnly for a CGdiObjMgr object definition.

6. Using CDCMgr templates properly:

CPaintClientDCMgr is for BeginPaint/EndPaint;
CClientDCMgr is for GetDC/ReleaseDC;
CCompatibleDCMgr and CResCompatibleDCMgr is for CreateCompatibleDC/DeleteDC;
CDCMoutedMgr is prepared for NATIVE hdc handle! You must NOT use any CDCMgr class in this library as its constructor's parameter!

7. In your code, Don't mess your code the direct GDI/DC API calls with this library. This library is manager(as its name, assistant), not sealer. If you write your own function that accept DC as the parameter(s), don't use HDC as the parameter(s) type, use any of the followings:

the original template class type
the CDCBaseMgr template class type that inherited by original template class
the CDCBaseMgr template class' sub-object type that inherited by original template class(by calling SafeExtractInherited()).

Last edited Aug 7, 2011 at 1:48 PM by UnitUniverse, version 10

Comments

No comments yet.