Solved

Long file names in DlgDirList

Posted on 2000-03-21
5
349 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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

912 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

21 Experts available now in Live!

Get 1:1 Help Now