Prev: Posting 60 messages per second to CView locks up UI (animation continues...)
Next: Turning on ClearType for TextOut() ???
From: Peter Olcott on 16 Mar 2010 09:13 "Joseph M. Newcomer" <newcomer(a)flounder.com> wrote in message news:vuetp51t69g884pi2lb6k5ibdbnqve3upp(a)4ax.com... > It is very, very simple: you, and you alone, are > responsible for knowing what objects are > selected into a DC. If an object is selected into a DC, > you must deselect it. You should > not use the DC as a "repository" of any object; the > simplest approach is to start with a > DC which has nothing active, select into it what you need > to do drawing, do the drawing, > deselect all the objects (RestoreDC()), and repeat as > required. It is considered poor > practicce to leave an object selected into a DC across, > say, an message pump invovation, > because temporary objects exhibit odd behavior under such > conditions. This is just good > programming practice. > joe > I want to minimize the complexity of the code, I want to minimize human user actions. To minimize the complexity of the code I have a minimum number of objects and I perform the minimum number of operations upon these objects to achieve the required functional result. I have exactly one CBitmap, CFont, and MemoryDC. Once a user has performed an operation upon these objects I want this operation to stick until the user performs another operation upon them. (These operations can occur in any order or sequence) I implemented this requirement by make these GDI objects the members of the CDialog instance. The user may load a graphics file into the MemoryDC's CBitmap, capture the screen to the bitmap, save whatever is in the bitmap to a graphics file, or write to whatever is in the bitmap using TextOut(). The user may do these operations in any order or sequence. A default 100 * 100 bitmap is created when ScreenCapture object that does all of these operations is created. The trick of always deselecting the currently selected GDI object is too difficult to implement and not required for the following reasons: (1) The only way to unselect a CBitmap is to select another CBitmap. (2) The other CBitmap does not exist until I create it, and to minimize program complexity there is only one of these. (3) So (according to your requirements) I can't create a CBitmap without deleting the original CBitmap, and I can't delete the original CBitmap without creating a new CBitmap. (4) Apparently I can delete the original CBitmap while it is selected into a MemoryDC, because DeleteObject() returns a 1 indicating successful deletion. > On Sun, 14 Mar 2010 12:22:49 -0500, "Peter Olcott" > <NoSpam(a)OCR4Screen.com> wrote: > >>The point is that the function listed below can be called >>repeatedly, and it does not work if the >> this->font.DeleteObject(); >>is removed which as far as I can tell must mean that a GDI >>object is definitely being deleted while selected in a >>device context. Proof that a GDI object is being deleted >>while selected into a device context contradicts Joe's >>statement that it can't be done. >> >>What I suspect is there is something that I am missing >>here, >>and Joe is not wrong, yet the explicit contradiction still >>remains unresolved. >> >>"Goran" <goran.pusic(a)gmail.com> wrote in message >>news:bc8e5ce9-773d-42df-b509-097b70e021d1(a)33g2000yqj.googlegroups.com... >>On Mar 12, 4:07 pm, "Peter Olcott" <NoS...(a)OCR4Screen.com> >>wrote: >>> I will study your concrete example an attempt to >>> implement >>> it. My great difficulty is with the tedious little >>> syntactical details, thus Joe's abstract examples don't >>> work >>> for me. I can't simply let my GDI object get destroyed >>> because the objects must retain their state in case >>> subsequent operations must be performed on them. >> >>I see in your code below that you use a memory DC. Without >>having any >>further info, only thing I have to say is that normally a >>memory DC is >>used to draw something, then e.g. stored in a bitmap or >>BitBlt-ed on >>screen. From that standpoint, normal drawing we all know >>is >>to select >>your GDI objects into the DC, call drawing code, then >>un-select them. >>If that is what you are doing, then you have no >>correlation >>between >>your GDI objects (e.g. bitmaps, pens, fonts, brushes) >>except >>that they >>have to outlive time when they are selected into the DC >>for >>drawing. >>So I honestly do not see your difficulty. Just make sure >>that, during >>the drawing code, you do stuff by the book (hence e.g. my >>CTempGdiObjectSelection), and as for lifetime of your GDI >>object, >>well, just handle their lifetime (hence my >>shared_ptr-based >>map >>there). >> >>> I open a bitmap and it may be written to, or saved or >>> another bitmap may be opened. If another bitmap is >>> opened, >>> I >>> must create another CBitmap object of this new size. >>> Currently I only have the single CBitmap object as an >>> object >>> member variable. >> >>So make that two of them. Note also that there's nothing >>wrong in >>creating whatever GDI object you might need on the heap. >> >>> I am guessing that I could implement your design by >>> making >>> pairs of GDI object member and toggling between them >>> upon >>> every re-use. For example CBitmap cbitmap[2]; and then >>> toggle its subscript between one and zero. >> >>Here, either I am not very smart, either there's a lot of >>things you >>have in your head that weren't put into writing here, >>because I don't >>understand what you are talking about :-). >> >>> I looked at your design again and there were too many >>> things >>> that I did not understand. >> >>I'd say, points to take from it: >> >>1. use something like CTempGdiObjectSelection to ensure >>correct GDI >>object selection idiom, which is "select it/draw/un-select >>it". >> >>2. use some C++ object lifetime handling technique to >>handle >>your GDI >>object (font, bitmap, pen...) lifetime. Hence I proposed >>shared_ptr - >>it gets you far. If you indeed use a std::map, attention >>to >>map::operator[], that might not do what you think (hence >>my >>insistence >>on that "const"). >> >>3. divorce lifetime of a DC and GDI objects used to draw >>on >>it (you >>seem to be having trouble with this). In other words, when >>you select >>an object into a DC, do not try to destroy it. But I don't >>understand >>why you even started down that route. >> >>> Here is my currently working code. It does not work if I >>> remove the: >>> this->cfont.DeleteObject(); >>> It does work with repeated invocations, thus must be >>> deleting the selected CFont object. >>> >>> // Displays a Font Selection DialogBox with >>> // default "Times New Roman", Bold, Italic, 8 Point >>> // >>> inline void ScreenCaptureType::SelectFont() { >>> this->cfont.DeleteObject(); >>> >>> int PointSize = 8; >>> int FontHeight = -MulDiv(PointSize, >>> GetDeviceCaps(MemoryDC.m_hDC, LOGPIXELSY), 72); >>> LOGFONT LogFont = {FontHeight, 0, 0, 0, FW_BOLD, 1, 0, >>> 0, >>> ANSI_CHARSET, OUT_DEFAULT_PRECIS, >>> CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | >>> FF_DONTCARE, L"Times New Roman"}; >>> CFontDialog dlg(&LogFont); >>> if (dlg.DoModal() == IDOK) >>> cfont.CreateFontIndirect(&LogFont); >>> this->MemoryDC.SelectObject(&cfont); >>> >>> } >> >>Without knowing anything about the rest of your code, the >>purpose of >>this function is to select some font into a memory DC >>(providing that >>MemoryDC indeed is a CMemoryDC). Normally, one selects a >>font into a >>DC to do some drawing, so I personally find it hugely >>surprising that >>a modal dialog is displayed just before selecting it into >>a >>DC. I see >>that working well in some bigger design only if said font >>is >>the only >>font that will be used while drawing. But, as I said >>before, >>I don't >>actually understand your difficulty at all, so I might be >>completely >>off the mark. >> >>By the way, please, PLEASE do something about that >>CreateFontIndirect >>call. It stands out like a sore thumb. It's a friggin >>resource >>allocation and that can fail (if so, it returns FALSE). >>Imagine that >>this indeed happens. Would you want to do >>MemoryDC.SelectObject(&cfont) then? >> >>Goran. >> > Joseph M. Newcomer [MVP] > email: newcomer(a)flounder.com > Web: http://www.flounder.com > MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Goran on 17 Mar 2010 04:25 On Mar 16, 1:54 pm, "Peter Olcott" <NoS...(a)OCR4Screen.com> wrote: > It looks like all of this may be moot > because DeleteObject() is confirming that the object is > being deleted while it is selected by its return value of 1, > so my code is already good the way that it is. I only tested > this with VS 2008, but, it works on Windows 7 and XP. Man, Joe is telling you repeatedly that you are mistaken. I told you to try some GDI resource monitor program and see whether your GDI objects indeed do get destroyed. I now believe that you didn't try that and that you don't actually know more about that except DeleteObject returned true. That is NOT ENOUGH. But fine, don't trust us. Try this: start your task manager and add "GDI objects" column. Make a test program where you create (or get) a DC, then create a font, select it into a DC, and then call DeleteObject on it. Watch GDI object count for your process. Did you see that count dropped by 1 when you called DeleteObject? No you haven't, despite the fact that DeleteObject returned TRUE. BTW, if you do this, you WILL see that GDI object count drops, but only when your DC (not font, DC) is destroyed. So that might mean that some sort of reference counting is employed for fonts, or it might mean absolutely NOTHING, because, AFAIK, none of that is actually documented. Now, do the same for e.g. a pen. There, you will see that GDI object count indeed does drop. This is in line with doc for DeleteObject who says "Do not delete a drawing object (pen or brush) while it is still selected into a device context.". That, IMO, hints at the following: if you delete e.g. a pen, and then do LineTo, what pen is a DC supposed to use to draw? There is no pen anymore! Perhaps situation is different for a font, but wouldn't you say now that it's a mess playing with DeleteObject like that? Just unselect it before destruction and that's all. So what you choose to do, is to distrust the documentation and disregard honest advice of people you often see here, and to rely on undocumented behavior that you observe in your particular use case. Do you actually think that is a good way to write code? If so, then perhaps you don't deserve to be helped. Goran.
From: Tom Serface on 17 Mar 2010 08:52 Funny, I thought starting to use .NET would help with some of this stupid Windows behavior. Not so. My first real .NET application sucked up GDI resources because I was creating Image objects and not specifically Dispose()'ing them. Turns out they don't give back handles even when garbage collected if you don't do that manually. The worse thing is if you have another thread and it tries to get resource that has been garbage collected, that you haven't destroyed, it will just throw an "out of memory" exception which causes you to go down a whole complete red herring path while debugging. I've found managing these sorts of resource "trickies" to be some of the most annoying parts of coding multimedia software. I can't blame Peter for being a little confused about it. The whole font creation and usage thing in MFC is just too cumbersome. That is something that .NET got right. You just use the font (properties or otherwise) and you don't have to worry about destroying it or whether it's still available or ... Tom "Goran" <goran.pusic(a)gmail.com> wrote in message news:a3fc4a6b-0a96-4674-88d4-d8e3caf79120(a)m37g2000yqf.googlegroups.com... > On Mar 16, 1:54 pm, "Peter Olcott" <NoS...(a)OCR4Screen.com> wrote: >> It looks like all of this may be moot >> because DeleteObject() is confirming that the object is >> being deleted while it is selected by its return value of 1, >> so my code is already good the way that it is. I only tested >> this with VS 2008, but, it works on Windows 7 and XP. > > Man, Joe is telling you repeatedly that you are mistaken. I told you > to try some GDI resource monitor program and see whether your GDI > objects indeed do get destroyed. I now believe that you didn't try > that and that you don't actually know more about that except > DeleteObject returned true. That is NOT ENOUGH. > > But fine, don't trust us. Try this: start your task manager and add > "GDI objects" column. Make a test program where you create (or get) a > DC, then create a font, select it into a DC, and then call > DeleteObject on it. Watch GDI object count for your process. Did you > see that count dropped by 1 when you called DeleteObject? No you > haven't, despite the fact that DeleteObject returned TRUE. > > BTW, if you do this, you WILL see that GDI object count drops, but > only when your DC (not font, DC) is destroyed. So that might mean that > some sort of reference counting is employed for fonts, or it might > mean absolutely NOTHING, because, AFAIK, none of that is actually > documented. > > Now, do the same for e.g. a pen. There, you will see that GDI object > count indeed does drop. This is in line with doc for DeleteObject who > says "Do not delete a drawing object (pen or brush) while it is still > selected into a device context.". That, IMO, hints at the following: > if you delete e.g. a pen, and then do LineTo, what pen is a DC > supposed to use to draw? There is no pen anymore! Perhaps situation is > different for a font, but wouldn't you say now that it's a mess > playing with DeleteObject like that? Just unselect it before > destruction and that's all. > > So what you choose to do, is to distrust the documentation and > disregard honest advice of people you often see here, and to rely on > undocumented behavior that you observe in your particular use case. Do > you actually think that is a good way to write code? If so, then > perhaps you don't deserve to be helped. > > Goran.
From: Goran on 19 Mar 2010 06:39 On Mar 18, 7:43 pm, "Peter Olcott" <NoS...(a)OCR4Screen.com> wrote: > Since I have no idea what an HFONT is (other than a handle > to a font) or how it works, knowing this does not help. > Learning MFC programming in terms of Win32 programming only > works if you know Win32 programming. That's a silly thing to say, and thinking that way won't get you far with MFC. MFC really is a rather thin layer over Win32. That influences very much how it's made and what it does. And indeed, if you look at docs for MFC, there is often corresponding Win32 function link at the end, and doc for it is pretty much the same then. For example, you work with LOGFONT. That has nothing to do with MFC and all to do with Win32. So you in fact do Win32, not MFC, programming, when you handle it. Your trouble here with selecting stuff into DC stems from poorly understanding both Win32 and MFC in that area. What's worse, your "functional requirements" you throw around are wrong because of that. You just shouldn't be doing this. There's no abstraction that doesn't leak (see http://en.wikipedia.org/wiki/Leaky_abstraction), and MFC is indeed an abstraction that leaks a lot, probably more than it has to. If you want stronger abstractions, use a better framework. Goran.
From: Joseph M. Newcomer on 19 Mar 2010 12:20
I agree. I have many slide disbributed through my Systems Programming course, which cover what happens when the Win32 API meets MFC, and it is not not always obvious what happens unless you understand both the API and MFC. And not at a superficial level. You can't really understand MFC unless you understand the concept of handle maps and temporary objects. Yes, I successfully worked in it for two years before I got the deep insights, but I found that they were critical to my future success. Now I teach how to use both STL and MFC collections at the interfaces. joe On Fri, 19 Mar 2010 03:39:55 -0700 (PDT), Goran <goran.pusic(a)gmail.com> wrote: >On Mar 18, 7:43�pm, "Peter Olcott" <NoS...(a)OCR4Screen.com> wrote: >> Since I have no idea what an HFONT is (other than a handle >> to a font) or how it works, knowing this does not help. >> Learning MFC programming in terms of Win32 programming only >> works if you know Win32 programming. > >That's a silly thing to say, and thinking that way won't get you far >with MFC. > >MFC really is a rather thin layer over Win32. That influences very >much how it's made and what it does. And indeed, if you look at docs >for MFC, there is often corresponding Win32 function link at the end, >and doc for it is pretty much the same then. > >For example, you work with LOGFONT. That has nothing to do with MFC >and all to do with Win32. So you in fact do Win32, not MFC, >programming, when you handle it. Your trouble here with selecting >stuff into DC stems from poorly understanding both Win32 and MFC in >that area. What's worse, your "functional requirements" you throw >around are wrong because of that. > >You just shouldn't be doing this. There's no abstraction that doesn't >leak (see http://en.wikipedia.org/wiki/Leaky_abstraction), and MFC is >indeed an abstraction that leaks a lot, probably more than it has to. >If you want stronger abstractions, use a better framework. > >Goran. Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm |