Feeds:
Posts

## VC++ 2008 with MFC and GDI+ dependency

A month or two back, I had to write a launcher app that would allow/disallow the running of select installers based on various conditions and config files. Since this would run prior to any of the MSIs, and thus couldn’t assume the presence of any dependencies like .NET or the VC++ runtime, I wrote it as a statically linked MFC dialog app using VC++ 2008. All was well with initial testing. But during a final testing phase, the launcher failed to run on a fresh Windows 2000 box with an error about a missing gdiplus.dll.

Windows XP and later OSes like Vista and Windows 7 have GDI+ by default though you may need to apply patches for the latest updates on XP. Windows 2000 predates GDI+ and thus does not have GDI+ by default though a redistributable is available as a separate download. So that’s why it failed. But why is there a dependency at all? I had always assumed that a statically linked MFC app would run anywhere. The it struck me – the BCG stuff that they added to MFC starting VC++ 2008! That code used GDI+ and thus had a GDI+ dependency.

Obviously I couldn’t include the GDI+ redistributable for the launcher app and I didn’t want to remotely take the risk of manually copying along the GDI+ DLL (or any extra files it needs) what with the various updates out there. Eventually, I rewrote the app as a plain Win32 API dialog app (the app was kinda simple, so it didn’t take me too long). I know not many people support Windows 2000 today, but some of our clients are still on that OS, and so it was important for us to support it. Anyway, this is just something to keep in mind when using MFC to write a stub executable that should run anywhere.

## Article : Deploying MFC applications via ClickOnce

Recently I had the task of deploying an MFC application suite along with its dependencies via ClickOnce. Visual Studio does not directly support deployment of MFC applications (even if it’s compiled with /clr) and one of the suggested solutions I found on the web was to have a stub C# executable which would launch the main MFC application. That way you could take advantage of Visual Studio’s built-in deployment functionality. My friend Rama Vavilala had a better suggestion for me – he asked me to try putting in a dummy cpp file into the main project that was compiled using /clr. If ClickOnce accepted such an executable as a .NET assembly, then all I would have to do would be to create the required manifest files on my own. Well all I can say is hats off to Rama for his elegant solution – because it actually did work. This article is a step-by-step pictorial on how you would deploy an MFC application using this trick.

## Creating an unmovable dialog

Someone recently asked in the MSDN forums how to create an unmovable dialog using MFC. The simplest way to do this is to remove SC_MOVE from the dialog’s system menu. This can be easily done in OnInitDialog :-

CMenu* pSysMenu = GetSystemMenu(FALSE);
{
}

Obviously, you need to think of why you’d want to do soemthing like this. I personally would find it very annoying if some app I installed prevented me from moving its main dialog. Though perhaps it would be okay if there was a way to minimize the app. An alternate way would be to prevent the dialog from moving via the mouse, but to still enable keyboard-based moving. To do that, we handle WM_NCHITTEST and do something like this :-

LRESULT CUnmovableDlg::OnNcHitTest(CPoint point)
{
LRESULT result = CDialog::OnNcHitTest(point);
return result == HTCAPTION ? HTCLIENT : result;
}

We treat HTCAPTION like HTCLIENT, so now the user cannot drag/move using the title bar. He can still move it via the keyboard (assuming you have not removed SC_MOVE). Once again, unless you have a very good reason, this is not something I would recommend at all.

## New article : Drag/Drop a non-existent file into Windows Explorer

Here’s an article I wrote for The Code Project that shows how you can drag and drop a virtual file (one that does not physically exist on disk) from your app into Windows Explorer. It’s useful for scenarios like extracting a file from an archive or where the source file is on a remote machine.

## The SHCreateDirectoryEx API function

Very frequently in the forums, someone posts a question asking for a function that will create a full directory path for them. For example, they may want to create c:\a\b\c\d\e\f with one call, where the intermediate folders may not exist. The answer, if you are on Win 2000 or later, is to use the SHCreateDirectoryEx function that’ll do this for you. Here’s some sample code :

SHCreateDirectoryEx(NULL,
_T("d:\\abc\\def\\ghi\\nish"), NULL);

It’s a really useful API function, just not that well known. Anyway this blog entry will serve as a link which I’ll point to, if I see this question popping up in the forums again.

## Selecting a default folder with SHBrowseForFolder

Today, there was an article posted on the Code Project website that showed how to use SHBrowseForFolder, and there was a question from a reader in the forum, asking how a default folder can be specified. This is pretty easy to do, and all you need to do is to set the BIF_VALIDATE flag, specify a callback, and in the callback, handle BFFM_INITIALIZED which indicates that the dialog is ready, and then SendMessage a BFFM_SETSELECTION message to the HWND of the dialog. Here’s some commented code that shows how this is done.

int CALLBACK BrowseCallbackProc(HWND hwnd,
UINT uMsg,LPARAM lParam,LPARAM lpData)
{
// Look for BFFM_INITIALIZED
if(uMsg == BFFM_INITIALIZED)
{
SendMessage(hwnd, BFFM_SETSELECTION,
TRUE,(LPARAM)_T("C:\\Program Files"));
}
return 0;
}

void ShowSHBrowseForFolderDemoDlg()
{
BROWSEINFO bi = {0};
// Make sure BIF_VALIDATE is specified
bi.ulFlags = BIF_USENEWUI|BIF_VALIDATE;
bi.lpszTitle = _T("Choose a folder");
// Set the callback function
bi.lpfn = BrowseCallbackProc;
LPITEMIDLIST pIDL = SHBrowseForFolder(&bi);
if(pIDL)
{
}
}

## Get the Office 2003 style menus/toolbars in your MFC apps using Windows Forms

This is my latest Code Project article, and it shows you how to use the Windows Forms 2.0 MenuStrip and ToolStrip controls in your MFC applications to give them an Office 2003 look and feel. These Forms controls are pretty well written, and if you change the XP theme/style, they change accordingly, just like Word or Excel would!

## Is MFC dead? Does MFC have a future?

“Is MFC dead?” is arguably the most common question appearing on the VC++ newsgroups along with variants like “Does MFC have a future?” and “Can MFC applications run on Longhorn?”. A short answer would be, “No, MFC is not dead, and you can continue to develop applications using MFC and be assured that they will run on Longhorn (or Vista).” For a more involved answer, continue reading.

What makes a technology dead? The fact that nobody uses it any more? If so, MFC is not dead now nor will it die in the next 10 years – because, the number of people using MFC must be in the millions right now and even assuming that newer adopters would be fewer as the years go by, the amount of MFC code is so extensive that there will be a large number of developers continuing to work on it. Okay, so that definition of death wouldn’t apply here. How about considering a technology to be dead if its development is frozen? If so, same results – MFC is simply not dead! MFC has got some new additions (mostly classes that allow it to interact easily with Windows Forms) and MFC will continue to be enhanced – if I have not grossly got my facts wrong, support for Avalon (or Windows Presentation Foundation) is being added to MFC for the Orcas release. Okay, so all this talk of a dead MFC is rubbish! Next point.

Does it have a future? My answer – YES, it does. One major concern people have is that with .NET all over the place, why would someone choose MFC. Well, one big reason would be that for Win32 GUI frameworks, I (and many others far more knowledgeable and technically recognized than I am) find MFC a far better option to Windows Forms. The CFrameWnd, CView, CDialog, CWnd set of classes do not have functionally equivalent classes under Windows Forms – so the whole GUI programming approach is different with Windows Forms than it’s with MFC. At the same time, it’d be terminally stupid to ignore .NET – but then using MFC does not require you to ignore .NET. VC++ lets you mix native applications (including MFC applications) with .NET seamlessly. So, there’re going to a be a good number of applications whose UI is developed with MFC but will extensively take advantage of the .NET classes for enhanced functionality with minimal lines of code. .NET is not MFC’s nemesis, rather .NET is something that can be used to enhance your MFC programming experience. Next point.

Will MFC apps run on Longhorn? They better do so else no one’s going to be using Longhorn or Vista or whatever it’s going to be called. MFC applications are essentially Win32 applications and right now, so are your .NET applications. While, .NET apps contain a lot of IL code as their primary content, they are PE executables just like other Win32 executables. There are millions of applications developed with MFC, ATL, WTL, Visual Basic, Windows Forms, Delphi etc. that will need to run on Longhorn. So, yes, your MFC apps will run on Longhorn. Will they look ugly? Uhm, I don’t know. They definitely won’t look like Longhorn apps unless you use the MFC-Avalon framework, but then the same applies to your Windows Forms applications.

Concluding, MFC is not dead now nor is it going to die any time soon; .NET is not going to kill MFC, rather it’s going to complement it; and MFC applications will not only work on Longhorn, but MFC is being updated/enhanced to support the Avalon framework. So if you are using MFC now, continue using it with confidence and pride, and next time you see this question asked in some forum, feel free to redirect the poster to this blog entry.

## The PathCompactPath function

PathCompactPath is a not-so-well-known function that truncates a file path to fit within a specific pixel-width and uses ellipses to show that it’s been truncated. Here’s some sample code that shows how you can use it :-

#ifndef UNICODE
#define _tcout cout
#else
#define _tcout wcout
#endif

void TruncateShow(LPCTSTR buff, UINT pixels)
{
TCHAR tmpbuff[MAX_PATH*4] = {0};
_tcsncat(tmpbuff, buff, MAX_PATH*4);
//While not mentioned in the docs, you can pass NULL for HDC
if(PathCompactPath(NULL,tmpbuff,pixels))
_tcout << pixels << _T(" pixel width : ")
<< tmpbuff << endl;
}

int main()
{
const TCHAR buff[MAX_PATH*4] = _T("C:\\Program Files\\Microsoft")
_T(" Visual Studio .NET 2003\\Vc7\\lib");
_tcout << _T("Original string : ") << buff << endl;
TruncateShow(buff,300);
TruncateShow(buff,200);
TruncateShow(buff,100);
return 42;
}

And here’s the output :-

Original string : C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\lib
300 pixel width : C:\Program Files\Microsoft Visual Studio ...\lib
200 pixel width : C:\Program Files\Microsof...\lib
100 pixel width : C:\Progra...\lib

## Locking splitter windows

Earlier today, someone asked on the vc.mfc NG how he can lock his splitter windows. I replied to him asking him to handle WM_SETCURSOR, WM_LBUTTONDOWN and WM_LBUTTONUP in a CSplitterWnd derived class and to call the base class implementation only if locking is disabled. Later, I decided to try it out and found that there was no need to handle WM_LBUTTONUP but that WM_MOUSEMOVE should be handled. I replied to him again, correcting my earlier post. For future reference, I thought it best to blog about it here and the relevant code snippets are posted below :-

class CLockSplitterWnd : public CSplitterWnd;

//. . .

BEGIN_MESSAGE_MAP(CLockSplitterWnd, CSplitterWnd)
ON_WM_LBUTTONDOWN()
ON_WM_SETCURSOR()
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()

//. . .

void CLockSplitterWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
if(m_bLocked)
CWnd::OnLButtonDown(nFlags, point);
else
CSplitterWnd::OnLButtonDown(nFlags, point);
}

BOOL CLockSplitterWnd::OnSetCursor(CWnd* pWnd,
UINT nHitTest, UINT message)
{
if(m_bLocked)
return CWnd::OnSetCursor(pWnd, nHitTest, message);
else
return CSplitterWnd::OnSetCursor(pWnd, nHitTest, message);
}

void CLockSplitterWnd::OnMouseMove(UINT nFlags, CPoint point)
{
if(m_bLocked)
CWnd::OnMouseMove(nFlags, point);
else
CSplitterWnd::OnMouseMove(nFlags, point);
}