|
You're having some kinf of reentrancy problem. Could you post the code for the destructor of your OpenGl class, as well as the Stop method and the message pump?
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Hi!
I added verbose debug output. It looks like the mainframe "takes over" the message pump activity, and never give control back to my OpenGL mainloop. So it does not get the chance to continue it's work to quit.
1. Here is a log from what happens when I toggle the OpenGL dialog. Everything runs fine:
<br />
[MainFrame] start modal<br />
\Mainloop running<br />
\PumpMessage<br />
/PumpMessage<br />
/Mainloop running<br />
....<br />
\Mainloop running<br />
\PumpMessage<br />
PumpMessage message 0x0200<br />
PumpMessage message 0x0202<br />
[MainFrame] post stop<br />
PumpMessage message 0x0012 WM_QUIT<br />
/PumpMessage WM_QUIT<br />
Mainloop leaving..<br />
MessageHandler message 0x0046<br />
MessageHandler message 0x0047<br />
MessageHandler message 0x0002 WM_DESTROY<br />
[MainFrame] stop modal<br />
2. Here is log from what happens when I close my mainframe, while the OpenGL dialog still runs. You will see the problems at the end.
<br />
[MainFrame] start modal<br />
\Mainloop running<br />
\PumpMessage<br />
/PumpMessage<br />
/Mainloop running<br />
....<br />
\Mainloop running<br />
\PumpMessage<br />
MessageHandler message 0x001c WM_ACTIVATEAPP<br />
PumpMessage message 0x0111 WM_COMMAND (wParam = ID_APP_EXIT)<br />
[MainFrame] OnClose <br />
[MainFrame] post stop<br />
MessageHandler message 0x0085<br />
MessageHandler message 0x000d<br />
MessageHandler message 0x0014<br />
MessageHandler message 0x001c<br />
MessageHandler message 0x001f<br />
MessageHandler message 0x000a<br />
MessageHandler message 0x001c<br />
MessageHandler message 0x001c<br />
Explanation: Everything runs fine... until I choose to quit my application. That's the WM_COMMAND from above, which is from my systray menu. Immediately the MainFrame::OnClose() is called and therr I post a QuitMessage to my OpenGL window.
But the control is never given back to my OpenGL message pump, so it can't receive the WM_QUIT and leave the mainloop.
Where do I have my concept failure? Thx for help...
PS: I can post all the code, if the debug output isn't enough (just ask). I thought this way it is easier to see what happens.
|
|
|
|
|
Moak wrote:
I added verbose debug output. It looks like the mainframe "takes over" the message pump activity, and never give control back to my OpenGL mainloop. So it does not get the chance to continue it's work to quit.
After a night of debugging, spending time in Petzold and MFC wincore.cpp it looks like only CWnd derived classes can provide an own messagepump within MFC.
Well, I'm not sure and honestly I'm not much less confused as I was before. I found no way to mix MFC messagepump with a Win32 messagepump. It looks possible in CWnd::RunModalLoop() to have more then one message pump (only one active at one time), but this seems not to be possible when mixing MFC (CWnd) with Win32 (homebrew).
Are there workarounds?
I can start my OpenGL window in a own thread, but I don't want to leave the thread context. How about calling my MainRender() method as often as possible? Any ideas how to achieve this, using OnIdle or set a windows hook for GetMessage(), etc?
Thx, Moak
|
|
|
|
|
PPS: Here are the requested code snippets, perhaps it helps to trace the problem.
BOOL CBlueSharkEngine::Run()
{
if(!ShowWindow()) return FALSE;
m_bEngineRunning=TRUE;
m_timer.Init();
for (;; )
{
TRACE("\\Mainloop running\n");
if(!m_pWindow->PumpMessage()) break;
TRACE("/Mainloop running\n");
}
TRACE("Mainloop leaving..\n");
DestroyWindow();
m_bEngineRunning=FALSE;
return TRUE;
}
void CBlueSharkEngine::Stop()
{
if(m_bEngineRunning) PostQuitMessage(0);
}
BOOL CBlueSharkEngineWindow::PumpMessage()
{
MSG msg;
TRACE(" \\PumpMessage\n");
while (::PeekMessage (&msg,NULL,0,0,PM_NOREMOVE))
{
TRACE(" PumpMessage message 0x%04x\n",msg.message);
if (!::GetMessage (&msg, NULL, 0, 0))
{
TRACE(" /PumpMessage WM_QUIT\n");
return FALSE;
}
::TranslateMessage (&msg);
::DispatchMessage (&msg);
}
TRACE(" /PumpMessage\n");
return TRUE;
}
CBlueSharkEngine:: ~CBlueSharkEngine()
{
Stop();
assert(!m_bEngineRunning);
DestroyWindow();
if(m_pWindow) delete m_pWindow;
if(m_pTexture) delete m_pTexture;
if(m_pFont) delete m_pFont;
if(m_pInterface) delete m_pInterface;
}
CBlueSharkEngineWindow:: ~CBlueSharkEngineWindow() {}
|
|
|
|
|
OK, try that: I don't think you need any extra message pump (since the main frame is active even when the OpenGL window shows). So, remove the pump out of CBlueSharkEngine::Run , move DestroyWindow to the handling code for WM_CLOSE , and elminate the post-run portion in CMainFrame::OnAppToggle3DDialog . Now replace your CBlueSharkEngine::Stop with a call to CloseWindow (i.e. send a WM_CLOSE to yout OpenGL window.) There are some more details to be adjusted, but I think the general scheme should work.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Thx, Joaquín.
Oops, the main important line in CBlueSharkEngine::Run() is what I commented out previously:
EngineMain(m_timer.GetElapsedSeconds());
This method will render the OpenGL scene (depending from elapsed time). Right now I called it from inside my message loop CBlueSharkEngine::Run() . I need to call this method regular (and as fast as possible to get a high frame rate). Hmm, I think I found a possible replacement, when couldn't start my main message loop. With some structure changes:
LRESULT CALLBACK Wnd_ProcMessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_PAINT:
if(pEngine->m_bEngineRunning)
pEngine->EngineMain(pEngine->m_timer->GetElapsedSeconds());
break;
default:
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
return 0;
}
The "trick" above is to get repeated WM_PAINT messages to render my frames with EngineMain() . The rendering starts, when I set m_bEngineRunning=TRUE (and Invalidate() ). It works in a test project, I will do more testings soon.
Some questions:
* How efficient is it to request repeated WM_PAINT messages generally, don't I loose CPU performance compared to my direct calling message loop? In a game engine or a 3D visualisation you want to reach highest frame rate.
* There is one negative side effect with this new type of message handling. The handling of the system menu became very "blocky", anything else negative that could happen?
Thx again, Moak
|
|
|
|
|
First of all, have you finally solved the message pump mess following my proposal? (just curious, you know.)
As for how to get repainting in your OpenGL window, you have various options:- The most simple way is to set a Windows timer (see
SetTimer ), which sends a message at a given rate. This is not very accurate, but should suffice for say up to 10 fps. - If you need more frames per second, probably your best bet is multimedia timers. These call a user-defined callback with much finer temporal resolution than Windows timers. This callback is executed in a thread different from your app's, so beware of concurrency problems. Probably the best way to avoid them is sending a user message to the OpenGL window from the multimedia timer callback and doing the reapaint there.
For much more info on different timers, read Nemanja Trifunovic's Timers tutorial.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Joaquín M López Muñoz wrote:
First of all, have you finally solved the message pump mess following my proposal?
Yes, your ideas was helpfull! I think it didn't solve the problem yet how to keep the rendering of the OpenGL scene alive? I was thinking about suggestion 1. + 2. too; what do you think about my solution 3. with the repeated WM_PAINT (see my last post)?
Briefly explanation: The trick was letting Windows sending you WM_PAINTs as often as possible, so you do not need an additional timer, multimedia or high resultion timer triggering the rendering. When rendering the OpenGL scene (just imagine a game with at least 30 FPS) you mainly don't need very regular redraws (which usually is an illusion in a multitasking environment). But you want them as often/fast as possible!
The trick with the WM_PAINT is working because you do not validate the update region with DefWindowProc function, Windows will send another WM_PAINT soon. As far as I understood from one Codeproject tutorial, WM_PAINT is a virtual message that is generated on the fly, whenever nothing else is happening. Idea sounds good and testing software showed it works.
I still prefer to use my own message pump and direct triggering of the rendering in a game, but the WM_PAINT trick seems to be a nice way to mix a MFC application with OpenGL.
Well, I need to do some profiling (evaluating FPS). Basically the WM_PAINT thing works... I wonder how efficient it is? What do you think?
Btw, befor you wonder about the timer in pEngine->EngineMain(pEngine->m_timer->GetElapsedSeconds()) . I use this timer to tell the rendering how many time has elapsed since last drawing. The EngineMain() function should update the object depending on time. I allready used this technique before to gain a CPU independent rendering.
Greets, Moak
|
|
|
|
|
I don't really think having your own message pump (unless it is an a separate thread) is a good idea at all. Depending on the host program, the message pump can do a lot of things you don't have the slightest idea about (as indeed does MFC), so replacing it with your own is bound to problems.
As for the WM_PAINT message, well if it works and you're satisfied with it, I guess it's OK. Nevertheless, multimedia timers are probably best fit for this kind of purposes (thery're are designed for this), and they allow for more flexibility to set the framerate desired (as it stands now, I guess the WM_PAINT method causes your app to choke a bit, doesn't it.)
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
A and B are correct.
Just for your knowledges as well, the modal dialog in MFC is different than the modal dialog that you will get with the Win32 API. That is because of that function that you found, RunModalLoop(), MFC creates its own message pump to handle all of the messages, which allows the rest of the app to intervene if necessary, MFC is able to accomplish this because it always creates its dialogs with CreateDialog rather than DialogBox. If you call DialogBox in the Win32 API, windows will create their own modal message pump for you.
Coincidentally this is how the processing for menus works as well, there is a modal message pump involved.
Build a man a fire, and he will be warm for a day Light a man on fire, and he will be warm for the rest of his life!
|
|
|
|
|
Is there anyway in code to create a file association. i.e. I want to associate a file type of .log with notepad. I know how to do this via Windows explorer, can I do this at runtime?
Thanks in advance,
Bob
|
|
|
|
|
You can create registry keys at HKEY_CLASSES_ROOT.
Create one for the file extesion (Ex.: .log) and put as default value a mime type (Ex.: text/plain). If the mime doesn´t exists you can create another key at HKEY_CLASSES_ROOT with a subkey shell\open and put the command to open the file there.
Mauricio Ritter - Brazil
Sonorking now: 100.13560 Trank
The alcohol is one of the greatest enemys of man, but a man who flee from his enemys is a coward.
|
|
|
|
|
|
Is there any way to have a combo box without the button to drop the list down? If not can the button be resized in any way?
Ed
|
|
|
|
|
Wow, a combobox without the buttong to drop list !!!!
It became an editbox or a listbox !!!!
I can't understand you !
Hung Son
i-g.hypermart.net
dlhson2001@yahoo.com
|
|
|
|
|
Ha ha, I know it sounds strange, but I still need it to drop the list when the user clicks the box or hits F4, I just can't get the boxes to fit where they need to without getting rid of the button somehow. I am dealing with a screen drawn for a character based program and I have to use the same layout so my space is limited.
Ed
|
|
|
|
|
you would need to do subclassing already.
how about checking this out.
http://www.codejock.com/developer/article02.htm
this makes a flat button.
so now u can don't draw the buttons instead
|
|
|
|
|
Hi,
Is there any way I can convert a HRESULT to the "errorcode string"
I mean, the HRESULT 2147746132 means "Class not registered", is there any functions that takes a HRESULT and gives me a string with the error text?
- Anders
Money talks, but all mine ever says is "Goodbye!"
|
|
|
|
|
FormatMessage API function will works with predefined HRESULT.
Philippe Mori
|
|
|
|
|
From MSDN Q169498:
// Obtain the error message for a given HRESULT
CString LogCrackHR( HRESULT hr )
{
LPVOID lpMsgBuf;
CString strTmp;
::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL );
// STR_TMP is defined within LOG.CPP to provide safe format string
// for both ANSI and UNICODE
strTmp.Format( "%s", (char *) lpMsgBuf );
// Free the buffer.
::LocalFree( lpMsgBuf );
return strTmp;
}
Brandon
The WTL newsgroup: over 1400 members! Be a part of it. http://groups.yahoo.com/group/wtl
|
|
|
|
|
I'm subclassing a static control. I would like to get the mouse click messages but they don't seem to get generated. I get them if I subclass from a button.
Cathy
Life's uncertain, have dessert first!
|
|
|
|
|
Mamke sure the static control resource on the dialog has the Notify style set to be able to get mouse messages.
Roger Allen
Sonork 100.10016
If I had a quote, it would be a very good one.
|
|
|
|
|
That did it. Thanks!
Cathy
Life's uncertain, have dessert first!
|
|
|
|
|
I created an ATL DLL server project and inserted a new ATL object as a simple one, named Inter. Next, i added a new BSTR-type property named Str.
I wrote:
STDMETHODIMP CInter::get_Str(BSTR *pVal)
{
_bstr_t x(*pVal, FALSE);
m_str=x.copy();
return S_OK;
}
This code links perfectly on debug versions but generates this error:
LIBCMT.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
on release versions.
Do you know how to handle it?
rechi
|
|
|
|
|
|