Solved

Long file names in DlgDirList

Posted on 2000-03-21
5
348 Views
Last Modified: 2012-08-13
I am using the DlgDirList function to display the contents of directories but it does not display long file names it uses the ~ symbol to shorten it.  Is it possible to display the full name?
0
Comment
Question by:Richard_Eustace
  • 2
  • 2
5 Comments
 
LVL 22

Accepted Solution

by:
nietod earned 100 total points
ID: 2639857
From the VC help

************

 QI am trying to fill a list box with filenames using the MFC function CWnd:: DlgDirList. I am using MFC 4.0 and Windows" 95. The list box fills fine, but instead of long filenames I get the short MS-DOS names. How can I get long filenames in my list box?

Charles Parker

ACWnd::DlgDirList is a wrapper for the Windows API DlgDirList. Under Microsoft" Windows 95, this function thunks down to the 16-bit version of DlgDirList in USER.EXE. In other words, DlgDirList is really a 16-bit function inherited from Windows 3.1. Because DlgDirList was never ported, it displays the old MS-DOS filenames. (The version of DlgDirList in Windows NT" works fine, as you would expect, since Windows NT has only long filenames.) PRB report Q131286 in the MSDN CD suggests converting your path spec to its short name by calling GetShortPathName before you pass the file spec to DlgDirList, but the PRB doesn't say how to make long names appear in your list box. In other words, the friendly Redmondtonians have cleverly spun the problem as not recognizing long filename specs rather than not displaying long filenames. They admit the flaw and then assert in their typically cryptic fashion, "this behavior is by design," without giving any reason. I suspect it has something to do with compatibility with old apps; maybe they were afraid some old programs that allocated thirteen bytes for 8.3 filenames (plus a zero at the end) would break when the list box got back too many characters under Windows 95. You'd think they'd at least give you a new flag or something to get the long filenames.

In any case, the question is what can you do about it? Frankly, not much. You have to forget about DlgDirList and generate the list yourself. Fortunately, it's pretty easy. Two API functions provide all the interface you need to navigate the files in a directory that match a file spec like *.* or *.txt: _findfirst and _findnext, both defined in <io.h>. (Yes, it still exists, even in the land of Windows.) These functions fill a C struct called _finddata_t.





 struct _finddata_t {
    unsigned    attrib;
    time_t    time_create; /*-1forFATfilesystems*/
    time_t    time_access; /*-1forFATfilesystems */
    time_t    time_write;
    _fsize_t  size;
    char    name[260];
};

The attrib member is a flag field that may contain any combination of the following flags:





 _A_NORMAL /* Normalfile-Noread/writerestrictions */
_A_RDONLY /* Read only file */
_A_HIDDEN /* Hidden file */
_A_SYSTEM /* System file */
_A_SUBDIR /* Subdirectory */
_A_ARCH   /* Archive file */

The various time_t members are what you'd expect; _fsize_t is the size of the file, and "name" is the filename relative to the current working directory. To use the findfile functions, you first call _findfirst with a file spec.





 _finddata_t fdata;
long ffhandle = _findfirst(*.txt, &fdata);

This fills fdata with information about the first file in the current directory that matches *.txt. If such a file exists, its information is stored in the _finddata_t struct and ffhandle gets a value other than -1. Usually, you'll examine the attribute bytes to skip over files such as directories or hidden/system files-unless that's what you're interested in-and then call _findnext repeatedly to get information about other files that match your file spec. When there are no more files, _findnext returns a nonzero value, which is your cue to call _findclose to release the ffhandle. _findfirst and company are old functions that you probably know and love from MS-DOS, but they're still important, at times even essential, in Windows.

Since this is a C++ column, I wrote a C++ class, CFileSpec, that encapsulates the findfile API (see Figure 1). CFileSpec is entirely self contained; it has nothing to do with list boxes. I use CFileSpec all the time, usually in MS-DOS-shell utility programs that do things with files. For example, I wrote a Unix-like wc (word count) utility using CFileSpec that counts pages as well as lines, words, and characters.

CFileSpec is implemented entirely within a single header file, filespec.h, so all I have to do is #include it in any app that uses it. I don't have to worry about linking with an object file or library.

Figure 1 FILESPEC.H




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

#ifndef BOOL
typedef int BOOL;
#endif

//////////////////
// Class to to navigate files that match a spec with wildcards
//
class CFileSpec : public _finddata_t {
      long            m_hfile;            // handle from findfirst

public:
       CFileSpec() {
            m_hfile = -1;
      }

      ~CFileSpec() {
            if (m_hfile>=0)
                  _findclose(m_hfile);
      }

      BOOL First(const char* filespec) {
            if (m_hfile>=0)
                  _findclose(m_hfile);
            return (m_hfile=_findfirst((char*)filespec, this)) != -1;
      }

      BOOL Next() {
            return m_hfile>=0 && _findnext(m_hfile, this)==0;
      }
};

The implementation itself is mostly straightforward. There are only two tricks you may not notice right away. First, I derived CFileSpec from _finddata_t, which is why CFileSpec::First and Next pass the this pointer as the second argument to _findfirst and _findnext. I use this tactic only because it makes accessing the information in _finddata_t easier. For example, you can write filespec.attrib to get the attribute byte or filespec.name to get the filename. This is marginally easier than writing something like filespec.data.name, which is what it would be if I made _finddata_t a member instead of a base class.

The second trick is that there's no Close function to mirror _findclose. Instead, CFileSpec closes the handle in its destructor. Generally, I use CFileSpec to enumerate the files matching a single spec. However, just in case someone tries to call First with another file spec, CFileSpec::First also closes the findfile handle if it's already open.

To test CFileSpec, I wrote a program called DIRLIST that compares the filenames generated by ::DlgDirList and CFileSpec (see Figure 2). As you can see from Figure 3, DlgDirList produces the short MS-DOS names, whereas CFileSpec gives you the long filenames. The main action in DIRLIST occurs when the dialog is initialized. The dialog's OnInitDialog handler subclasses the list boxes and calls CListBox::DlgDirList (actually it's in CWnd) for one list box. It uses CFileSpec to fill the other:





 CFileSpec spec;
for (BOOL more=spec.First("*.*"); more; more=spec.Next()) {
    if(!(spec.attrib&(_A_SUBDIR|_A_HIDDEN|_A_SYSTEM)))
    m_lb2.AddString(spec.name);
}

Figure 2 DIRLIST.CPP




 ////////////////////////////////////////////////////////////////
// DIRLIST Copyright 1996 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// DIRLIST illustrates how to fill a list box with long filenames.


#include "stdafx.h"
#include "resource.h"
#include "filespec.h"

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

////////////////////////////////////////////////////////////////
// Dialog class
//
class CMyDialog : public CDialog {
      CListBox m_lb1;
      CListBox m_lb2;
public:
      CMyDialog();
      virtual BOOL OnInitDialog();
      DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
END_MESSAGE_MAP()

CMyDialog::CMyDialog() : CDialog(IDD_DIALOG1)
{
}

////////////////
// Initialize dialog: Subclass the edit controls
//
BOOL CMyDialog::OnInitDialog()
{
      BOOL bRet = CDialog::OnInitDialog();
      m_lb1.SubclassDlgItem(IDC_LIST1, this);
      m_lb2.SubclassDlgItem(IDC_LIST2, this);

      // Initialize list box #1 using DlgDirList
      char dir[128] = "*.*";
      DlgDirList(dir, IDC_LIST1, 0, DDL_READWRITE);

      // Initialize list box #2 using CFileSpec
      CFileSpec spec;
      for (BOOL more=spec.First("*.*"); more; more=spec.Next()) {
            if (spec.attrib & (_A_SUBDIR|_A_HIDDEN|_A_SYSTEM))
                  continue;
            m_lb2.AddString(spec.name);
      }
      return bRet;
}

////////////////////////////////////////////////////////////////
// Application class



Figure 3 Long filenames ahoy!

This code adds all the "normal" files in the current directory to my list box. That is, names that do not represent hidden or system files or the names of subdirectories. It's a little more work than calling DlgDirList, but at least you get the long filenames.

As an enhancement to CFileSpec, you could add another argument to CFileSpec::First that represents an attribute mask, which you would use to filter out files the way DlgDirList does. With this added functionality, you wouldn't have to check the attribute byte in your app since it would be part of CFileSpec. You could even derive a general-purpose enumerator class from CFileSpec. Such a class would have a function CFileEnumerator::Enumerate(const char* filespec)-perhaps with an attribute mask-and would do the First/Next enumeration, calling some other virtual function (something like CFileEnumerator::Action) for each filename. CFileEnumerator::Action would be abstract; that is, there would be no implementation in CFileEnumerator. Programmers using the file enumerator would have to derive from CFileEnumerator and implement their own Action function to do whatever they want with each file. Such enumerator classes are common in many C++ class libraries. In this case it seems more trouble than it's worth, since CFileSpec is already so simple.


0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 2642626
Actually, that is from Microsoft Systems Journal (Paul DiLascia) and is provided as part of MSDN.
0
 
LVL 22

Expert Comment

by:nietod
ID: 2642798
True.  I write the "from the VC help" before finding the article  (I knew it was there). and didn't go back to correct my note after I pasted it in.

Ronslow, out of curiosity--and don't take this the wrong way, I'm just asking--Why are you here again these days?
0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 2642808
My interest goes in waves .. and the offer of a PDA for being in the TOP 100 spiked my interest.  I had a look and I'd dropped from #7 down to around #44 (I think) .. Now I've workd my way up to #40 again .. but still a long climb ahead.

I tend to move between news-groups, mailing lists, gode guru, EE and just having a break.

Now I'm back .. be afraid .. be very afraid ;-)

PS: Only pointed out the source of your info in case someone not having MSDN tried to find this in the VC help and wondered what you were talking about.

0
 
LVL 1

Author Comment

by:Richard_Eustace
ID: 2648909
Cheers mate
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
MSVCR80.dll crash 2 126
Programming Arduino to control a Max7219 using C 2 97
why "." vs "->" 23 116
C++ assignment question 7 129
What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now