Apr 152009
 

What’s CMFCEditBrowseCtrl ?

CMFCEditBrowseCtrl is a specialized edit control (MFC Feature Pack VS2008) with a browse button attached to it’s right side, when we click on this button we get an open file dialog or an open folder dialog. See this sample screenshot.

Browse edit control sample

Browse edit control sample

Also this control allows us to implement our own event handling by overriding OnBrowse function of this class. This a cool control which I like a bit too much since I know how painful it is to implement one.

Some tips on how to make such a control…

  1. Handle nc calcsize event, so that you can specify size of the area that the browse button will take which in turn results in edit control resizing it’s client area to adjust the button.
  2. Handle nc paint to draw the button, also you have to force generate an nc calcsize message for once in the beginning.
  3. Handle mouse up and mouse down event.

Usage of CMFCEditBrowseCtrl

  1. You should be working in VS2008 SP1 or with Feature pack installation
  2. Add an edit control to a dialog
  3. Open .h file of this dialog’s class and add a member variable – CMFCEditBrowseCtrl m_EditBrowse;
  4. Open .cpp file and add a call to sub class this item in DoDataExchange
    DDX_Control( pDX, IDC_EDIT_FILEBROWSE, m_EditBrowse);
  5. Then in OnInitDialog
    // Note: Only one of these calls will work at a time!
    m_EditBrowse.EnableFileBrowseButton(); // To show file open dialog
    m_EditBrowse.EnableFolderBrowseButton(); // To show folder browse dialog
    m_EditBrowse.EnableBrowseButton(); // To do custom event handling
  6. That’s it, now you’ve got the browse edit working. 🙂
  7. Note that above calls take some parameters (which has default values) look up in MSDN.

Custom event handling for CMFCEditBrowseCtrl browse button

Here is a small sample on how to handle custom browse button event handling, taken from MSDN samples…

class CMyBrowseEdit : public CMFCEditBrowseCtrl
{
    virtual void OnBrowse()
    {
        MessageBox(_T("Browse item..."));
        SetWindowText(_T("New value!"));
    }
};

More on CMFCEditBrowseCtrl…

Use SetBrowseButtonImage to change browse button image, also has an option to maintain such a bitmap or an icon. There are some more virtual functions which could be useful…

  1. OnDrawBrowseButton – Override to for some additional painting from your side
  2. OnChangeLayout – Called when browse button mode changes, i.e. from folder to file mode etc (I guess so). 🙂
Mar 232009
 

When working in MFC/ATL and then using a resource dll can at times cause bugs because resource handle is not properly set. For e.g. if you have a dialog resource in a dll and you instantiate a CDialog which results in an error because resource cannot be located in this application or dll since it’s in the resource dll. So solution for above problem will be to set correct resource handle.

MFC provides two functions…

  1. AfxGetResourceHandle
  2. AfxSetResourceHandle

pretty obvious names. 🙂

So we should first save our current resource handle and then set new resource handle before loading such a resource. Also don’t forget to set our old handle since its good practice.

Sample code snippet.

extern HINSTANCE hResDll = NULL;
HINSTANCE hDefInstance = AfxGetResourceHandle();
AfxSetResourceHandle(hResDll);
CBitmap Bmp;
Bmp.LoadBitmap( IDB_NIBUS_FACE );

AfxSetResourceHandle(hDefInstance);

Internally MFC calls FindResource and LoadResource using this handle, so if it’s not properly set this will cause failure in loading resources.

In ATL it’s quite similar except that we’ve got a new function called AddResourceInstance which adds our new resource handle to existing list of handles. So when a look up is done given resource handle is also used. Following functions are provided in ATL to work with resource handles…

  1. AddResourceInstance
  2. GetResourceInstance – Similar to AfxGetResourceHandle
  3. SetResourceInstance – Similar to AfxGetSetResourceHandle

For newbies this is always a painful bug to resolve as they don’t know what went wrong since they expect this to be automagically done.

Mar 112009
 

Recently I faced an issue in one of my project code bases. There is a worker thread (created using CreateThread native API) which calls AfxGetMainWnd() MFC function and since worker threads doesn’t have a main window associated with it, CWnd::Create( …WS_CHILD… ) call fails. A window created with WS_CHILD needs a parent window but AfxGetMainWnd() returned NULL hence the crash.

So I searched using Google and found suggestions ranging from sending window handle as the custom parameter to the thread and then passing this on to the function were the Create function call is made! But that call is about 20 functions deep :roll:. The calls go from one class to another, this means I’ve got to carry this darn handle that far, naah I decided, I’m not going to do that. So I set about exploring other possibilities.

Then a pretty cool thought 💡 struck me, AfxGetMainWnd() calls AfxGetThread(), which returns NULL which in turn results in a NULL window handle. So I thought a possible solution will be to create a CWinThread object using AfxBeginThread(WorkerThread). Then another novel thought came that I can set the main window’s handle to m_pMainWnd member variable of this CWinThread object…

CWinThread* pThread = AfxBeginThread( (AFX_THREADPROC)ThreadProc,
                                      (LPVOID)this,
                                      THREAD_PRIORITY_NORMAL,
                                      0,
                                      CREATE_SUSPENDED,
                                      NULL );
pThread->m_pMainWnd = AfxGetMainWnd(); // Possibly already set by above call
pThread->ResumeThread();

So this does the trick for me and my project. Now calls to AfxGetMainWnd in this worker thread succeeds, haven’t seen any issue with this so far. If you find any, please post as a comment.

Mar 022009
 

Is there any way to handle child window messages in a parent window? Yes you can use WM_PARENTNOTFIY message or OnParentNotify MFC handler. Note that this message is sent or this function is only called when WS_EX_NOPARENTNOTIFY style is not set. A child controls notifies it’s parent window on…

  • Creation
  • Destruction
  • Mouse button clicks

Please go through following MSDN links for more information…

  1. WM_PARENTNOTIFY
  2. OnParentNotify
  3. A thread in MSDN forums on this topic
Feb 272009
 

SHGetFolderPath is the API that we use to retrieve special folder paths.  Some examples of special folders are as follows…

  • My Documents
  • My Pictures
  • Windows directory
  • System directory
  • Program files
  • Internet history directory
  • Cookies directory
  • etc…

So here is a small useful function which retrieves path of any special directory documented for SHGetFolderPath API in MSDN.

CString GetSpecialFolderPath( const DWORD csidl )
{
   HANDLE ProcToken = NULL;
   VERIFY( OpenProcessToken( GetCurrentProcess(), TOKEN_READ, &ProcToken ));

   TCHAR szBuffer[MAX_PATH*2] = { 0 };
   SHGetFolderPath( NULL, csidl, ProcToken, SHGFP_TYPE_CURRENT, szBuffer );
   CString SpecialFolderPath = szBuffer;

   CloseHandle( ProcToken );

   return SpecialFolderPath;
}

// Usage
int main()
{
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; } // Get my documents folder path const CString MyDocuments = GetSpecialFolderPath( CSIDL_PERSONAL ); ::MessageBox( NULL, MyDocuments, "MyDocuments Path", MB_OK | MB_ICONINFORMATION ); // Get Program files path const CString ProgramFiles = GetSpecialFolderPath( CSIDL_PROGRAM_FILES ); ::MessageBox( NULL, ProgramFiles, "Program files", MB_OK | MB_ICONINFORMATION ); // Get internet history path const CString HistoryFolder = GetSpecialFolderPath( CSIDL_HISTORY ); ::MessageBox( NULL, HistoryFolder, "History folder", MB_OK | MB_ICONINFORMATION ); return 0; }[/sourcecode] There may be times when a special folder might not be present, so for that case you can combine CSIDL_FLAG_CREATE with other CSIDL flags. For example if you want to create my documents folder if there is no such one present then we'll call "GetSpecialFolderPath" likewise... [sourcecode language='cpp']// You can be sure that my documents will be present this way CString MyDocuments = GetSpecialFolderPath( CSIDL_PERSONAL | CSIDL_FLAG_CREATE ); // Now create a file in this directory MyDocuments += "/Learning.txt"; // CreateFile [/sourcecode] Above function is extremely easy to use but internally it opens and closes a token handle, so if you want to retrieve multiple entries then you can provide a token handle which may increase performance. Return values from ShGetFolderPath is interpreted as follows... S_FALSE -> SHGetFolderPathA only. The CSIDL in nFolder is valid, but the folder does not exist. Note that the failure code is different for the ANSI and Unicode versions of this function. E_FAIL -> SHGetFolderPathW only. The CSIDL in nFolder is valid, but the folder does not exist. Note that the failure code is different for the ANSI and Unicode versions of this function. E_INVALIDARG -> The CSIDL in nFolder is not valid.

Feb 172009
 

Use following functions

  1. _strupr
  2. _strlwr
  3. std::transform – does the trick too but hard to understand
/* STRLWR.C: This program uses _strlwr and _strupr to create
 * uppercase and lowercase copies of a mixed-case string.
 */

#include <string .h>
#include <stdio .h>

void main( void )
{
   char str[100] = "The String to End All Strings!";
   printf( "Mixed: %s\n", str );
   printf( "Lower: %s\n", _strlwr( str ));
   printf( "Upper: %s\n", _strupr( str ));
}

// Output
// Mixed: The String to End All Strings!
// Lower: the string to end all strings!
// Upper: THE STRING TO END ALL STRINGS!

How about converting std::string to upper or lower case?

// Convert std::string to upper or lower case
std::string teststr = “Nibu Babu Thomas”;
_strlwr( &teststr[0] );
cout << endl << teststr.c_str() << endl; _strupr( &teststr[0] ); cout << teststr.c_str() << endl;[/sourcecode] How about converting 'CString' to upper or lower case? Fortunately and wisely enough there are member functions called 'MakeLower', 'MakeUpper'. Phew! [sourcecode language='cpp']CString csTest = _T( "Nibu Babu Thomas" ); csTest.MakeUpper();// Now in upper case csTest.MakeLower(); // Now in lower case[/sourcecode]

Jan 232009
 

Many of you must have faced this obscure problem, it’s because you haven’t called AfxInitRichEdit/AfxInitRichEdit2() function.

Wait! Before you jump onto add this call identify your rich edit control’s class name, if you are working in VC6 then rich edit class name will be RICHEDIT and or if VC7 and above then it will be RichEdit20A. So if the class name is RICHEDIT then call AfxInitRichEdit(), else if the class name is RichEdit20A then call AfxInitRichEdit2().

Jan 152009
 

Recently a colleague of mine asked this question, he added a message map entry for LBN_SELCHANGE but still no notification message arrived and also this list box was dynamically created using CListBox::Create function.

The reason was that he missed out a style id while creating this window. LBS_NOTIFY should be specified when creating a list box if parent window intends to receive notification messages. List box created using resource editor has this option turned on by default.

Some other controls with a similar style id are as follows…

  1. Button control has BS_NOTIFY style which enables a button to send BN_DBLCLK, BN_KILLFOCUS, and BN_SETFOCUS notification messages to its parent window. Note that buttons send the BN_CLICKED notification message regardless of whether it has this style.
  2. Static control has SS_NOTIFY style which enables it to send it’s parent window STN_CLICKED, STN_DBLCLK, STN_DISABLE, and STN_ENABLE notification messages when the user clicks or double-clicks the control.
Jan 072009
 

So what are stock objects? “Stock” as the name says means goods in hand, which a shop keeper always has as a backup. In the same way GDI also keep a set of backup of objects which it creates during initialization and these are known as stock objects. Let’s think of a case like this… you are creating a fancy brush but somehow the creation fails, so what will be your backup policy, mine would be to use a stock brush, for e.g. BLACK_BRUSH since these objects are always available. Think of a case where you have to quickly create a black brush, I would go for stock object BLACK_BRUSH.

Following are the stock objects that I know, taken straight out of MSDN

Value Meaning
BLACK_BRUSH Black brush.
DKGRAY_BRUSH Dark gray brush.
DC_BRUSH Windows 2000/XP: Solid color brush. The default color is white. The color can be changed by using the SetDCBrushColor function. For more information, see the Remarks section.
GRAY_BRUSH Gray brush.
HOLLOW_BRUSH Hollow brush (equivalent to NULL_BRUSH).
LTGRAY_BRUSH Light gray brush.
NULL_BRUSH Null brush (equivalent to HOLLOW_BRUSH).
WHITE_BRUSH White brush.
BLACK_PEN Black pen.
DC_PEN Windows 2000/XP: Solid pen color. The default color is white. The color can be changed by using the SetDCPenColor function. For more information, see the Remarks section.
NULL_PEN Empty pen.
WHITE_PEN White pen.
ANSI_FIXED_FONT Windows fixed-pitch (monospace) system font.
ANSI_VAR_FONT Windows variable-pitch (proportional space) system font.
DEVICE_DEFAULT_FONT Windows NT/2000/XP: Device-dependent font.
DEFAULT_GUI_FONT Default font for user interface objects such as menus and dialog boxes. This is MS Sans Serif. Compare this with SYSTEM_FONT.
OEM_FIXED_FONT Original equipment manufacturer (OEM) dependent fixed-pitch (monospace) font.
SYSTEM_FONT System font. By default, the system uses the system font to draw menus, dialog box controls, and text.Windows 95/98 and Windows NT: The system font is MS Sans Serif.Windows 2000/XP: The system font is Tahoma
SYSTEM_FIXED_FONT Fixed-pitch (monospace) system font. This stock object is provided only for compatibility with 16-bit Windows versions earlier than 3.0.
DEFAULT_PALETTE Default palette. This palette consists of the static colors in the system palette.

If you look at above table, we can see that almost all GDI object classes (exceptions are regions and bitmaps) has a backup stock object, mainly to act as a fallback method just in case of a failure IMO.

To use retrieve a stock object we call function GetStockObject. The good thing about working with a stock object is that we don’t have to delete it, since we didn’t create it. Now think how easy it’s to create a black pen, or black brush. No overheads to delete/free them. It’s definitely faster too.

There is a brush called NULL_BRUSH, a pen called NULL_PEN which are quite useful to draw a hollow rectangle without any fill, instead of selecting a brush equivalent to background window color to simulate drawing a rectangle without any fill.

CDC class of MFC has a very easy to use function for stock object. It’s CDC::SelectStockObject. I’m going nuts for this function. 😉 Take a look at these calls….

// Select a black brush
CDC ClientDC;
ClientDC.Attach( ::GetDC( m_hWnd ));

const int RestorePoint = ClientDC.SaveDC();

// Select a black brush
ClientDC.SelectStockObject( BLACK_BRUSH );
// Select a black pen
ClientDC.SelectStockObject( BLACK_PEN );
// Select existing font
ClientDC.SelectStockObject( <strong>DEVICE_DEFAULT_FONT</strong> );

ClientDC.RestoreDC( RestorePoint );

See how easy it’s to use these stock objects. No overhead of creating or freeing them, but as I already said these are just for backup action purpose. For fancy stuff you of course have to go for other functions.

So bottom line is stock objects are just to avoid cases were you end up with no brush to paint or no pen to write. 🙂

Jan 052009
 

Why Change CListCtrl Row Height

I’ve seen in forums numerous times people asking this query: How to change the row height of list control? Answers vary from , owner drawing to adding a fake image list with tall images. 🙂

Well so I decided to find out a way to do this in a proper way.  So here is a way to do this. This works both ways through a dialog resource and through dynamic creation. Note that this method uses owner drawing “for a moment”. 😉

Here is a screen shot before applying this method…

CListCtrl before changing row height

Demo via Dialog Resource

Well so follow these steps if you are using a dialog resource with a CListCtrl

  1. Add a list control to this resource
  2. Change the style of this list control to “owner draw fixed” style!
  3. Now override OnMeasureItem method of your dialog class, note that this method is not to be overridden in you CListCtrl class, you need not have a derived class of CListCtrl.
    OnMeasureItem prototype:
    afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct);
  4. Now inside OnMeasureItem add this piece of code, this is how mine looks…
    void CDialogGuineaPig::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
    {
       // If this is our list control then increase height
       if( nIDCtl == IDC_ROWLIST )
       {
          // Increase height by 20
          lpMeasureItemStruct->itemHeight += 20;
       }
    }
  5. Now goto OnInitDialog, this is how my OnInitDialog section for implementing this behavior looks…
    BOOL CDialogGuineaPig::OnInitDialog()
    {
        // Insert two columns and two rows...
        m_RowList.InsertColumn( 1, TEXT( "First column" ), LVCFMT_LEFT, 200, 0 );
        m_RowList.InsertColumn( 2, TEXT( "Second column" ), LVCFMT_LEFT, 200, 1 );
        m_RowList.InsertItem( 0, _T( "Row1 Column1" ), 0 );
        m_RowList.SetItemText( 0, 1, _T( "Row1 Column2" ));
        m_RowList.InsertItem( 1, _T( "Row2 Column1" ), 0 );
        m_RowList.SetItemText( 1, 1, _T( "Row2 Column2" ));
    
        // Remove owner drawn style, our job is done,
        // we're tricking the framework into thinking that
        // we wanna owner draw this control so that we get
        // a call to OnMeasureItem.
        m_RowList.ModifyStyle( LVS_OWNERDRAWFIXED, 0, 0 );
    
        // Just to show the change in row height
        m_RowList.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES );
    
        return FALSE;  // return TRUE  unless you set the focus to a control
    }
  6. That’s it, we now have a list control with rows having bigger height. Here is a screen shot of the resultant list control…CListCtrl after changing row height

Demo via Dynamic Creation

Now with dynamic creation, make sure specify LVS_OWNERDRAWFIXED style while creating so that OnMeasureItem is called by the framework, after which remove this style. Similar to dialog resource method.

So to summarize in one line what we’ve done… we turned on owner drawn style temporarily so that window framework calls OnMeasureItem, so that we can pass our own height value and then we’ve turned off this style so that the windowing framework does the drawing stuff instead of us.

Have fun programming…