Porting a legacy MFC application to MFC Feature Pack
Introduction
I had to polish the GUI of an existing MFC application and decided to use the Visual Studio 2008 MFC Feature Pack.
At first, I wanted to follow the procedure described by Marius Bancila. Nevertheless, many steps did not work out directly; instead I got tons of compilation errors and assertions. Since I (and others) have found that documentation is lacking and many tutorials focus on demo applications only, I decided to let you participate in my struggle.
For the googlers, i included most of the assertion messages as well. All screenshots are (for legal reasons) not taken from the original application I am working with, but from the (standard MFC) wordpad sample, distributed with VS 2008; so sometimes they might not fit 100% to the text.
Remove Win98 Style
Before we start, the first step to a modern GUI is to enable Visual Styles:
CMyApp::InitInstance()
{
...
INITCOMMONCONTROLSEX CommonControls;
CommonControls.dwSize = sizeof (INITCOMMONCONTROLSEX);
CommonControls.dwICC = ICC_STANDARD_CLASSES; // I could not see any effect of the specific value here
InitCommonControlsEx (&CommonControls);
Ensure that version 6 of ComCtl32.dll is used:
#pragma comment(linker, "\"/manifestdependency:type='win32'\
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
Win98 style:
WinXP Style:
Refactoring to anticipate Compilation Errors
In the new MFC-classes some interfaces have changed, so if you directly switch to the new classes you will get compilation errors and have to work blindly for a while. Therefore, it is advantageous to perform some minimal refactoring beforehand.
GetToolBarCtrl
The method GetToolBarCtrl()
is not available in the new CMFCToolBar
, so some usages must be modified. (To more easily detect these issues I introduced my own MyToolBar which derives from CToolBar
, but has GetToolBarCtrl()
marked as deprecated)
CToolBar
(I suppose this was not possible in earlier versions of the MFC). These callsCEdit* pEdit = (CEdit*) this->GetToolBarCtrl().GetDlgItem(ID_EDIT);
this->GetToolBarCtrl().GetItemRect();
this->GetToolBarCtrl().GetButtonCount();
can be replaced by
CEdit* pEdit = (CEdit*) this->GetDlgItem(ID_EDIT);
this->GetItemRect();
this->GetCount() ;
HideButton
I could not find a straight-forward way to remove several calls to HideButton
. (Depending on a configuration in our app, some toolbars will be not available at all, others will contain fewer items)
I decided to prepare for later use of SetNonPermittedCommands; therefore I have to change the visibility of all buttons at once, not via individual calls.
My attempt is to replace
m_wndToolBar->GetToolBarCtrl().HideButton(ID_FILE_PRINT,false);
m_wndToolBar->GetToolBarCtrl().HideButton(ID_FILE_OPEN,true);
with something like this MyToolBarBase::ItemVisibility_t visibility;
visibility[ID_FILE_PRINT] = true;
visibility[ID_FILE_OPEN] = false;
myToolBar->SetItemVisiblity(visibility);
The Conversion itself
For all of the following, I recommend to have application verifier running to immediately detect new assertions.
I started with the procedure described by Marius Bancila:
Add a new header to your stdafx.h
#include <afxcontrolbars.h>
Start using the new Classes
Change CWinApp
to CWinAppEx
.
Add the following lines to InitInstance
InitContextMenuManager();
InitShellManager();
InitKeyboardManager();
InitTooltipManager();
CMFCToolTipInfo ttParams;
ttParams.m_bVislManagerTheme = TRUE;
theApp.GetTooltipManager()->
SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);
Change CMDIFrameWnd
to CMDIFrameWndEx
This gives an assertion in CWinAppEx::GetRegSectionPath
:
f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxregpath.cpp(33) : Assertion failed!
Add the following to InitInstance()
SetRegistryKey(_T("MyCompany"));
Now the app runs a bit further, but crashes in CMDIClientAreaWnd::CreateTabGroup
"CMDIClientAreaWnd::OnCreate: can't create tabs window\n"
Before this crash, already many more asserts and error messages appeared. In particular this one:
Can't load bitmap: 42b8. GetLastError() = 716
(Don't click this link, it won't help you). And this one
f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtabctrl.cpp(1395) : Assertion failed!
The corresponding line of code is
ENSURE(str.LoadString(IDS_AFXBARRES_CLOSEBAR));
so again some resource is missing. The solution is given in msdn:
"it's a known problem for statically linked projects using the feature pack"
=> Change application to use MFC as a shared dll. In my case this was not a big deal, otherwise, follow the instructions in the link above.
The assertions continue:
f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\winfrm2.cpp(92) : Assertion failed! f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\winfrm2.cpp(99) : Assertion failed!
occurs in
CFrameWnd::DockControlBar(...)
pDockBar = (CDockBar*)GetControlBar(dwDockBarMap[i][0]);
ASSERT(pDockBar != NULL);
The reason is that CControlBars
and CToolBars
cannot be docked in a CFrameWindow
Ex (see social.msdn).=> For the moment, comment out all statements of the form
DockControlBar(&m_myToolBar,AFX_IDW_DOCKBAR_TOP); DockControlBar(&m_myDialogBar,AFX_IDW_DOCKBAR_LEFT);
=> Yes! The Application starts!
Ok, all the dialogs and the menu is missing, but for the moment lets not bother with that.
Instead, lets go for something positive and enable styles :
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (MyBaseClass::OnCreate(lpCreateStruct) == -1)
return -1;
CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerOffice2007));
CMFCVisualManagerOffice2007::SetStyle(CMFCVisualManagerOffice2007::Office2007_ObsidianBlack);
Restart the application and - voilà! - the application shows a cool dark frame and modified system menu buttons.
Re-Show the Menu Bar
Besides all the toolbars and controlbars now also the menu disappeared. Well, actually it did not disappear completely; after pressing e.g. Alt-F it becomes visible again, but somewhere on top of the window.
To fix this, add a CMFCMenuBar
to CMainFrame
.
if (!m_wndMenuBar.Create(this))
{
TRACE0("Failed to create menubar\n");
return -1; // fail to create
}
m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);
...
m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndMenuBar);
Now the menu is visible (and shows the new style); also the system menu appears now where it should.
Replace every CDialogBar with a CPaneDialog
Avoid GetControlBar
When we replace ToolBars and DialogBars, calls to GetControlBar(IDD_MY_TOOLBAR)
will start to return NULL
and possibly crash the application.
Therefore avoid GetControlBar
and maybe replace
ShowControlBar(GetControlBar(IDD_MY_TOOLBAR, ...))
by
ShowControlBar(&m_myToolBar, ...)
with an overloaded ShowControlBar
that can handle CBasePane
as well. In one case I could also handle the case of an ID:
BOOL CMainFrame::OnToggleBar(UINT nID)
{
MyControlBarBase* pBar = GetControlBar(nID);
bool bfReturn = false;
if(pBar != NULL)
{
ShowControlBar(pBar, !pBar->IsWindowVisible(),false);
return true;
}
CBasePane * pPane = GetPane(nID);
if (pPane != NULL) {
ShowControlBar(pPane, !pPane->IsWindowVisible(), false);
return true;
}
return false;
}
(but later it turned out that this particular function is now obsolete and handled automatically by the framework; see below).
Change CDialogBar to CPaneDialog
Ensure that the WS_VISIBLE
flag for the resource is set (see this post).
GetWindowRect
was caused by a change in window hierarchy. So I replacedGetParent()-> GetParent()->GetWindowRect(&m_window_rect);
with
GetParent()->GetWindowRect(&m_window_rect);
until it turned out that the whole block (more than 100 lines) was an (attempted) bugfix by a former programmer that - since it didn't work out - had been redone elsewhere and was now superfluous.
Ensure minimal Size while Docking
When dialogs are now dragged around, the size becomes much too small. The reason is, that the method CalcDynamicLayout
is now not called anymore. Instead, in the constructor of the dialog call
SetMinSize(CSize(190, 480));
Important: The handling of the minimal dialog size, must be explicitly enabled, e.g. in CWinApp::InitInstance
call
CPane::m_bHandleMinSize = true;
That was all! Dialogbars are working now!
Change CStatusBar to CMFCStatusBar
Here just a single line in Mainfrm.h requires a change; worked without problems:
Change CToolBar
to CMFCToolBar
While CMFCToolBar
already can host many different controls, for
CToolBar
this functionality had to be programmed individually. Therefore it is not really possible to just replace a CToolBar
(with possibly lots of individual programming) by a CMFCToolBar
.
In my case, for the individual toolbars I got tons of asserts, because all the Combo boxes, Edit Fields, etc. are missing now. So every GetDlgItem(IDD_MY_FANCY_CONTROL)
fails now.
Anyways, if you are lucky, and your toolbar just shows some buttons, here we go:
Change CToolBar
to CMFCToolBar
.
Everything compiles, no asserts occur but - no toolbar is visible. Clicking them in the menu also doesn't help. For the status bar everything works as expected.
In my case the reason was twofold:
- the registry path where the toolbar states are stored has changed, and contains no information yet that the toolbars should be shown
- the menu is not yet functional for (un-)displaying toolbars
Show and Hide Toolbars from the Menu
In the original application, for each toolbar a menu entry was created. This entry was then essentially linked to these methods
CFrameWnd::OnBarCheck(UINT nID);
CFrameWnd::OnUpdateControlBarMenu(CCmdUI* pCmdUI);
This approach (and with it the method OnToggleBar
mentioned above) is now obsolete. The corresponding menu for showing toolbars can instead be dynamically created. All you need is a (sub)menu with e.g. ID ID_VIEW_TOOLBAR
and
// Enable toolbar and docking window menu replacement
EnablePaneMenu(TRUE, ID_VIEW_CUSTOMIZE, strCustomize, ID_VIEW_TOOLBAR);
in the OnCreate()-method. Instead of ID_VIEW_CUSTOMIZE
you can use 0, if you do not allow customization of toolbars. strCustomize
is the text to be displayed for the "Customize"-functionality if enabled.
Some problems appear here for me, caused by the localization of the menu. The menu originally contains only placeholder text, which is used as resource key to obtain the localized text. Unfortunately, the code behind does not work for sub-menus ("//recursion not implemented yet") and seemingly also not for the dynamic texts. As a workaround, I removed the toolbar entries from the menu; they can now only be enabled / disabled via right click on the toolbar-pane.
Furthermore, the toolbars and dialogs now need a meaningful (and localized) caption, since the menu entry will display the corresponding text.
m_wndToolBar.SetWindowText("Fancy Toolbar");
Porting the existing CToolBars
If the look and feel of your application is refreshed, then probably many toolbars will be redesigned as well (and e.g. be replaced by dockable multi lined controls), so probably the following will not be necessary at all.
Anyways, let's give it a try and port the existing, advanced CToolBars
to CMFCToolBars
. As mentioned, for simple toolbars this works immediately.
Also the new tooltips show up now:
Unfortunately, for custom toolbars, life is not so easy. The format toolbar in the wordpad example is totally screwed up now:
Replace Individual Elements by CMFC-classes
Note: the following will look different for you, depending on your custom toolbar implementation, but it should give a hint, if conversion is possible for you. Furthermore, I describe what I did in my application; I did not try to port the wordpad-toolbars.
There is also an msdn-article on putting controls on toolbars.
To insert an Edit-Control into the toolbar, the existing code looked like that
case ToolBar_Value::tbs_Edit :
{
rect.DeflateRect(0,2);
pWin = new CEdit();
tbvLocal.setControlWindow(pWin);
if (!((CEdit*)pWin)->Create(WS_CHILD | WS_VISIBLE | WS_TABSTOP |
ES_AUTOHSCROLL |
WS_BORDER | tbvLocal.getCtlAlignStyle() ,
rect, getToolbar(), lCurrID))
{
TRACE0("Failed to create Editfield\n");
return ;
}
((CEdit*)pWin)->SetLimitText(50);
}
break;
The modified code is quite a bit shorter
case ToolBar_Value::tbs_Edit :
{
CMFCToolBarEditBoxButton editButton(lCurrID, 0);
m_pWinToolbar->ReplaceButton(lCurrID, editButton);
break;
}
Inheritance Hierarchy
Interestingly, the following call will still work:
CEdit* pEdtPosition = (CEdit*)this->GetDlgItem(ID_EDT_FOO);
The reason is, that the returned value is of type CMFCToolBarEditCtrl
which inherits from CEdit
. Similar hierarchies exist for other control types.
Correct Amount of Elements
A problem occurs, because in my case the custom elements were added to the toolbar, while in the new semantics existing buttons are replaced, i.e., placeholders must be added. I had to update the code containing SetButtons
-statements.
=> Ensure that enough placeholders (with correct IDs) are present.
Correct Button Images
For some buttons, the icons are determined programmatically. Interestingly
myToolBar.SetButtonInfo(buttonId, ID_BTN_TO_CHANGE, state, 3);
does now not use button #3 of myToolbar
, but icon #3 of the first toolbar.
The correct way now is, to dynamically determine the image, via the command it belongs to, e.g.
CMainFrame::OnUpdateFooUI(...)
{
int imageIndex = GetCmdMgr()->GetCmdImage(ID_BTN_WITH_IMAGE_I_NEED, FALSE);
...
myToolBar.SetButtonInfo(buttonId, ID_BTN_TO_CHANGE, state, imageIndex);
Event Routing
In one toolbar, an event was fired as soon as an item was picked from a combo box. The event handler was placed in the toolbar class:
BEGIN_MESSAGE_MAP( MyToolBar, MyToolBarBase )
ON_CBN_SELENDOK(ID_LB_FOO ,OnSelChangeCmbFoo)
This event is now not catched any more. One could argue, that the above is bad practice anyway (I wouldn't agree with that), and you should instead put the message handler to e.g. CMainFrame
. (I am not really sure what the advantage of having 200 message handlers within CMainFrame actually is, but feel free to tell me).
One workaround is, to explicitly inform the toolbar about the event (see bcg-forum)
BOOL
CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
BOOL Handled = FALSE;
if (m_wndToolBar->CommandToIndex(nID) >= 0)
Handled |= m_wndToolBar->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
Handled |= MyMDIFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
return Handled;
}
Note that I am not returning early here, if m_wndToolbar
could handle the command. The reason is, that (for whatever reasons) part of the handling is done in the toolbar class (e.g. OnCommand
(..)) whereas other parts are done in CMainFrame (e.g. OnUpdateCommandUI
(..)). Until this is unified, routing must be done as above.
Points of Interest
MFC Feature Pack Samples
As already mentioned, the documentation in the MSDN is really lacking. As you know, if there is one thing that is worse than a missing documentation, it is a wrong one. My favorite example is CMFCToolBarMenuButton:
The
page shows a code snippet that i just could not find in the samples on
my harddisk. So I decided to download the samples again. In principle, I
was on the right track, but when you follow the links given in the MSDN
(CMFCToolBarMenuButton -> Wordpad-Sample -> Visual Studio 2008
Samples Page -> Visual C++ samples) you will arive at an outdated
site (dating 11/2007), featuring the same samples I already had installed. Even better, Microsoft knows about these outdated links, see this post.
Here is the correct download for the MFC Feature Pack Samples (the relevant samples are e.g. here C:\Program Files\Microsoft Visual Studio 9.0\Samples\1033\AllVCLanguageSamples\C++\MFC\Visual C++ 2008 Feature Pack).
Other Assertions
Actually, I performed parts of the conversion several times. When i found that some errors could already be anticipated before switching to the new library, i rolled back everything and implemented the fix in the original, compilable and immediately testable application.
During these attempts also some other assertions occured, that I don't want you to miss.
f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\winfrm.cpp(1712) : Assertion failed!
now caused by
this->LoadBarState("TOOLBARSTATES");
The reason is that some toolbars were visible before conversion, and are themselves not converted yet. Still, the configuration states something about those toolbars.
=> Remove the configuration (either the .ini-file or the registry entry) to avoid configuration of unconverted toolbars.
Conclusion
- Documentation is lacking: The documentation is by far not complete. Many pages in the msdn suggest to look up the source code (RTFC). Many answers could only be found in the BCGSoft Forum. Therefore: if you find out other beneficial information, don't hesitate to share it here!
- Custom controls cause problems: Many problems occur with custom controls, in particular such, that were originally written to work around lacks of the available MFC classes. In my case, most problems where caused by our localization, toolbar and tooltip classes.
- It is worth the trouble: Although, I did not yet touch the layout of the application (all boxes, toolbars, ... are still where they used to be) the look and feel has noticeably improved. At the same time, all instabilities introduced could be immediately detected (at least I hope I did so)
History
July 6th 2011: Inital version. Application compiles. Menu, docking dialogs working. Toolbars broken.
July 18th 2011: Application runs with no known deficits
Post Comment
p3ERj5 Very nice post. I certainly love this site. Continue the good work!
eBlMLw What as Happening i am new to this, I stumbled upon this I have found It absolutely helpful and it has aided me out loads. I hope to contribute & aid other users like its helped me. Great job.
loans personal online
15 min payday loans
bad credit loans guaranteed approval
cash advance lenders bad credit
personal loan9Ue5lv Im grateful for the article post.Much thanks again. Awesome.
ADwvgF Really enjoyed this post.Thanks Again. Much obliged.
KKuVPG I want to be able to write entries and add pics. I do not mean something like myspace or facebook or anything like that. I mean an actual blog..
XHX1xE Thanks again for the blog post.Much thanks again. Want more.
Of course, what a magnificent blog and instructive posts, I definitely will bookmark your blog.Have an awsome day!
A medida quejas (sorra, tirar, entre todo) y me gustaria conocer hombres así, que leyendo dos chicas, de
bares, discrepancias... Después parece que las cosas son aprenas.
Todos acerca de ciertos este fin de semana y hasta
coger un vínculo de pareja en general. Luego cuenta con un gran sistema de parejas compatibles.
Plataforma desde el nuevo punto de vista
de su interés. Algunos habían opciones amorosas que puedes encontrar personas de alta
o cualquier otro tipo que no o extraterren chochos cara a cara.
Contraria como diferentes modificaciones de libros que incluyen la información, actualmente a
los servicios gratis y lo más importante.Vt72T2 You, my friend, ROCK! I found just the information I already searched all over the place and just could not locate it. What a perfect website.
2bKqkz to be capable of get these phones add alone is usually to pay for
zVl2bJ Well I really liked studying it. This post procured by you is very effective for proper planning.
WZb7iB Wow, incredible blog format! How lengthy have you been blogging for? you made running a blog look easy. The total glance of your site is fantastic, as well as the content material!
PlhXOn If you wish for to take a great deal from this paragraph then you
T9ErGw Really appreciate you sharing this post.Really thank you! Really Great.
swVDCz is rare to look a great weblog like this one these days..
wDKm3r Muchos Gracias for your article.Thanks Again. Really Great.
4aJJzj Major thanks for the article.Really looking forward to read more. Want more.
qGNWKQ IaаАабТТаЂааАабТТаБТd ought to talk to you here. Which is not some thing I do! I quite like reading a post which will make men and women believe. Also, many thanks permitting me to comment!
TdOSwJ website a lot of times previous to I could get it to load properly.
dOqrAx Very informative blog article.Really thank you! Fantastic.
v2mAEN Muchos Gracias for your article.Really looking forward to read more. Really Cool.
X3JU1Q Very informative blog article.Really looking forward to read more. Great.
Mg3DF8 This can be such a great position, plus took place sense very much exactly the same myself. Another fantastic keep posted.
FDOQ31 Superb weblog here! Also your web site loads up quick! What host are you utilizing? Can I get your affiliate link to your host? I wish my internet site loaded up as rapidly as yours lol
JdEuLq Your style is so unique compared to other people I ave read stuff from. I appreciate you for posting when you have the opportunity, Guess I will just book mark this web site.
qqydtR Very nice article and straight to the point. I am not sure if this is actually the best place to ask but do you people have any thoughts on where to hire some professional writers? Thanks in advance
6jodJz They were why not look here permanently out. There was far too much fat on
lrYypC pretty valuable stuff, overall I think this is well worth a bookmark, thanks
jruLj9 Normally I don at learn article on blogs, however I wish to say that this write-up very compelled me to check out and do it! Your writing taste has been amazed me. Thank you, quite nice article.
vLaKf3 Looking forward to reading more. Great post. Will read on
d3g3es Wow! Thank you! I constantly wanted to write on my website something like that. Can I take a portion of your post to my blog?
qZ7N2R It as going to be finish of mine day, however before ending I am reading this wonderful article to improve my know-how.
jDMaod qui se retrouve et son petit cul les hommes
Jdbsrf It?s arduous to search out knowledgeable folks on this subject, but you sound like you recognize what you?re talking about! Thanks
hjD9bn Really clear internet site, thanks for this post.
Ed37Pw Wow, amazing blog layout! How long have you been blogging for? you make blogging look easy. The overall look of your web site is fantastic, let alone the content!
3lwHYu I reckon something genuinely special in this site.
B5rjav I was suggested this blog by my cousin. I am not sure whether this post is written by him as nobody else know such detailed about my difficulty. You are incredible! Thanks!
Utterly pent content material , appreciate it for selective information.
FL5Rml lots up very fast! What host are you the usage of? Can I get
hLGl5c What as up, just wanted to mention, I enjoyed this post. It was funny. Keep on posting!
vJ4hUt Video lesbiennes sexe porno ladies Also visit my blog post sexshop
gV2zS0 Some genuinely prime articles on this website , saved to favorites.
DEqZvL We stumbled over here different web page and thought I might as well check things out. I like what I see so now i am following you. Look forward to checking out your web page for a second time.
RUiRYV magnificent issues altogether, you just received a brand new reader. What would you recommend about your submit that you simply made a few days ago? Any sure?
q7gRrL Really informative blog post. Fantastic.
OXTmRs
BEBZKV
Hv5KAG Thanks for the blog.Really looking forward to read more. Will read on