Share via


C++ Q&A

List View Mode, SetForegroundWindow, and Class Protection

Paul DiLascia

Code download available at: CQA0403.exe (185 KB)
Browse the Code Online

Q I'm trying to customize the file open dialog using C++/MFC. Is there a way to change the list view type when the open/save dialog starts up? The default on startup is list view, which is not useful. I would really like my applications to start the dialogs with the details view—or even better, the last view used by the user. Can you recomment a way to do this?

Q I'm trying to customize the file open dialog using C++/MFC. Is there a way to change the list view type when the open/save dialog starts up? The default on startup is list view, which is not useful. I would really like my applications to start the dialogs with the details view—or even better, the last view used by the user. Can you recomment a way to do this?

Udi Mishan

A Of course! In Windows®, there's always a way. When I first read your question, I thought, that's easy. Just grab the list view in WM_INITDIALOG and set its view mode to details. But as so often happens in Windows, what seems logical doesn't work. There are three problems.

A Of course! In Windows®, there's always a way. When I first read your question, I thought, that's easy. Just grab the list view in WM_INITDIALOG and set its view mode to details. But as so often happens in Windows, what seems logical doesn't work. There are three problems.

Problem number one is getting hold of the list view. Several readers have asked me about this because it turns out to be non-trivial. As you can discover using Microsoft® Spy++, the list control isn't a direct child of the dialog, it's a grandchild. Figure 1 is a screen capture from Spy++ that reveals the true window hierarchy of the file open dialog. As you can see, the main dialog has a child window whose class name is SHELLDLL_DefView; this, in turn, contains the list control of files and folders. (I first mentioned SHELLDLL_DefView in my January 2002 column.) The ID of the SHELLDLL_DefView is lst2 (value 0x0461, defined in dlgs.h), but it's not a listbox or list control. The real SysListView32 is a child of SHELLDLL_DefView, with child ID=1.

Figure 1 Window Hierarchy

Figure 1** Window Hierarchy **

Problem number two is that the combined list control/SHELLDLL_DefView window doesn't exist yet when your dialog gets WM_INITDIALOG. It doesn't even exist when you get CDN_INITDONE, despite the fact that this message is supposed to mean that the open dialog has finished initializing. Oh well. This is empirical programming at its best: the only way to discover what Windows does is to conduct an experiment—or read MSDN® Magazine, of course! Figure 2 shows a test dialog I wrote to convince myself that the list control doesn't exist. CMyOpenDlg has a function called SetListView that does what its name claims. This function also displays TRACE diagnostics indicating whether it was able to find the list control. As the TRACE stream in Figure 3 shows, the list view doesn't exist when either WM_INITDIALOG or CDN_INITDONE arrives. In both cases, GetDlgItem returns NULL. So what can you do? The simplest workaround is to have your dialog post a message to itself:

BOOL CMyOpenDlg::OnInitDialog()
{
   CFileDialog::OnInitDialog();
   PostMessage(MYWM_POSTINIT,0,0);
   return TRUE;
}

Figure 2 Where's the List Control?

MyDlg.h

////////////////////////////////////////////////////////////////
// MSDN Magazine — March 2004
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual Studio .NET on Windows XP. Tab size=3.
//
// My open dialog — customized to set default view mode.
//
class CMyOpenDlg : public CFileDialog {
public:
   CMyOpenDlg();

protected:
   CListCtrl m_wndList;          // list view showing files and folders

   enum { ID_LISTVIEW = lst2 };

   // reverse-engineered command codes for SHELLDLL_DefView
   enum LISTVIEWCMD
   {  ODM_VIEW_ICONS = 0x7029,
      ODM_VIEW_LIST  = 0x702b,
      ODM_VIEW_DETAIL= 0x702c,
      ODM_VIEW_THUMBS= 0x702d,
      ODM_VIEW_TILES = 0x702e,
   };

   virtual BOOL OnInitDialog();         // handle init dialog
   virtual void OnInitDone();           // handle CDN_INITDONE
   afx_msg LRESULT OnPostInit(WPARAM wp, LPARAM lp); // the REAL 
                                                     // initialization

   BOOL SetListView(LISTVIEWCMD cmd);

   DECLARE_DYNAMIC(CMyOpenDlg)
   DECLARE_MESSAGE_MAP()
};

Figure 2 Where's the List Control?

MyDlg.cpp

////////////////////////////////////////////////////////////////
// MSDN Magazine — March 2004
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual Studio .NET on Windows XP. Tab size=3.
//
#include "StdAfx.h"
#include "MyDlg.h"
#include "Resource.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// self-initialization message posted
const MYWM_POSTINIT = WM_USER+1;

IMPLEMENT_DYNAMIC(CMyOpenDlg, CFileDialog)
BEGIN_MESSAGE_MAP(CMyOpenDlg, CFileDialog)
   ON_MESSAGE(MYWM_POSTINIT, OnPostInit)
END_MESSAGE_MAP()

////////////////
// ctor
//
CMyOpenDlg::CMyOpenDlg() : CFileDialog(TRUE)
{
   m_ofn.lpstrTitle = _T("Select a file or folder");
}

//////////////////
// Handle WM_INITDIALOG: shows that list view still not created yet.
//
BOOL CMyOpenDlg::OnInitDialog()
{
   TRACE(_T("CMyOpenDlg::OnInitDialog, hwnd=%p\n"), m_hWnd);
   CFileDialog::OnInitDialog();
   SetListView(ODM_VIEW_DETAIL); // this will fail
   PostMessage(MYWM_POSTINIT,0,0);
   return TRUE;
}

//////////////////
// Handle CDN_INITDONE: shows that list view still not created yet.
//
void CMyOpenDlg::OnInitDone()
{
   TRACE(_T("CMyOpenDlg::OnInitDone\n"));
   CFileDialog::OnInitDone();
   SetListView(ODM_VIEW_DETAIL); // this will fail
}

//////////////////
// Handle MYWN_POSTINIT: finally, the list view is created.
//
LRESULT CMyOpenDlg::OnPostInit(WPARAM wp, LPARAM lp)
{
   TRACE(_T("CMyOpenDlg::OnPostInit\n"));
   SetListView(ODM_VIEW_DETAIL); // this will succeed
   return 0;
}

//////////////////
// Change the list view to desired mode if the view exists.
// Display TRACE diagnostics either way.
//
BOOL CMyOpenDlg::SetListView(LISTVIEWCMD cmd)
{
   TRACE(_T("CMyOpenDlg::SetListView: "));

   // note that real dialog is my parent, not me
   CWnd* pshell = GetParent()->GetDlgItem(lst2);
   if (pshell) {
      TRACE(_T("hwnd=%p.\n"), m_wndList.GetSafeHwnd());
      pshell->SendMessage(WM_COMMAND, cmd);
      return TRUE;
   }
   TRACE(_T("failed.\n"),m_wndList.GetSafeHwnd());
   return FALSE;
}

Figure 3 Trace

Figure 3** Trace **

MYWM_POSTINIT is my own user-defined message (WM_USER+1) whose handler calls SetListView. Because OnInitDialog posts it with PostMessage instead of sending it with SendMessage, Windows doesn't process MYWM_POSTINIT until after all other pending messages have been processed. By that time, the open dialog has settled down and SetListView succeeds in obtaining the list control.

Which is where problem number three steps in. Once you finally have the list control, how do you set its view mode? You might think all you have to do is call SetView/LVM_SETVIEW. Alas, it doesn't work. The list view comes up blank. A paint update problem? Perhaps. But if you think about it, you'll realize that sending LVM_SETVIEW is fraught with peril. Remember the SHELLDLL_DefView window? It's not hard to imagine it maintains some kind of state information about its list control. If you go mucking with the list control directly, how will it know what you've done? In any case, it doesn't work. You have to find another way.

Figure 4 Using Spy++

Figure 4** Using Spy++ **

Fortunately, in this case that way is not hard to find. Once again Spy++ comes to the rescue. Spelunking with Spy++ reveals what happens when the user selects a different view, using the dropdown menu in Figure 4. The dialog sends a WM_COMMAND message to the SHELLDLL_DefView, with command ID = 0x702c. So to change the view to details, all you have to do is send a WM_COMMAND—to the shell window, not the list control:

CWnd* pshellwnd = dlg->GetDlgItem(lst2);
   pshellwnd->SendMessage(WM_COMMAND, ODM_VIEW_DETAIL); // 0x702c

After all the consternation, the final solution is easy. It's getting past all the quirks and gotchas that's the hard part. You can use Spy++ to examine the command codes for each view mode. Since I'm so nice, I did it for you. Figure 5 shows the results. I called these from Windows XP; other versions of Windows have one or two other codes you can discover on your own. Figure 2 shows the source for CMyOpenDlg, which invokes the file open dialog in details view. I leave it as an exercise for you to figure out how to persist the view mode across user sessions; the details are straightforward. As always, you can download the whole test program from the link at the top of this article).

Figure 5

Command ID (WPARAM) List View Mode
0x7029 Icons
0x702B List
0x702C Details
0x702D Thumbnails
0x702E Tiles

Q I'm trying to implement a program in C# using the Microsoft .NET Framework and I need to activate another window. In Windows/MFC I would call SetActiveWindow. How can I do this in the .NET Framework?

Q I'm trying to implement a program in C# using the Microsoft .NET Framework and I need to activate another window. In Windows/MFC I would call SetActiveWindow. How can I do this in the .NET Framework?

John McCormick

A You can call Form.Activate to activate your own form, but astonishingly there's no function in the .NET Framework to activate a window that belongs to another process or app. Never fear, you can always use interop to call Windows directly any time the .NET Framework fails to provide what you need. In this case, the function you want is SetForegroundWindow. It takes a single argument—the handle (HWND) of the window you want to activate:

using System.Runtime.InteropServices;
public class MyClass {
  [DllImport("user32.dll")]
  public static extern void 
    SetForegroundWindow(IntPtr hwnd);
}

A You can call Form.Activate to activate your own form, but astonishingly there's no function in the .NET Framework to activate a window that belongs to another process or app. Never fear, you can always use interop to call Windows directly any time the .NET Framework fails to provide what you need. In this case, the function you want is SetForegroundWindow. It takes a single argument—the handle (HWND) of the window you want to activate:

using System.Runtime.InteropServices;
public class MyClass {
  [DllImport("user32.dll")]
  public static extern void 
    SetForegroundWindow(IntPtr hwnd);
}

With this interop declaration in your code, and assuming you already have the window handle of the window you want to activate, all you have to do is call SetForegroundWindow:

IntPtr hwnd = // get HWND
SetForegroundWindow(hwnd);

How do you get the window handle? There are any number of ways depending how your app works, but the most common one is to call FindWindow, which lets you find a window based on its caption or class name. Here, again, you need to use interop. In C#, it looks like this:

public class MyClass {
  [DllImport("user32.dll")]
  public static extern IntPtr
    FindWindow(String classname, String title);
}

classname is the window's registered window class name and caption is the window's title (the caption text). Either of these parameters can be null, but not both.

Q How can I prevent other classes from being derived from my C++ class at compile time? For example, I have a class:

class MyClass { };

If anyone tries to declare a class like the following

class Derived : public MyClass { };

I want the compiler to throw an error. Can this be done?

Q How can I prevent other classes from being derived from my C++ class at compile time? For example, I have a class:

class MyClass { };

If anyone tries to declare a class like the following

class Derived : public MyClass { };

I want the compiler to throw an error. Can this be done?

Asha Udupa

A In C#, there's a keyword that does just what you want: sealed. When you make a C# class sealed, you tell the compiler that nobody can derive from this class.

A In C#, there's a keyword that does just what you want: sealed. When you make a C# class sealed, you tell the compiler that nobody can derive from this class.

For example,

sealed class MyClass { ... }

says that nobody can derive from MyClass. Many (some would say too many!) classes in the .NET Framework itself are sealed.

But you asked about C++, not C#. Alas, there's no sealed keyword in C++ (at least, not yet—but my sources tell me it will soon be added to the official standard). But there are relatively easy ways to achieve the same goal. Just declare all your constructors private:

class MyClass {
private:
  MyClass() { ... }
  MyClass(int arg) { ... }
};

Now there's no way to derive from MyClass because there's no way to instantiate it. But wait a minute—if there's no way to instantiate your class, how can anyone use it? Good question. The answer is you have to add static functions to create instances of your class:

class MyClass {
public:
  static MyClass* CreateInstance() { 
    return new MyClass(); 
  }
private:
  MyClass() { }
};

Now anyone can call MyClass::CreateInstance to create an instance of your class, but nobody can derive from it. This method works fine in most situations, but it has a drawback: it doesn't let you easily create instances of MyClass on the stack. For that, you need a slightly more complex solution:

class MakeSealed { 
private:
    MakeSealed () { } 
    friend class MyClass;
};

class MyClass : virtual MakeSealed { };

Now nobody can create an instance of MakeSealed except MyClass, which is its friend. You can create an instance of MyClass on the stack, but you can't derive from MyClass. You can use MakeSealed to make other classes sealed, but you have to add them as friends. MyClass uses MakeSealed as a virtual base class so you don't run into problems if you use multiple inheritance. Pretty clever, eh? Happy programming!

Send your questions and comments for Paul to  cppqa@microsoft.com.

Paul DiLascia is a freelance writer, consultant, and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). Paul can be reached at https://www.dilascia.com.