From: Doug Harrison [MVP] on 17 Jun 2005 12:21 On Fri, 17 Jun 2005 08:52:27 -0700, Dan Mitchell wrote: > What will happen to MFC if we _don't_ have a main window? I don't know. I've always found it best just to give MFC what it wants. :) (Seriously, I don't have any more information on that than you went on to talk about.) -- Doug Harrison Microsoft MVP - Visual C++
From: Joseph M. Newcomer on 17 Jun 2005 13:05 See below... On Fri, 17 Jun 2005 08:33:03 -0700, Dan Mitchell <djmitchella(a)yahoo.com> wrote: >Joseph M. Newcomer <newcomer(a)flounder.com> wrote in >news:nva4b15jc6ve5h00su82g80kaid54cj7ag(a)4ax.com: >> Do not delete the main window for your app like this. That would be >> erroneous programming. If you are deleting the main window, you have >> committed a serious breach of programming style. Do not at all be >> surprised if anything collapses into a mess after that. Don't do it. >> If you are closing your main window using this technique, your program >> is incorrect. There is no reason to expect an erroneous program should >> exhibit anything resembling sensible behavior under any conditions. > > Fair enough; I'd suspected something like this was the case. > >> If you want to exit a dialog-based program, call CDialog::OnOK(). Or >> maybe OnCancel. Do not use any other method for terminating the >> program. Certainly nothing that either deletes the main window or >> calls DestroyWindow. These would simply be incorrect. > > The actual code we have running is not a dialog app, that was just the >easiest way to demonstrate the problem -- but if we're not meant to >destroy the main window of our app, then I guess I have a different >question: > > Is it legal to _change_ the main window in an MFC app? The basic >structure we need is that the program launches, a dialog comes up with a >list box in there; if the user hits 'cancel', the app exits, but if they >select from the list box and hit 'ok', that dialog goes away and our >main app window comes up. **** Sounds very risky. MFC makes a lot of assumptions about the Way The World Works, and violating those cannot be done with impunity. When I skirt the edges of MFC's world image, I usually spend a lot of time single-stepping to figure out exactly what has gone wrong. Most of the time, I end up formulating a new solution that is not going to be sensitive to MFC's assumptions (which could change in a new release). I would be inclined to consider doing the dialog in the mainframe creation handler; if the user clicks "Cancel", then return -1. This means that it fits nicely in the MFC model and doesn't require playing tricks that may or may not work. **** > > To add to the fun, further down the line, we may end up switching the >main app window back and forth between the normal one and a different >window. All of the 'main' windows after the startup dialog are just >standard CWnd, we aren't doing an MFC document/view application, but we >want the MFC structure there to make our coding life easier. **** This is getting out into the weird edges of the world. I would have been inclined to use a completely different approach. One way is to do an SDI app and simply change views (which is well-documented and works well). If it is something with lots of dialog-like windows, then an SDI CFormView with a single "document" and multiple views would do the job. It is one of the more amazing errors I've seen made that if someone doesn't need document/view then the assumption is that using any aspect of document/view is "inefficient". I've had clients who get frustrated because they can't get rid of the CDocument class in a doc/view architecture. I would need more insight into what you are trying to do, but changing "main windows" seems a peculiarly complex way to accomplish a simple task. It is a bit sensitive to the underlying assumptions of MFC. ***** > > Is there a good way to do this? We used to have things structured so >the main dialog hangs around through all of execution and is invisible >when we're done with it, but then we got weird problems where if you >minimised the app, it wouldn't restore properly (the WM_RESTORE message >got routed somewhere peculiar), and general strangenesses with the >taskbar entry not doing what we'd expected. ***** Yes, the problems with minimization certainly follow naturally, and taskbar problems, and lots of other problems. None of them would surprise me in the slightest. You're far beyond the assumptions of either MFC or the underlying operating system at this point, and are begging for trouble. ***** > > If there's a good way to have MFC around to help with message handling >and such, but not have to worry about messing with m_pMainWnd, I'd love >to know what it is, because we know that the way we're doing things is a >bit sketchy -- it seems to work, but if there's a generally better >approach, that would be great. ***** If there is only one dialog at a time being shown, I'd suggest the trick I proposed earlier: an SDI app with multiple CFormView views. If you need multiple dialogs at a time, MDI would be a possible choice. Note that VS.NET MFC supports applications with multiple top-level windows, and in spite of the horrors of the VS.NET IDE, it would be worth considering. At that point you have a model that MFC supports intrinsically, instead of trying to fake out a mechanism that the earlier MFC doesn't support well, or possibly at all. Note that it isn't hard to move a dialog into a CFormView; I've done it. A bit of tedious editing, but pretty straightforward. ***** > > thanks, > > -- dan Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Dan Mitchell on 17 Jun 2005 16:32 Joseph M. Newcomer <newcomer(a)flounder.com> wrote in news:r2u5b1h1a1umt32m5kn6q3dph57apo1hft(a)4ax.com: >> Is it legal to _change_ the main window in an MFC app? [snip] > [snip] > I would be inclined to consider doing the dialog in the mainframe > creation handler; if the user clicks "Cancel", then return -1. This > means that it fits nicely in the MFC model and doesn't require playing > tricks that may or may not work. That's an interesting idea, though I'm not sure it'll work in our case -- we've done a fair bit of restructuring to our code in preparation for a Mac port, and part of that restructuring was assuming that we can have a main window that changes around, because until I found this MessageBox problem, I thought that was a valid assumption. >> To add to the fun, further down the line, we may end up switching the >>main app window back and forth between the normal one and a different >>window. [snip] > This is getting out into the weird edges of the world. I would have > been inclined to use a completely different approach. One way is to do > an SDI app and simply change views (which is well-documented and works > well). If it is something with lots of dialog-like windows, then an > SDI CFormView with a single "document" and multiple views would do the > job. Sadly, it's none of the above. We're writing a desktop-sharing application, and the three windows in question are a dialog containing a list of conferences to join, and after you've picked one, a window for viewing someone else's desktop (so far, so good, with DoModal() in OnCreate()), but the third possibility is that if you're sharing your desktop, the main application window is a shaped window that goes around the border of the screen to show what you're sharing. No titlebar, no standard menu, no system buttons, no 'document', etc. It would be theoretically possible to have the viewing window be able to change itself into a sharing window, but the resulting class would then end up containing two lots of code rather than one, and some really messy stuff to show/hide the relevant bits of UI when it changes mode. > Note that VS.NET MFC supports applications with multiple top-level > windows, and in spite of the horrors of the VS.NET IDE, it would be > worth considering. I'll take a look at that; it would certainly be an interesting solution. Poking through the MFC source, it looks as if leaving the app's main window as NULL all the way through should be okay for us, because while it's certainly used in some places, we either don't use that functionality, or we can replace AfxGetMainWnd() with pOurApp-> GetOurMainWnd(). thanks for the advice; I suppose it's good to know for certain that we're doing something unexpected, it means that we can't assume the problem is in our code, it may be in bits of MFC that our code is confusing.. -- dan
From: Joseph M. Newcomer on 18 Jun 2005 00:47 See below... On Fri, 17 Jun 2005 13:32:53 -0700, Dan Mitchell <djmitchella(a)yahoo.com> wrote: >Joseph M. Newcomer <newcomer(a)flounder.com> wrote in >news:r2u5b1h1a1umt32m5kn6q3dph57apo1hft(a)4ax.com: >>> Is it legal to _change_ the main window in an MFC app? [snip] >> [snip] >> I would be inclined to consider doing the dialog in the mainframe >> creation handler; if the user clicks "Cancel", then return -1. This >> means that it fits nicely in the MFC model and doesn't require playing >> tricks that may or may not work. > > That's an interesting idea, though I'm not sure it'll work in our case >-- we've done a fair bit of restructuring to our code in preparation for >a Mac port, and part of that restructuring was assuming that we can have >a main window that changes around, because until I found this MessageBox >problem, I thought that was a valid assumption. **** The problem is that MFC doesn't run on the Mac. The last version of MFC that ran was something like 4.2, now long deceased. Although Microsoft has an internal version of it, they no longer support it as a product, and I've not found any alternative. One of my clients is rewriting a major app (100K+ lines) using an entirely different framework library, but one that runs on Windows, the Mac, Linux, and a couple other Unix environments. So worrying about how to interface something to MFC while also supporting the Mac is probably not going to benefit you very much. You will still need to do so much platform-specific code that the benefits of MFC code are questionable. I've ported three different major apps to Windows, where one had already been ported to the Mac. The simplest one was the one where no attempt had been made to make the code portable; we had it up and running in three days of fairly intense work. The worst one was the one that had been designed to be "portable", and in fact had so many erroneous assumptions in it that it was almost a major rewrite to get code that would actually execute on the Mac and Windows (it had been written for X on Unix). That took us three weeks. But I wouldn't even try to port MFC code to the Mac at this point. Getting a more generic package...I think he's using one called DVT, but if you care, send me private email and I'll check it out, allows you to write-once, or so he hopes. I'm not sure he's actually started that project yet, but it is definitely beyond the planning stage and implementation may have started. **** > >>> To add to the fun, further down the line, we may end up switching the >>>main app window back and forth between the normal one and a different >>>window. [snip] >> This is getting out into the weird edges of the world. I would have >> been inclined to use a completely different approach. One way is to do >> an SDI app and simply change views (which is well-documented and works >> well). If it is something with lots of dialog-like windows, then an >> SDI CFormView with a single "document" and multiple views would do the >> job. > > Sadly, it's none of the above. We're writing a desktop-sharing >application, and the three windows in question are a dialog containing a >list of conferences to join, and after you've picked one, a window for >viewing someone else's desktop (so far, so good, with DoModal() in >OnCreate()), but the third possibility is that if you're sharing your >desktop, the main application window is a shaped window that goes around >the border of the screen to show what you're sharing. No titlebar, no >standard menu, no system buttons, no 'document', etc. **** I hope you have massive bandwidth, because desktop-sharing by sending bitmaps is pretty bad. I once used one over a closed 100-BaseT network (that is, we only had six machines on the network, and only two of them were doing the sharing) and the performance was pretty pathetic. One of the common techniques, and I don't know how it is actually implemented, is to intercept the GDI commands and ship, in effect, the metafile across the gap; Remote Desktop, Carbon Copy, and others use this technique, but I don't know how they inject themselves into the stream to accomplish it. But I can now use Remote Desktop over my 802.11g wireless network and use my main machine from my laptop (and enjoy sitting on my back porch with a nice breeze and pretty trees to look at) and not feel any performance problem. The special window that wraps the desktop can be created as a top-level window, WS_OVERLAPPED, and doesn't have to be a dialog at all. Check out my Better Zoomin utility on my MVP Tips site (I only use it to highlight the selection on the screen; you don't need one to do screen capture, since GetDC(NULL) appears to work fine. Using DoModal to create this window is probably a Really Bad Idea. The window for viewing the remote desktop could be done using another top-level window; again, DoModal and a dialog would probably be poor choices for this. Note these can all be MFC-based, but they don't need to be, and I suspect they shouldn't be, modal dialogs. Take a look at whiat I did in my zoomin utility; you might get some interesting ideas about alternative implementaitons. My suspicion is that you should not be using modal dialogs, or even modeless dialogs, at all. DIalogs have a lot of built-in semantics that might well only interfere with the solution, instead of aiding it. For the wrapping window, I'd suggest a WS_OVERLAPPED/WS_EX_TRANSPARENT window. But now that the problem has been stated farily clearly, there may be others who have even better ideas, or can tell what they have done in similar situations. ***** > > It would be theoretically possible to have the viewing window be able >to change itself into a sharing window, but the resulting class would >then end up containing two lots of code rather than one, and some really >messy stuff to show/hide the relevant bits of UI when it changes mode. ***** If there is common code, you could consider a superclass to hold the common code and two subclasses to deal with the code specific to each type of window. If they were top-level windows (instead of dialogs) their management could have fewer problems. I know there is a technique (I've not used it) for keeping these windows out of the Start-bar if you want to, I know it has been discussed in this newsgroup in the past. **** > >> Note that VS.NET MFC supports applications with multiple top-level >> windows, and in spite of the horrors of the VS.NET IDE, it would be >> worth considering. > > I'll take a look at that; it would certainly be an interesting >solution. Poking through the MFC source, it looks as if leaving the >app's main window as NULL all the way through should be okay for us, >because while it's certainly used in some places, we either don't use >that functionality, or we can replace AfxGetMainWnd() with pOurApp-> >GetOurMainWnd(). **** There are still problems that come up if you do this. For example, the default for a modal dialog is the main window. If that is NULL, the parent is the desktop, and thus any other app can overlay the dialog (or MessageBox). I've been done in badly by certain subroutine libraries (notably CodeBase) that did this; all the pointless MessageBoxes they popped up (instead of doing something intelligent like returning an error code, silently) popped up under the app. Since there was no parent to disable, the app appeared to keep running. Then, if I found one and clicked OK, they exited the program! (This demonstrated to me that they were totally clueless about how to write libraries. And their documentaiton sucked. I have no idea if this product improved, but they were unwilling to listen to any changes suggested by anyone...) So the consequences of a NULL main window may be subtler than you can think of, and in some cases, may lead to nasty ASSERT failures in those places MFC believes that there is a valid window handle there. **** > > thanks for the advice; I suppose it's good to know for certain that >we're doing something unexpected, it means that we can't assume the >problem is in our code, it may be in bits of MFC that our code is >confusing.. > > -- dan Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Dan Mitchell on 20 Jun 2005 11:56
Joseph M. Newcomer <newcomer(a)flounder.com> wrote in news:ii87b1h16r8e4brv1cdbm0q5jkr567on4h(a)4ax.com: > The problem is that MFC doesn't run on the Mac. [snip] Oh, sorry, I wasn't clear; there's a logic layer, a data layer, and a UI layer. The logic layer and data layer are (as far as possible) cross- platform; the UI layer is platform-specific. On Windows, we're implementing the UI with MFC; on the Mac, with Carbon/Cocoa. There's no attempt to port the actual UI code, just the basic structure of 'first a dialog, then a window'. > I hope you have massive bandwidth, because desktop-sharing by sending > bitmaps is pretty bad. This is under control; we're using a system where photographic parts of the screen are sent as jpegs and non-photographic bits are sent as gzipped deltas. It's not perfect, but we've done tests remotely controlling a full-colour 1024x768 desktop over a 56k modem link and it's certainly usable. > One of the common techniques, and I don't know how it is actually > implemented, is to intercept the GDI commands and ship, in effect, the > metafile across the gap; Remote Desktop, Carbon Copy, and others use > this technique, but I don't know how they inject themselves into the > stream to accomplish it. This is how NetMeeting does it, and it's definitely the way to go for absolute lowest bandwidth; the problem is firstly, how to get at the GDI commands (mirror driver or something?), but secondly how to transfer things across -- lines/setpixel are easy enough, bitmaps we have to keep track of which ones the host knows about but that's doable; it gets really tricky if the host machine is drawing in a font that the client machine doesn't have, because then the host machine has to ship that font over to the client. I think (though don't know for sure) that you can buy the NetMeeting technology from Microsoft if you know who to ask and have a big chequebook handy. > The special window that wraps the desktop can be created as a > top-level window, WS_OVERLAPPED, and doesn't have to be a dialog at > all. Oh, yes, it's just a standard CWnd, we do SetWindowRgn to be the shape of the border, a bit of fiddling around to get around the problems with other windows that don't redraw properly when a shaped window changes its shape on top of them (Outlook is particularly bad for this), and a couple of other workarounds becuse the windows desktop itself tends to not redraw as often as we'd like, and that works fine. > [two windows doing similar-ish things] > If there is common code, you could consider a superclass to hold the > common code and two subclasses to deal with the code specific to each > type of window. We actually had that design two versions ago, and it turned out that the common code wasn't quite as common as we'd hoped by the time we were done, sadly. > If they were top-level windows (instead of dialogs) > their management could have fewer problems. I know there is a > technique (I've not used it) for keeping these windows out of the > Start-bar if you want to, I know it has been discussed in this > newsgroup in the past. **** ITaskBarList does this. >>Poking through the MFC source, it looks as if leaving the >>app's main window as NULL all the way through should be okay for us, > > There are still problems that come up if you do this. [snip] > I've been done in badly by certain subroutine > libraries (notably CodeBase) that did this; all the pointless > MessageBoxes they popped up (instead of doing something intelligent > like returning an error code, silently) popped up under the app. Since > there was no parent to disable, the app appeared to keep running. We've had the same problem from time to time; under some situations we have a full-screen window temporarily covering the entire desktop to let the user know that they're about to start sharing it (because in user tests, anything smaller than their entire monitor just didn't get noticed, and we fortunately only need to pop this up for a second or two). If an error happens, then we had all sorts of pain where our error dialogs came up behind the full-screen window, so we've gone to great pains to keep Z order working properly. > So the consequences of a NULL main window may > be subtler than you can think of, and in some cases, may lead to nasty > ASSERT failures in those places MFC believes that there is a valid > window handle there. I think we just have to keep our eyes open for that sort of thing; as far as I can see, if we're not using MFC then we're either doing it with raw Win32, or at best ATL, and while that's certainly doable, at this point that'll be more work than keeping our eyes on MFC for any funny business.. -- dan |