How to disable MFC writing workspace settings to registry? - c++

By default, a basic MFC C++ project in Visual Studio 2010 will store all its workspace settings in the HKCU registry hive under a user-configurable key name. This includes last window size/position, ribbon settings, status bar, etc.
How can you disable this feature completely so as to not write to the registry at all?
I tried not setting SetRegistryKey(), which gave me a debug assertion from the framework on first read/write to registry. SetRegistryKey((LPCTSTR)NULL) gave the same results. SetRegistryBase() seems to have no effect. No other methods in CWinApp/CWinAppEx seem to help.

Edit: My original answer was wrong. I have edited the answer.
You can tell MFC to store settings in an .ini file instead of the registry. See this previous answer. (Update: That only works if you are not using CWinAppEx.)
If you want to prevent MFC from saving some of the state of the menus and toolbars altogether, add the following to your app’s constructor:
m_bSaveState = FALSE;
The m_bSaveState member is only defined if your app is derived from CWinAppEx.
Alternatively, you can override CWinAppEx::SaveState and CWinAppEx::LoadState.
To eliminate the WindowPlacement registry key, override CWinAppEx::StoreWindowPlacement.
You may still get other registry entries being written. A complete solution would involve subclassing CSettingsStore and then, in your application, calling CSettingsStoreSP::SetRuntimeClass. (See this for more information.) This is fairly difficult because there are a whole bunch of virtual functions you will have to override in your custom CSettingsStore class.

I know this is late for many but I found that CFrameWndEx -- it will be in your CMainFrame class -- uses WM_CLOSE window to SAVE your applications default location. I do not know what loads the location. However, if you override WM_CLOSE, then that window state is never saved when you exit the program. It will attempt to re-load your last window state, but since none was ever save to begin with, there's nothing to worry about.
A GOTCHA SITUATION:
Since the framework DOES still call some sort of WM_INIT function to LOAD the last position, if you compiled your code as normal, maximize the application window, closed the program with the X, WM_CLOSE would have saved the application state as MAXIMIZED. If you recompile the app by overriding WM_CLOSE as mentioned above, re-launch the application, you'll notice it starts out maximized! Obvious not what you intended. Thus, you must re-activate (comment out WM_CLOSE), let the program save the application state by re-launch the program, when closing as a normal window. Allow the overridden WM_CLOSE to work again and you'll see that normal window reappears.
CODE:
In your CMainFrame.h
public:
afx_msg void OnClose();
In your CMainFrame.cpp ... Extend your MESSAGE MAP
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)
*ON_WM_CLOSE() // <<< I ADDED THIS*
....
END_MESSAGE_MAP()
void CMainFrame::OnClose()
{
PostQuitMessage(0);
//CFrameWndEx::OnClose(); << WE DO NOT WANT TO HAVE THIS CALLED!
}

Related

MFC: Best place to prevent a window from re-appearing from a Restore operation

When my application is minimized, and the application programmatically closes a child window, the state of the child window between my framework and MFC goes out of sync because MFC will not send a WM_SHOWWINDOW message when the application is minimized. I noticed that Qt had the same problem: https://codereview.qt-project.org/#/c/93410/
Things that I have tried:
Override OnShowWindow() -- if the states are out of sync, then I alter the BOOL parameter before passing it to the CDialog::OnShowWindow. But doing so does nothing. It is as if the BOOL parameter given to the override is read-only.
Handle WM_SHOWWINDOW in PreTranslateMessage -- this does not work because WM_SHOWWINDOW does not appear here.
I know I can check SW_PARENTOPENING to know when to look for out-of-sync problems and handle it, but I just don't know where is the best place to do it.
My current solution is to override OnShowWindow, check for SW_PARENTOPENING, then post a SW_HIDE. It works, but it feels like a waste because I should be able to prevent it from restoring entirely rather than defer it.
Summary:
Basically, I am just programmatically closing a window, say from a timer call, or user's remote command, or whatever, while the main application is minimized. The dialog will be temporarily hidden when minimized (the MFC framework will automatically call ShowWindow(SW_HIDE) but with an internal flag to re-open when the app is restored). If my program sends ShowWindow(SW_HIDE) now, this call will not be registered, and the window will be re-opened by MFC when the app is maximized. To my user, he/she has closed the window remotely and does not expect the window to re-appear, so I need to re-call my ignored ShowWindow(SW_HIDE) somehow when restoring the main app.

How to save layout settings of MFC application?

I understand there are functions that can easily write windows registry, however I found out that in new MFC project created with wizard, some information (like split bar position, visibility of controls) gets stored automatically (or at least I found no CWinApp::Write* calls in the project). Since I have also older projects that don't have this behaviour I need to figure out how to make this without help of project wizard. Would anyone please know how does this work?
The MFC control state saving magic happens in the 'New' MFC Feature Pack, specifically in the SaveState methods, for example CMFCToolBar::SaveState.
To take advantage of this you'll therefore need to upgrade your Toolbars and Menus to use the newer controls and upgrade your application to inherit from CWinAppEx. I recommend that you use a New MFC Wizard based app as a guide on how to upgrade your old MFC app.
Most of the information is saved in CPane::SaveState(), thus if you want state of some component saved, you need to use classes derived from CPane. (for more info here is the class hierarchy).
The process of saving window states is initiated through CFrameImpl::OnClosingMainFrame(). This function in turn calls CWinAppEx::SaveState() which saves some application settings and then ALL instances of CMFCToolBar (they add themselves to global list of CMFCToolBars in call to OnCreate). In a similar way all dockable panes are saved but the list belongs to your main frame. Then positioin and size of your main frame is saved.
CViews and CFrameWnds are somewhat less favored, for what I found and tried out, the only information saved was visibility.
I used that loooong time ago. If I correctly reminds it, you should save the informations you want in a overridden CWinApp::ExitInstance() before calling base class method, and you load them in CWinApp::InitInstance. Be sure to allow for default values, because at first run, there will be nothing to load, and do not forget to call (or copy) base class.

ExitInstance not called in MFC app

Until now, I never really needed the Winapp ExitInstance() of a large MFC (Single Document Interface if it matters) app I'm working on. But now I do, mainly to cleanup memory alocations, unload some DLLs, etc. Well I soon learned by the obvious memory leaks and such that ExitInstance was not being called. Have I missed something obvious? Do I need to manually add something to the message map to make sure my ExitInstance override is called?
I guess i can do my cleanup elsewhere, but it's the best place if I can get it to run. Interestingly, I found quite a few instances of this by typing strings like "ExitInstance never called" and such into Google, and in no case were any real answers offered. The app normally closes when someone clicks the close box or the "Exit" from the File menu, and the OnClose() of the mainframe window certainly does always get called. I even tried forcing things by putting AfxGetMainWnd()->DestroyWindow(); in that mainframe OnClose() event, but still I can't get the ExitInstance() to actually run. Maybe it's just a big dummy function? Or maybe I'M just a big dummy? :-)
I had a similar problem to you...mine was caused by mixing Unicode and MBCS built code....maybe that was your underlying cause?
I had to convert an MBCS application to Unicode, but it was impossible to convert the whole project, so I had to mix Unicode compiled (the application) and MBCS compiled code (the DLLs).
Some of the MBCS DLLs were MFC extension DLLs, others were regular DLLs.
One of the MFC extension DLLs contained resources (bitmap image list, and common dialogs).
I didn't convert the DLL to UNICODE because it had a lot of dependent DLLs which would also have had to be converted, and in addition I didn't need the controls in the common dialogs to support Unicode text.
So I kept the DLL as MBCS, and used AfxSetResourceHandle prior to using any class in the MBCS DLL that used resources.....this was to make the resources get pulled from the DLL directly, instead of through the MFC resource chain, because MFC couldn't find the non-unicode resources otherwise.
I guess MFC doesn't like it when you have a mix of Unicode and non-unicode compiled code containing resources.....lookups in the resource chain fail (I guess has something to do with the conversion of resource IDs to an ID string i.e. via MAKEINTRESOURCE).
I made the main application UNICODE, and made sure the C++ headers of classes in the MBCS DLLs used CStringA in the function prototypes, or accepted wide strings and did the conversion internally.
What I found was my application wouldn't exit properly...it would stay in the MFC CWinThread::PumpMessage/AfxInternalPumpMessage() call, and ExitInstance would never be called.
To solve it, in my CMainFrame::OnDestroy() I put the following as the last 2 statements:
void CMainFrame::OnDestroy()
{
....
CFrameWnd::OnDestroy();
AfxPostQuitMessage(0);
}
I had the same problem. Turns out it was caused by some messages being pretranslated by the CWinApp object after the mainframe was destroyed. It would hit some validty check during that processing and just bail out instead of exiting through the normal exitinstance.
Overriding the apps PreTranslate message and returning immediately when there was no main frame resolved the issue.
BOOL CYourAppClass::PreTranslateMessage( MSG* pMsg )
{
// If the main window has gone away there is no need to do pre-translation.
// If the pre-translation were allowed to proceed the
// CWinAppEx::PreTranslateMessage may bail out without calling
/// the app's ExitInstance nor destructing the app object.
if ( !m_pMainWnd )
return FALSE;
return CWinAppEx::PreTranslateMessage( pMsg );
}
You say that "the OnClose() of the mainframe window certainly does always get called". I had a problem with one of my MFC apps; I had put some thread-cleanup code into my window's OnClose() event handler, and it wasn't getting called. After some pain, I put the thread-cleanup code into an OnDestroy() event-handler, and now it always gets called. So you may want to check whether or not your OnClose() event-handler always gets called.
I had exactly the same problem.
In my case, the issue was solved by overriding PostNcDestroy in CMainFrame. It seems that treating this last message in the main frame window makes it all work:
virtual void PostNcDestroy();
void CMainFrame::PostNcDestroy()
{
}
Another possibility is that there is an assertion failure happening in the middle of the shut down process. For example while destroying a window.
I found that when this happens, ExitInstance also gets skipped.
So, just before closing your application, clear the output/debug log window and see if such a message is printed.
If that's the case, solve the problem and see if ExitInstance gets called again.

How to stop MFC from disabling my controls if I don't declare a message map entry for it's corresponding command?

I have the following problem: MFC is disabling my toolbar (a CToolbar) controls if I don't have a message map entry for it's corresponding message (let's say ID_MYBUTTON1). Is there a way around this? I had the same problems with the menu but I found that you could disable the auto disabling by setting CFrameWnd::m_bAutoMenuEnable to false but I cannot find a similar member for CToolbar.
I guess I could add handlers redirecting to an empty function but it would be nice if I could just stop this behavior without using "tricks".
Thanks
Add a ON_UPDATE_COMMAND_UI handler for each of the controls in your toolbar. Something like this:
ON_UPDATE_COMMAND_UI(ID_MYBUTTON1, uiButtonHandler);
void myToolBar::uiButtonHandler(CCmdUI* pCmdUI)
{
pCmdUI->Enable(TRUE); // Or whatever logic you want.
}
For details read the appropriate section in the MSDN.
Well like I said in reply to zdan's answer I found a way. Just override the OnUpdateCmdUI function in CToolBar like this
class MyToolBar : public CToolBar
{
public:
virtual void OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
{ return CToolBar::OnUpdateCmdUI(pTarget, FALSE);}
}
the bDisableIfNoHndler flag is the one responsible for telling the toolbar to disable buttons if no handlers is found so I just force it to FALSE.
Though now I'm having another problem. The toolbar seem fines but it do not send the commands when I press a button. I'm not sure why because if I access the same commands from the menu it works fine. I'll try to see if this is related.
Thanks for your helps.
Update: Found my problem. Basically the problem was that my handlers to my commands were in MyFrame::PreTranslateMessage (after doing like suggested in this question's answer : How to redirect MFC messages to another object?) but the commands were not sent through this function (though when accessed from the menu they did). They were sent through MyFrame::OnCommand though so I just changed the code from PreTranslateMessage to OnCommand and now everything works fine. I don't know MFC enough to know why this was the case but now everything seems to works so thanks for the help everyone.

DLL plugin that creates a parented window doesn't handle messages correctly

I'm creating a plugin framework, where my application loads a series of plugin DLL's, then creates a new window and pass this new window's handle to the plugin. The plugin can, then, use this handle to create their own GUI.
Everything seems to be working very well. The only problem is that when I press TAB on a plugin widget (An editbox, for example), it doen't jump to another widget. I figured out that some Windows messages are passed, and some others aren't. The WM_KEYDOWN is passed for other keys, because I can type on the editbox, but this message doesn't handle TAB key.
Hope somebody has a hint.
I'm using Borland VCL with CBuilder, but I think I could use any framework under WIN32 to create these plugins, since they never know how their parent windows were created.
It's very complex matter indeed.
When you hit TAB focus jumps to another control only when these controls belong to a Modal Dialog Box. In fact there are some buttons like ESC, LEFT, RIGHT, DOWN, UP, TAB which modal dialog message function treats in a special way. If you want these keys to behave in similar way with modeless dialog box or any other window you should change you message processing function and use IsDialogMessage inside. You'll find more information about IsDialogMessage function in MSDN also to better understand this stuff you may check as well Dialog Boxes section.
And, as was mentioned before, you should set WS_TABSTOP and WS_GROUP styles when needed.
Good luck!
I believe you'll have to take the following steps:
Subclass your edit controls (and other controls as needed).
Capture the WM_KEYDOWN message in your edit control's WndProc.
Check to see if the shift key is currently held down (using GetKeyState or similar).
Call GetWindow, passing in a handle to your edit control and either GW_HWNDPREV or GW_HWNDNEXT depending on whether shift is held down. This will give you the handle to the window that should receive focus.
Call SetFocus and pass in the window handle you got in step 4.
Make sure you handle the case where your edit controls are multiline, as you might want to have a real tab character appear instead of moving to the next control.
Hope that helps!
I believe you suffer from having a different instance of the VCL in each of your dlls and exes. Classes from the dll are not the same as the ones from your exe, even if they are called the same. Also global variables (Application, Screen) are not shared between them. Neither is the memory since they both have their own memory manager.
The solution is to have the dlls and the exe share the VCL library and the memory manager. I am not a BCB developer, but a Delphi developer. In Delphi we would just use the rtl and the vcl as runtime packages. Maybe you could do the BCB equivalent.
A DLL has its own TApplication object.
to provide uniform key handling. when the DLL Loads.
assign the DLL::TApplication to the EXE::TApplication
Be sure to do the reverse on exit.
--
Michael