Multiple Projects and Include Paths

Posted by Matt | Filed under ,

One thing that I am struggling with in working in new development projects is how to deal with multiple vcproj projects and the include paths that go with them.  What is the best schema for dealing with include folders?  Currently, we copy files that we know are needed in other projects to a common include folder. 

For example, if we have projects A.vcproj and B.vcproj, and B.vcproj requires A.h from A.vcproj, then in A.vcproj we will have a custom build step for A.h to copy it to the common include folder.  B.vcproj will then add the common include folder to it’s include path.  This method avoids having to know which project and which directory contains the header file.  It works well for the 60 projects involved in our main solution file.

There is a caveat to this system:  sometimes it’s not possible to keep a strict dependency tree for projects.  For example, occasionally, there may be 2 projects which create a circular dependency based on include requirements.  We work very hard to avoid this for DLLs.  However, for LIBs, we are less strict.  As such, we cannot rely on one project’s header files being copied before it’s needed by another project.  Our solution was to create a pre-process build step that would parse the project files and perform the copy for all projects before the first one was built.

It’s not elegant, but it works.  And sometimes that’s what matters.

What I’m wondering now is how other companies deal with similar issues.  How do you deal with including files from other projects?  Do you use a common include folder?  Do you hard code the paths to the included files in your #include statement?  Or do you have another system that’s working well for you?

Allowing CView Classes to Handle Tab Navigation

Posted by Matt | Filed under , , ,

Working on a recent project, I wanted to give my CView-derived view class Tab navigation (like CFormView has).  By default, if you add CEdit and other controls to your CView-derived view, pressing Tab will do nothing.  Also, adding CStatic with & accelerators are ignored.

Doing some Googling, I found the following solution.  Add the following function to your CView-derived class:

BOOL CMyView::PreTranslateMessage(MSG* pMsg)
{
  if(IsDialogMessage(pMsg))
    return TRUE;
  else
    return CWnd::PreTranslateMessage(pMsg);
}

At first, it was working great.  However, I noticed that my overall application accelerators were not working.

Looking deeper into CFormView, I found the solution. The above code is not enough.  The following is a more complete solution.

BOOL CMyView::PreTranslateMessage(MSG* pMsg)
{
  ASSERT(pMsg != NULL);
  ASSERT_VALID(this);
  ASSERT(m_hWnd != NULL);

  // allow tooltip messages to be filtered
  if (CView::PreTranslateMessage(pMsg))
    return TRUE;

  // don't translate dialog messages when in Shift+F1 help mode
  CFrameWnd* pFrameWnd = GetTopLevelFrame();
  if (pFrameWnd != NULL && pFrameWnd->m_bHelpMode)
    return FALSE;

  // since 'IsDialogMessage' will eat frame window accelerators,
  //   we call all frame windows' PreTranslateMessage first
  pFrameWnd = GetParentFrame();   // start with first parent frame
  while (pFrameWnd != NULL)
  {
    // allow owner & frames to translate before IsDialogMessage does
    if (pFrameWnd->PreTranslateMessage(pMsg))
      return TRUE;

    // try parent frames until there are no parent frames
    pFrameWnd = pFrameWnd->GetParentFrame();
  }

  // don't call IsDialogMessage if form is empty
  if (::GetWindow(m_hWnd, GW_CHILD) == NULL)
    return FALSE;

  // filter both messages to dialog and from children
  return PreTranslateInput(pMsg);
}

I will admit, this code came directly from CFormView in the MFC source for Visual Studio 2008 (give credit where credit is due).

After including this code in my class, tab navigation is working as well as overall application accelerators.

Unresolved CLSID and IID identifiers

Posted by Matt | Filed under , ,

When using COM objects, occasionally, you may run into “LNK2001 unresolved external” errors.  This is often because the header file that declares the GUID for the COM object or interface is using DEFINE_GUID.  This macro is setup to declare the GUID as extern, meaning it needs to finally reside somewhere.  The error you are getting means that it has no home.

So solve this issue, include “InitGuid.h” before your header file that is declaring the GUID.  Ultimately, it actually needs to be before guiddef.h so you may need to include it in your precompiled headers if guiddef.h is being included somewhere in there.

In my case, I was including “Evalcom2.h” and CLSID_EvalCom2 and IID_IValidate were unresolved.  To solve it, I did this:

// So the GUIDs get defined (and not just declared)
#include <InitGuid.h>
#include <Evalcom2.h>

More information can be found on Microsoft’s page, here.

Template Functions, Part 2

Posted by Matt | Filed under , ,

In a previous post, I gave you the basics for template functions in C++.  One of the issues I find when I have a problem and am looking for examples of the solution is that many times the examples are so basic that they don’t really demonstrate the solution.  Today, I want to show you a complex solution that I had to create using templates.  Hopefully this will give you a better idea of what we can do.

In my real-world case, I had a potential file system of files and a database.  Files had to be defined in a data structure (read from Xml), traversed and put into a database.  The database contained different table definitions for different types of files (binary files, document files, etc.).

At first, the data structures looked like this:

class CBinaryFile
{
public:
  // Data

  typedef CList<CBinaryFile> List;
};

class CContentFile
{
public:
  // Data

  typedef CList<CContentFile> List;
};

class CFolder
{
public:
  CBinaryFile::List BinaryFiles;
  CContentFile::List ContentFiles;

  typedef CList<CFolder> List;

  CFolder::List Folders;
};

CFolder::List TheFolders;

You’ll notice that this is a recursive list of Folders that contains binary files and content files.  However, additional requirements came into play:  in memory, the binary file hierarchy needed to be separated from the content file hierarchy.  This means that I needed to have one set of folders for binary files and a different set of folders for content files.  I could have very well kept the data structure the same, but just had two top-level variables for the folders:

CFolder::List BinaryFolders;
CFolder::List ContentFolders;

Using this, I’d basically have wasted memory.  No one likes that :)

So I changed to use templates.  My CFolder class changed to this:

template <class T>
class CFolder
{
  T::List Files;

  typedef CList< CFolder<T> > List;

  CFolder::List Folders;
};

This way, my two top-level variables become:

CFolder<BinaryFile>::List BinaryFolders;
CFolder<ContentFile>::List BinaryFolders;

So far though, all I’ve shown you was class templates.  Where does the function template come into the picture?

I now need to traverse the folders and process the files into the database.  I did not want to add the functions to the class.  I’ll explain later.

void Traverse(CFolder<BinaryFile>::List &folders)
{
  // ...
}

void Traverse(CFolder<ContentFile>::List &folders)
{
  // ...
}

Again, duplication of code.  And we don’t want that either.  So I did this:

template <class T>
void Traverse(CFolder<T>::List &folders)
{
  // ...
}

This gave me a single function that does the work of traversing the folders.  But what about the end?  The 2 classes needed to be processed differently.  For this, I created an abstract item handler class that was implemented for each file type.  Here is the final code:

class CBinaryFile
{
public:
  // Data

  typedef CList<CBinaryFile> List;
};

class CContentFile
{
public:
  // Data

  typedef CList<CContentFile> List;
};

template <class T>
class CFolder
{
public:
  typename T::List Files;
	
  typedef CList< CFolder<T> > List;

  List Folders;
};

template<class T>
class IItemHandler
{
public:
  virtual void ProcessItem(T &item) = 0;
};

class BinaryFileHandler : public IItemHandler<CBinaryFile>
{
public:
  void ProcessItem(CBinaryFile &item)
  {
    // Insert into the database
  }
};

class ContentFileHandler : public IItemHandler<CContentFile>
{
public:
  void ProcessItem(CContentFile &item)
  {
    // Insert into the database
  }
};

template <class T>
void Traverse(typename CFolder<T>::List &folders, 
    IItemHandler<T> *pHandler)
{
  POSITION pos = folders.GetHeadPosition();
  while (pos != NULL)
  {
    CFolder<T> &folder = folders.GetNext(pos);

    // Process our items
    POSITION p2 = folder.Items.GetHeadPosition();
    while (p2 != NULL)
    {
      T &item = folder.Items.GetNext(pos);
      pHandler->ProcessItem(item);
    }

    // Do the sub-folders
    Traverse(folder.Folders, pHandler);
  }
}

BinaryFileHandler bfh;
Traverse(binaryFolders, &bfh);

ContentFileHandler cfh;
Traverse(contentFolders, &cfh);

Sometimes, it seems like alot of work and code just to avoid duplicating code or wasting memory.  These are judgement calls. The nice thing is that if I needed to add another file type later on, the groundwork is already done.  All I need to do is define my file type class and then create the handler class to insert it into the database.  Obviously, the more file types that are needed, the better looking this solution is.  It’s questionable whether this work is beneficial for only 2 file types.  But if you had 25 or 50, I think it’s obvious that this solution is better than code duplication.  Especially if you find a bug in the traversal code :)

Template Functions

Posted by Matt | Filed under , ,

Many people know about template classes in C++.  When you start working with templates, this is usually what you’re looking for.  The staple of template demonstrations is the linked list class.  This one is a common need from developers.  The good news is that the STL libraries implement this one very well.  Anyways, what many people don’t know about templates is that you can create template functions just as easily.  Sometimes, you may not even know you’re calling a template function.

Let’s create a min function.  It’s common to use macros, but macros don’t always maintain type-safeness.

template <class T>
T Min(T a, T b)
{
  if (b < a)
    return b;
  else
    return a;
}

You’ll notice that the first line is the same as when you’re creating a template class.  And just like a template class, you just reference your template parameter normally.

However, now, we can write code like this:

int a = 5;
int b = 6;
int m = Min(a, b);

This will generate a function Min using int as a base type.  And it’s type-safe.  You can easily change it to:

double a = 5;
double b = 6;
double m = Min(a, b);

This will call a double version of the same function.  If you wanted to do this without templates, you’d have to either (a) use macros, or (b) create 2 different functions.

The following code will generate an error:

int a = 5;
double b = 6;
int m = Min(a, b);

“error C2782: 'T Min(T,T)' : template parameter 'T' is ambiguous”

To fix the above issue, you can call Min as this instead:

int m = Min<int>(a, b);

This will disambiguate the parameters giving you a solid definition.  Note that b will be casted to an int as the call commences.

You get the idea now.  This is a very basic example.  I always hate it when you needs to see how to use something, but they always give very basic examples which don’t show the solution to the problem you’re having.  In an upcoming post, I’ll show you a much more complex solution using templates that I actually needed to develop.

Vista Style CFileDialog and the compiling OS

Posted by Matt | Filed under , , , ,

A reader asks:

“To set bVistaStyle to FALSE, under which OS, we need to compile my project.
[…]
in the MSDN I found that the “This parameter is applicable only if you are compiling in Windows Vista.
Please see the following MSDN link: http://msdn.microsoft.com/en-us/library/wh5hz49d.aspx
Kindly let us know your feedback on this.”

I took a look at the documentation and it does indeed say that.  However, the documentation is incorrect.

There are no #defines or #pragmas around the constructor for CFileDialog that would restrict the OS at compile time.  What they may be intending to say is that the parameter is applicable only if you are running in Windows Vista.

I wrote a test program that shows a CFileDialog and I explicity set the bVistaStyle flag to TRUE.  I Compiled it on XP and I got the XP-style file open dialog (which is to be expected).  I then copied the EXE to my Vista computer (I did not recompile).  There, I got the Vista style dialog.

So you can safely compile on XP and it will still show the Vista dialog on Vista.

Note that this is restricted to Visual Studio 2008.  The parameter is not present in Visual Studio 2005 (out of the box).

Selecting Multiple Files using CFileDialog, Properly

Posted by Matt | Filed under , , , ,

I ran into an issue today.  I had a CFileDialog object set to select multiple files (using OFN_ALLOWMULTISELECT).  When I launched the dialog, I selected 24 files.  However, after looping through the files using CFileDialog::GetNextPathName(), there were only 10 being returned.

The problem is that there wasn’t an error being set anywhere (that I could find).  CFileDialog was being quite sneaky in that it wasn’t giving me any signal that 14 of the selected files were basically being ignored.  At first, I thought it was just a bug in Windows.  After some thinking, it turns out that there’s an internal buffer used to store these files.  This may seem very obvious to some people, but it wasn’t obvious to me.

Part of the OPENFILENAME structure is a buffer for holding (a) the default file when the dialog is opened, and (b) the selected files when the dialog is closed.  This member is called lpstrFile.  nMaxFile is also used in conjunction to specify the length of the buffer pointed to by lpstrFile.

By default, CFileDialog uses a buffer 260 characters long.  In my problem above, 260 characters is basically able to only hold 10 of the selected files.  The rest were just dropped quietly.

The workaround is to use a larger buffer.  It’s quite easy to do this:

// Create our dialog object
CFileDialog dialog(TRUE, NULL, NULL, OFN_HIDEREADONLY | 
  OFN_ALLOWMULTISELECT);

// This is the trick
const int nBufferSize = 128*1024; // May be excessive?
TCHAR *szBuffer = new TCHAR[nBufferSize];
memset(szBuffer, 0, sizeof(TCHAR) * nBufferSize);
dialog.m_ofn.lpstrFile = szBuffer;
dialog.m_ofn.nMaxFile = nBufferSize - 1;

// Show the dialog
if (dialog.DoModal() == IDOK)
{
  POSITION pos = dialog.GetStartPosition();
  while (pos != NULL)
  {
    CString sFile = dialog.GetNextPathName(pos);
    // Process the file
  }
}

delete[] szBuffer; // No leaks

The trick is to set lpstrFile and nMaxFile to a larger buffer.  128k may be excessive, but at least it will work.  Feel free to use whatever size buffer you want to.

Note: Above, I created the buffer on the heap using ‘new’.  If you don’t want to deal with ‘new’ and ‘delete’, you could create it on the stack like:

TCHAR szBuffer[nBufferSize];

and skip the ‘delete’ call.  However, this will eat up a good chunk of your stack space.  The default stack is 1 MB.  So 128k is 10% of your stack.  For this reason, I created it on the heap.

Adding MFC to an ATL EXE Project

Posted by Matt | Filed under , , , ,

By default, when you create a new ATL EXE project, you are not given the chance to add MFC support to the project.  This is because Microsoft is giving developers a nice, tight framework for creating ATL servers.  Adding MFC could potentially slow things down.

However, there are times where MFC is required to be used by the ATL server, and using the ATL alternatives is not enough.  I ran into this exact scenario:  I had an existing codebase that I needed to be wrapped by a COM object.  I wanted to use an ATL EXE so that I didn’t have to deal with component services, etc.

I found this article from 2005, How to add MFC support to an ATL project in Visual C++, however this article is outdated and does not work for Visual Studio 2008.  What follows are the steps I found to have worked for my Visual Studio 2008 project.

1.  Add MFC support to the project settings

This is done on the General tab in project properties.  Set “Use of MFC” to either “Use MFC in a Shared DLL” or “Use MFC in a Static Library”.

2.  Add the MFC headers to stdafx.h

Add the following code to your stdafx.h file above the ATL header files but after _ATL_APARTMENT_THREADED:

#include <afxwin.h>   // MFC core and standard components
#include <afxext.h>   // MFC extensions
#include <afxdisp.h>  // MFC Automation extensions

3.  Remove the old COM module and WinMain

In your EXE’s main cpp file, remove the module object and the WinMain function.  However, hold onto the DECLARE_LIBID and DECLARE_REGISTRY_APPID_RESOURCEID lines.  You’ll need them in the next step.

4.  Add the new COM module and CWinApp

In the same file, add code like the following:

class CMyProjectModule : public CAtlMfcModule
{
public:
    DECLARE_LIBID(LIBID_MyProject)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYPROJECT,
        "{00000000-0000-0000-0000-000000000000}")
};

CMyProjectModule _AtlModule;

class CMyProjectApp : public CWinApp
{
public:
    virtual BOOL InitInstance();
    virtual int ExitInstance();
};

CMyProjectApp theApp;

BOOL CMyProjectApp::InitInstance()
{
    // Initialize OLE libraries.
    if (!AfxOleInit())
    {
        AfxMessageBox(_T("OLE Initialization Failed!"));
        return FALSE;
    }

    // Parse command line for standard shell commands, 
    // DDE, file open
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);
#if !defined(_WIN32_WCE) || defined(_CE_DCOM)
    // Register class factories via CoRegisterClassObject().
    if (FAILED(_AtlModule.RegisterClassObjects(CLSCTX_LOCAL_SERVER, 
            REGCLS_MULTIPLEUSE)))
        return FALSE;
#endif // !defined(_WIN32_WCE) || defined(_CE_DCOM)
    //// App was launched with /Embedding or /Automation switch.
    //// Run app as automation server.
    if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated)
    {
        // Don't show the main window
        return TRUE;
    }
    // App was launched with /Unregserver or /Unregister switch.
    if (cmdInfo.m_nShellCommand == CCommandLineInfo::AppUnregister)
    {
        _AtlModule.UpdateRegistryAppId(FALSE);
        _AtlModule.UnregisterServer(TRUE);
        return FALSE;
    }
    // App was launched with /Register or /Regserver switch.
    if (cmdInfo.m_nShellCommand == CCommandLineInfo::AppRegister)
    {
        _AtlModule.UpdateRegistryAppId(TRUE);
        _AtlModule.RegisterServer(TRUE);
        return FALSE;
    }

    return TRUE;
}

int CMyProjectApp::ExitInstance()
{
#if !defined(_WIN32_WCE) || defined(_CE_DCOM)
    _AtlModule.RevokeClassObjects();
#endif
    return CWinApp::ExitInstance();
}

The DECLARE_LIBID and DECLARE_REGISTRY_APPID_RESOURCE should be copied from what was saved in step 3.

5.  Use AFX_MANAGE_STATE

You’ll need to make use of the following in your exported functions:

AFX_MANAGE_STATE(AfxGetAppModuleState());

That should do it.  Compile and link and you should have MFC support.

One problem that I am working on is that after this change, the EXE shuts down immediately after the last COM object has been destroyed.  I am trying to figure out how to maintain the timeout feature that the original ATL EXE project had.

Fixing up MFC Office 2007 Ribbon Applications, Part 3

Posted by Matt | Filed under , , , ,

In my continuing series, I am providing some workarounds/solutions to some issues in the Visual Studio 2008 SP1 projects created that use the new “MFC Feature Pack”.

Today’s issue is Print Preview.

In a new Office 2007 Ribbon application, if you tell the MFC Application Wizard to not include Print Preview support, it will still include it in the File/Application menu.  Once you create your new project, you’ll still see Print, Print Preview, etc. in the File menu (by clicking the “Pearl” on the top-left corner of the window).

To fix this, locate and remove the following code in MainFrm.cpp:

bNameValid = strTemp.LoadString(IDS_RIBBON_PRINT);
ASSERT(bNameValid);
CMFCRibbonButton* pBtnPrint = 
	new CMFCRibbonButton(ID_FILE_PRINT, 
		strTemp, 6, 6);
pBtnPrint->SetKeys(_T("p"), _T("w"));
bNameValid = strTemp.LoadString(IDS_RIBBON_PRINT_LABEL);
ASSERT(bNameValid);
pBtnPrint->AddSubItem(new CMFCRibbonLabel(strTemp));
bNameValid = strTemp.LoadString(IDS_RIBBON_PRINT_QUICK);
ASSERT(bNameValid);
pBtnPrint->AddSubItem(
	new CMFCRibbonButton(ID_FILE_PRINT_DIRECT, 
		strTemp, 7, 7, TRUE));
bNameValid = strTemp.LoadString(IDS_RIBBON_PRINT_PREVIEW);
ASSERT(bNameValid);
pBtnPrint->AddSubItem(
	new CMFCRibbonButton(ID_FILE_PRINT_PREVIEW, 
		strTemp, 8, 8, TRUE));
bNameValid = strTemp.LoadString(IDS_RIBBON_PRINT_SETUP);
ASSERT(bNameValid);
pBtnPrint->AddSubItem(
	new CMFCRibbonButton(ID_FILE_PRINT_SETUP, 
		strTemp, 11, 11, TRUE));
pMainPanel->Add(pBtnPrint);

Also, when you run the application, if you customize the Quick Access Toolbar, you’ll notice that Print Preview commands are still available.  To remove this, add the following code just after the ribbon is created, before the call to CMainFrame::InitializeRibbon():

m_wndRibbonBar.EnablePrintPreview(FALSE);

I have submitted the issue to Microsoft.  You can view the issue report here.

Fixing up MFC Office 2007 Ribbon Applications, Part 2

Posted by Matt | Filed under , , , ,

In my continuing series, I am providing some workarounds/solutions to some issues in the Visual Studio 2008 SP1 projects created that use the new “MFC Feature Pack”.

Today’s issue is the recent file list.

If you create a new project using the MFC Application Wizard, and you specify an Office 2007 Ribbon user interface, then your application will be created with the ability to have a recent file list.  This list is located in the File or “Application” menu (the “Pearl” in the top-left corner of the application).  The space is there, but if you run your application, you’ll notice that the recent file list is never populated with files you open or save.

The reason for this is because the project was created using a maximum of 0 files for the recent file list.  Take a look in your application’s main .cpp file in InitInstance() of your class.  You’ll notice a call to LoadStdProfileSettings().  It is passing as a parameter ‘0’.  This means to make the recent file list 0 elements long, or empty. 

To fix this issue, set the parameter to LoadStdProfileSettings() to something realistic, or remove the parameter completely since it has a default value of 4.

In summary: you need to change this:

LoadStdProfileSettings(0);  // Load standard INI file options

to:

LoadStdProfileSettings();  // Load standard INI file options

I have submitted the issue to Microsoft.  You can view the issue report here.