Solved

HDC is not always working?

Posted on 1998-08-24
17
282 Views
Last Modified: 2013-12-04
Hi,

I am using NT 4.0.

I programmed sort of a plugin-system for my application (PlugIns are dynamically loaded DLLs).

Plugins may draw onto certain areas inside the applications's main window by using HWNDs of controls passed to them:

Main App -> createbutton -> HWND(of button) -> PlugIn
Plugin uses GetDC(HWND) and then draws...

This works fine. Almost always.

But there are situations, were nothing is visible on screen, although the HWND is valid and the DC is valid as well. (I am using DrawIconEx for example)

Sorry for not beeing able to clearify this "Situations" but it is rather hard. Example: Pressing CTRL+ALT+DELETE shows the lock screen under NT. When not returning to the normal screen, the whole app is beeing redrawed. I intercept the WM_PAINT and try to redraw my icon and nothing happens..

I even tried GetDCEx(hDrawWnd, 0, DCX_LOCKWINDOWUPDATE | DCX_PARENTCLIP); but no results.

Any ideas?


0
Comment
Question by:Mirko
  • 8
  • 5
  • 2
  • +2
17 Comments
 
LVL 2

Expert Comment

by:duneram
ID: 1413887
Are you remembering to ReleaseDC() on any ones that you are acquiring?  There is a limited amount of resources (DC's in this case).
0
 

Author Comment

by:Mirko
ID: 1413888
Yes I do remember. The drawing routine looks rather primitive:

            HDC hDC = GetDC(hDrawWnd);

            if (hDC)
            {
                  DrawIconEx(
                        hDC,
                        TraySlots[slot].x,
                        TraySlots[slot].y,
                        TraySlots[slot].hIcon,
                        16,
                        16,
                        0,
                        NULL,
                        DI_NORMAL
                  );
                  ReleaseDC(hDrawWnd, hDC);
            }


0
 

Author Comment

by:Mirko
ID: 1413889
New informations: I discovered, that the framework I am using (Allegris Views), first makes a "BeginPaint", before calling my code.
Is it possible, that GetDC returns trashy DCs, when called between a BeginPaint and a EndPaint?






0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1413890
Mirko you're right, I know this from experience on other WIndows versions.
0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1413891
BTW the fix is to pass a CDC or HDC into your plug-in as a parameter, which incidentally IMHO is probably better anyway, as it latter allows you to use the same plug-in code to paint to alternative places (e.g. the printer)

0
 

Author Comment

by:Mirko
ID: 1413892
Hi "Answers",

thank you for helping me, but I don't think passing a HDC is a good idea:
1. what if the framework calls a "BeginPaint" behind the back of the Plugin? It's HDC will become useless, won't it?
2. Is I know there are only limited HDCs available. When I have 20 plugins, the system has 20 open HDCs less...

What is a "CDC" ?




0
 
LVL 2

Expert Comment

by:duneram
ID: 1413893
I think the reference to CDC was the MFC wrap of the DC
0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1413894
"1. what if the framework calls a "BeginPaint" behind the back of the Plugin? It's HDC will become useless, won't it?"
.depends on how you do it...

In the app it would be best to have all your drawing in one place :-

In App (call this solution #1 cos I refer to it later)
a. BeginPaint...
b. Do the app drawing on the HDC from BeginPaint
c. Use this HDC and pass it to each plug in, in turn
d. EndPaint
e. Whenever you want to update the screen do InvalidateRect then UpdateWindow


ALTERNATIVE - OR (not as good)

Do as above for BeginPaint...EndPaint, for painting elsewhere in app do (call this solution #2)
a. GetDC
b. Call each plug in in turn with the _same_ HDC
c. ReleaseDC
[or repeat steps a->c for each call to the plug-in, that is to say GetDC/process-plug-in/ReleaseDC before doing the next plug-in]

ALTERNATIVE - OR (also not so good)
Do as solution #1 for BeginPaint..EndPaint except remove c.  After EndPaint do solution #2 a, b and c.


"2. Is I know there are only limited HDCs available. When I have 20 plugins, the system has 20 open HDCs less..."
-- in Win3.1 there's a limit of (I think) 5.  Don't know what the limit is in Win32, but in any case you shouldn't need more than one open.  Share the same DC between multiple plugins, or release the dc for a plug in before moving onto the next one


3. "What is a CDC ?"
CDC is the MFC C++ object which encapsulates an HDC.  If you're not using MFC ignore this comment (I now guess you're not).


0
Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

 
LVL 8

Expert Comment

by:Answers2000
ID: 1413895
One other thought, going back to the original question
"Plugins may draw onto certain areas inside the applications's main window by using HWNDs of controls passed to them: "

Have you considered letting the plug-ins subclass the controls in the app.  This way the plug in would have access to all the messages sent to these controls including WM_PAINT, mouse clicks and whatever else you needed.

0
 
LVL 3

Expert Comment

by:byang
ID: 1413896
Exactly when you app is not painting correctly ? Only when you press Ctrl-Alt-Del ?

BeginPaint() returns an HDC, if the frameworks calls BeginPaint() for you, it should provide a way for you to get this HDC. On the other hand, I don't think calling GetDC between BeginPaint and EndPaint should have any problems.


0
 
LVL 2

Expert Comment

by:milenvk
ID: 1413897
If you get a DC using GetDC() and try to paint in it you are looking for trouble. All the documents discussing Windows painting state that you need to call BeginPaint() and then use the returned DC. Everything else is undocumented and even if it works now it could be changed in future versions of the system. Answer200 had some good proposals. Why don't you do it in the right way?
0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1413898
Hi Mirko any news ?  

Thought I'd see if your making any more progress  [personally the subclassing idea is really growing on me - I think this is the _best_ answer (at least that I've seen so far) - do you need explanation on that].

milenvk - GetDC's okay provided it's not the DC/Window that's being processed by BeginPaint/EndPaint

0
 

Author Comment

by:Mirko
ID: 1413899
Hello Anwers,

yes, I'd gladly receive some tips on what you mean by subclassing and so on (especially in context of the "PlugIn System" Idea).

So by this comment I change the question. The question is now:

How can I implement a PlugIn -System (like described) based on dynamically loaded DLLs? The DLLs shall be able to paint onto the client area of the "host" or "parent" window.

(If possible, please explain a bit, because I never did windowing stuff on base level. Always used my framework to create windows...)

Thank you
  Mirko
0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1413900
I don't know the framework you're using so I hope this applicable, I'll and explain it at the API level:

Basically the idea of subclassing is that you can have more than window procedure process messages for a particular window.

The way this works is you setup your 2nd, 3rd, 4th etc. window procedure and you chain them together.

For example
1. you create a window called 'A', you have a procedure which handles it's messages called 'A1' (that lives in your EXE).  That's normal windows stuff right
2. However suppose you want a 2nd procedure (A2) which lives in a DLL to handle some of the messages for Window A.  Here's what you do you :
i. long lSave = SetWindowLong( hWnd_A, GWL_WINDPROC, A2 ) ;
This would set the window procedure to window A to be function A2.   This would mean that A2 could handle any messages it wanted, AND provided you keep lSave safe, you have the address of the original window procedure (A1) so you can still pass any messages A2 doesn't _completely_ handle to the original window procedure.
ii. When you're done with a Window (WM_DESTROY would be a good place), you can set back the window procedure to the original function using SetWindowLong( hWnd, GWL_WNDPROC, lSave ) ;

3. Now you want a 3rd window procedure A3 (in a second DLL).  Basically repeat the steps using SetWindowLong to add A3  into the chain, thus A3 get's first bite at the messages, and any messages it doesn't _completely_ handle get passed to A2.

4. Repeat ad infinitum.

The main thing to watch out for, being that you add new Window proc's to the chain in a particular order, and that when you remove them you do it in the exact reverse order.

Now if you look in a book or sample code, most examples of subclassing will apply to the built in Windows controls.  For example you might see an "edit" control being converted into a masked edit.  However there is no reason this technique can't be used on your own windows.


Now switching to a more sophisticated real world example, we could start to thing about what's wrong with this scenario...here's the problems that I'd see
(i) BeginPaint/EndPaint should only be called in by the procedure at the top of the subclassing chain.  Ideally you'd want to use the HDC you got back from BeginPaint all the way down the chain.  Now you work round or avoid this problem.  But for now let's just say it's an issue
(ii) Each Window proc is responsible for passing messages to the window procs below it in the chain, lot's of room for errors here.  One abherent plug-in could mess up the others quite easily.  Maybe you could live with this, if for example you intended to use the plug-in interface only yourself, but you might not be able to if other people
(iii)
0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1413901
Sorry my browser crashed - so I'll continue from where I left off (thankyou God for letting the first part submit - phew!)

.if other people are writing plug-ins too (now or in the future), plus it makes the DLLs hard to debug (a problem with one plug-in might not show up in that plug-in but cause problems with another one).
(iii) Not very extensible.

Now you could overcome the above problems, but it seems to me it may be better to centralize the logic to overcome these problems in one place.  I suggest a class which is your "plug-in manager" one instance of which lives in your EXE, and you "simulate" the subclassing effect in your code with a bit of refinement.

The plug-in manager would

1. maintain a list of all your plug-ins (address of their Window proc's or a message processing function in each DLL) and if you wanted could also be responsible for loading & unloading the DLLs.
2. When the Window proc in your app gets called, for all messages except WM_PAINT it tells the Plug-in Manager object about the message it received and gives the Plug-ins a chance to process the message.
3. For WM_PAINT the main EXE's Window procedure does the BeginPaint/EndPaint.  The DC it gets back from BeginPaint is passed to internal code, and then via the Plug-in manager to each plug-in's "pseudo" paint routine.
4. If you want to get sophisticated you could allow each plug-in to tell the plug-in manager that it "owns" a region of your window (so nobody else can paint to it, so two plug-ins can't fight over a region of the screen).  The Plug-in manager could even set clipping before calling a plug-in "pseudo" paint routine to enforce such rules.

Now to implement this you need each DLL to EITHER
(A) Include a standard set of routines to be exported (the functionality might vary, but the EXPORTS don't)
or
(B) Export a class/object with the same base class for all plug-ins.  The plug-in manager simply then manages a list of pointers to these plug-in objects.

For example you're _base_ class for a plug-in might be something like

class EXPORTEDCLASS1 CPlugInBase
{
public:
   CPlugInBase( CPlugInManager * pManager )
   {
      pManager->Register( this ) ;
      // tell the plug in manager we exist
   }

   virtual ~CPlugInBase()
   {
      pManager->Unregister(this) ;
      // tell the plug in manager we're done
   }

   virtual LRESULT ProcessMessage( ...etc... ) = 0 ;
   virtual LRESULT ProcessPaint( HDC hDC, ...etc... ) = 0 ;

   virtual int GetNumberOfRegions() const
     { return 0 ; } // default behaviour is to have no regions to paint

   virtual void GetRegionRect( int nRegion, LPRECT lpRect )
     { ASSERT(FALSE) ; } // not valid for a plug-in with no regions to be asked "what's the n'th region"
} ;

Each plug-in would derive a class from CPlugInBase containing its bit of functionality AND also export a function to "new" it's object (and if you want to be clean to delete it too), something like

CPlugInBase *  __declspec(dllexport) NewPlugIn( CPlugInManager * pManager ) ;

The Plug-in manager would keep track of all the Plug-in objects by storing an array (or map or linked list) of CPlugInBase's (actually pointers to their derived objects - polymorphism at it's best<g>).

class EXPORTEDCLASS2 CPlugInManager
{
// array of CPlugInBase * is a member variable
public:
    void Register( CPlugInBase * pBase ) ;
    {
       // adds an item to the array
    }

    void Unregister( CPlugInBase * pBase )
    {
      // removes item from the array
    }

    void ProcessMessage( ...etc... )
    {
      // passes a message thru to each plug-in in turn
    }
} ;

Couple of points to note
1. CPlugInManager class should live in a DLL (it can be instantiated in the EXE but as the way I set it up has the plug-ins calling it, they need to be able to import its class definition - if you want to avoid this, change the Registering method)

2. EXPORTEDCLASSn is a #define to export/import a class.  This is compiler dependant.  For VC5 something like

#ifdef BUILDING
#else EXPORTEDCLASS __declspec(dllexport)
#else
#else EXPORTEDCLASS __declspec(dllimport)
#endif

Where BUILDING is defined if your exporting from a class from a DLL, and not defined if you're importing the class from a DLL into your EXE or another DLL.

Hope this helps.  I love this stuff!
0
 

Author Comment

by:Mirko
ID: 1413902
Hello Answers,

thanks a lot! Your proposal is really great. I will implement my plugin-system exactly that way using a "PlugIn manager".

Would you be so kind konverting your "Comment" into an "Answer", so that I am able giving you the expert-points which you surely deserved?

Regards
  Mirko

0
 
LVL 8

Accepted Solution

by:
Answers2000 earned 300 total points
ID: 1413903
OK Mirko - this is a dummy answer as requested, real answer in the comments!

See you round, good luck with your app
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

This article describes a technique for converting RTF (Rich Text Format) data to HTML and provides C++ source that does it all in just a few lines of code. Although RTF is coming to be considered a "legacy" format, it is still in common use... po…
As more and more people are shifting to the latest .Net frameworks, the windows presentation framework is gaining importance by the day. Many people are now turning to WPF controls to provide a rich user experience. I have been using WPF controls fo…
The purpose of this video is to demonstrate how to set up the WordPress backend so that each page automatically generates a Mailchimp signup form in the sidebar. This will be demonstrated using a Windows 8 PC. Tools Used are Photoshop, Awesome…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…

747 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

13 Experts available now in Live!

Get 1:1 Help Now