( Index )
Month

Brief Information about the February '13 CSIG Meeting

FStatProto - Top40 File Extensions on your C: Drive based on Size

by Bruce Arnold
Thumbnails

Welcome to the CSIG, a Special Interest Group of the ACGNJ. This is an exciting time for the C Language programming since Microsoft now has 4 different language compilers: C++, C++ Express, C-Sharp, and C-Sharp Express. These are all capable of creating Windows (tm) programs. (Some are FREE!)

Here's a brief synopsis of this month's meeting:

This month begins a multi-part presentation of creating an application with a particular purpose. The first step is the algorithm.  Next, this must be coded to verify the plan.  The code presented this month creates a Console type program as show above. There's no point in struggling with the Windows functions until the algorithm viability and performance has been verified. The App uses Microsoft's C++/CLI features and simply displays to a "DOS" box. Once we have confidence in the approach, the program can then be converted into a Windows program using the normal user interface and dotnet functions. (Note: this will be a dotnet re-write of  FSTAT presented in Csig2005.)

Object: to scan the entire C: drive and tabulate the statistics of all of the files. This includes size and count totals, and percent relative to file extension. Directory functions will be used for the scanning and temperred with exception (error) handling code for special circumstances. There will be activity on the screen during scanning so that the user is kept informed. Since the output is within a Console Box or Dos Box, it may be captured and transferred to another program such as Excel.

The program uses a number of DOT NET Library functions including the following:

array
backgroundcolor
binarysearch
catch
class
cli
collections
compare
compareto
console
consolecolor
datetime
directories
directory
dotnet
exception
extension
filearray
fileinfo
filename
foregroundcolor
gcnew
generic
getdirectories
getenumerator
getfiles
icomparable
icomparer
ienumerator
movenext
recursive
resetcolor
setwindowsize
string
subdirectory
subdirs
substring
topdirectoryonly
write
writeline


There are a number of ways to refer to Microsoft's latest compilers and code. Here's what Wikipedia says:
The Common Language Infrastructure (CLI) is an open specification developed by Microsoft that describes the executable code and runtime environment that form the core of the Microsoft .NET Framework. The specification defines an environment that allows multiple high-level languages to be used on different computer platforms without being rewritten for specific architectures.

Microsoft .Net Framework 3.5, etc.
C++ 9.0, etc.
.Net 3.5, etc.
CLI
Common Language Infrastructure
Managed


Here's what Nishant Sivankumar (C++/CLI in Action) says:
The role of C++/CLI C++ is a versatile programming language with a substantial number of features that makes it the most powerful and flexible coding tool for a professional developer. The Common Language Infrastructure (CLI) is an architecture that supports a dynamic language-independent programming model based on a Virtual Execution System. The most popular implementation of the CLI is Microsoft’s .NET Framework for the Windows operating system. C++/CLI is a binding between the standard C++ programming language and the CLI.


Sample Code

// FStatProto.cpp : main project file.
//
//
// Version 2.0    1/30/2013   B.Arnold
//
// Object: To use the latest dotnet (c++/CLI) programming to 
// scan the entire C: drive and tabulate file extension data.
// EXT name, SIZE of files, COUNT of files, PERCENT occupied.

#include "stdafx.h"

// FStat.cpp : main project file.

using namespace System;
using namespace System::IO;
using namespace System::Collections::Generic;
// Caution:
// Program uses methods from both "Collections" and "Collections::Generic".
// ie. List is Generic, IEnumerator is not Generic. (BA)


/////////////////////////////////////////////////////////////////////////////
// Implement IComparable CompareTo method - provide default sort order.

public ref class FSTATdata: public IComparable<FSTATdata^>
   {
   public:
      String ^Ext;
      unsigned long long KBytes;    //(2013)
      float Percent;
      int Count;

      virtual int CompareTo( FSTATdata ^other )
         {
         return String::Compare(this->Ext, other->Ext);
         }
   };

/////////////////////////////////////////////////////////////////////////////
// Implement IComparer ("er" vs "able") to create a particular sort order.

public ref class mSortByCount : public IComparer<FSTATdata ^>
   {
   public:
      virtual int Compare(FSTATdata ^aaa, FSTATdata ^bbb)
         {
         int count1, count2;
         count1 = (int) aaa->Count;
         count2 = (int) bbb->Count;
         if      (count1 < count2) return 1;
         else if (count2 < count1) return -1;
         else return 0;
         }
   };

/////////////////////////////////////////////////////////////////////////////

// Implement IComparer ("er" vs "able") to create a particular sort order.

public ref class mSortBySize : public IComparer<FSTATdata ^>
   {
   public:
      virtual int Compare(FSTATdata ^aaa, FSTATdata ^bbb)
         {
         unsigned long long size1, size2;
         size1 = (int) aaa->KBytes;
         size2 = (int) bbb->KBytes;
         if      (size1 < size2) return 1;
         else if (size2 < size1) return -1;
         else return 0;
         }
   };

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////


void Process(String ^ root, List<FSTATdata^>^ mList, int &Loop);
void CatogorizeFile(String ^ filename, List<FSTATdata^>^ mList);

/////////////////////////////////////////////////////////////////////////////

int main(array<System::String ^> ^args)
{
    args; // avoid warning.
   String ^path = gcnew String("C:\\");

   List<FSTATdata^>^ Stats = gcnew List<FSTATdata ^>();     // Create an empty list.

   unsigned long long TotalKBytes = 0L;
   int TotalFiles = 0;
   int Loop = 0;
   int wd = Console::LargestWindowWidth;
   int ht = Console::LargestWindowHeight;
   Console::BackgroundColor = ConsoleColor::White;
   Console::ForegroundColor = ConsoleColor::Black;
   if (wd>0) // Zero when using Visual Studio IDE in Release solution.
      {
      Console::SetWindowSize(wd>25 ? 25:wd, ht>6 ? 6:ht);
      Console::Clear();
      }
   Console::WriteLine(" Scanning Folders ...");

   Process(path, Stats, Loop);      // Make a List of all file extensions
                                    // ----------------------------------
   if (wd>0)
      {
      Console::SetWindowSize(wd>80 ? 80:wd, ht>60 ? 60:ht);
      Console::Clear();
      }

   for each(FSTATdata ^record in Stats)        // Calculate totals
      {
      TotalKBytes += record->KBytes;
      TotalFiles  += record->Count;
      }

   for each(FSTATdata ^record in Stats)        // Update "Percent" data
      {
      record->Percent = (float)(record->KBytes * 100) / (float)TotalKBytes;
      }

   Console::WriteLine();
   Console::WriteLine("                     Top 40 File Extensions");
   Console::WriteLine("                     ======================");
   Console::WriteLine("                      "+System::DateTime::Now);
   Console::WriteLine(String::Format(
      "\n\n            {0:0,0} files ({1:0,0} KBytes) {2}", TotalFiles, 
      TotalKBytes,
      path));
   Console::WriteLine();

   IComparer <FSTATdata ^> ^mSBS = gcnew mSortBySize();
   Stats->Sort(mSBS);   // Sort by Size

   //             1234567890123456789012345678901234567890123456789012345678901234567890
   Console::WriteLine("           EXTENSION         KBYTES    PERCENT      COUNT\n");

   int i=0;
   String ^mExt;
   for each(FSTATdata ^record in Stats)         // display a few
      {
      if (++i > 40) break;
      mExt = record->Ext + "                    ";// pad 20.
      mExt = mExt->Substring(0,20);          // truncate.
      mExt = mExt->TrimEnd(nullptr);            // trim spaces.
      Console::WriteLine(String::Format(
         "{0,20} {1,14:0,0} {2,10:F2} {3,10}",
         mExt,
         record->KBytes,
         record->Percent,
         record->Count));
      }

   Console::WriteLine("\n\n          Written by B.Arnold, 1/31/2013\n\n");
   Console::ResetColor();
   Console::ReadLine(); // Wait for Enter key.
   return 0;
   } 

/////////////////////////////////////////////////////////////////////////////

void Process(String ^ root, List<FSTATdata^>^ mList, int &Loop) // Recursive file search
    {
    String ^path;
    FSTATdata record;
    
    path = root;
   if (!path->EndsWith("\\")) path += "\\";

   // Scan the files in this directory.
   // --------------------------------
   array<String^> ^fileArray;
   try
      {
      fileArray = Directory::GetFiles(path, "*", SearchOption::TopDirectoryOnly);
      System::Collections::IEnumerator^ iFile = fileArray->GetEnumerator();
      while ( iFile->MoveNext() )
         {
         String^ fileName = safe_cast<String^>(iFile->Current);
         CatogorizeFile( fileName, mList);
         }
      }
   catch ( System::UnauthorizedAccessException ^uae) 
       {
      uae;
        ; // SKIP    Console::WriteLine( "LINE: 87, The process failed: {0}", uae );
        }
   catch ( Exception^ e ) 
       {
        Console::WriteLine( "The FILES process failed: {0}", e );
        }

   // Scan the directories in this directory.
   // --------------------------------------
   array<String^> ^subdirs;

   try
      {
      subdirs = Directory::GetDirectories(path, "*", SearchOption::TopDirectoryOnly);
       System::Collections::IEnumerator^ dirs = subdirs->GetEnumerator();
      while ( dirs->MoveNext() )
         {
         String^ subdirectory = safe_cast<String^>(dirs->Current);
         Console::Write(String::Format("\r {0,-8}",++Loop));         // Show loop counter
         Process( subdirectory , mList, Loop);  // RECURSIVE !!!
         }
      }
   catch ( System::UnauthorizedAccessException ^uae) 
       {
      uae;
        ; // SKIP    Console::WriteLine( "LINE: 108, The process failed: {0}", uae );
        }
   catch ( Exception^ e ) 
      {
      Console::WriteLine( "The DIRECTORIES process failed: {0}", e );
      }
    }

/////////////////////////////////////////////////////////////////////////////

void CatogorizeFile(String ^ filename, List<FSTATdata^>^ mList)
   {
   // If Extension exists in mList, bump count & kbytes
   // Else
   //    Add entry with a count of 1.
   //    Re-sort mList.

   FileInfo ^ stats;
   try
      {
      stats = gcnew FileInfo(filename);
      }
   catch(...)  
      {
      return;     // Skip on any error.
      }
   FSTATdata ^record = gcnew FSTATdata();
   record->Count=1;
   record->Ext = stats->Extension->ToUpper();
   record->KBytes = stats->Length >> 10;
   if (record->KBytes % 1024) ++record->KBytes; // round up.
   int idx = mList->BinarySearch(record);
   if (idx < 0)
      {
      mList->Add(record);
      mList->Sort();
      }
   else
      {
      mList[idx]->Count++ ;
      mList[idx]->KBytes += record->KBytes;
      }
   }
/////////////////////////////////////////////////////////////////////////////



SOURCE CODE

Source Code Files

For help, email me at b a r n o l d @ i e e e . o r g
Back to C++ Main Page