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:
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.
// 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;
}
}
/////////////////////////////////////////////////////////////////////////////