From: Doug Harrison [MVP] on
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
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
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
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
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