![]() |
Desktop Development »
Shell and IE programming »
Shell Programming
Intermediate
Visual browsing of alternative data-streams in Windows ExplorerBy nashcontrolADSDetector information with code examples. |
C#, Windows, .NET1.0, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

Visual Alternative Data Streams Detector Bar is a user control, based on Dino Esposito's article A Programmer's Perspective on NTFS 2000 Part 1: Stream and Hard Link and Pavel Zolnikov's BandObjectLib which makes it an Explorer bar.
VADSD 1.0 is integrated into Windows Explorer thus allowing visual real time viewing of a non encrypted file's alternative data streams. The streams are shown with their icon and name in a ListView object. The bar is activated from the Explorer menu ("View" -> "Explorer Bar" -> "Visual ADS Detector").
While creating a file on NTFS partition, you actually create an unnamed stream, which is the main file. However, NTFS allows you to create multiple streams within this file, making them alternative streams. An alternative stream is not visible to the regular user since it usually contains data such as SummaryInformation, Permissions, Icon etc. But there is a way of creating these streams and manipulating them as if they were regular files on the disk.
Non encrypted files can be searched for alternative data streams, using the BackupRead() API function (when performing backup, it's important to backup the alternative streams too..) to enumerate all the streams inside a file, then each stream can be accessed via the CreateFile() API function and act as a regular file.
In this example, ADS is a class that represents a stream (name, size, fullpath and extention):
/// <summary>
/// read all streams in a certain location (files and directories)
/// </summary>
/// <PARAM name="filepath">location on the drive</PARAM>
/// <returns>array of ADS classes, representing each stream found
/// </returns>
public static ADS[] ReadStreams(string filepath)
{
ArrayList FoundADS = new ArrayList();
IntPtr stream = CreateFile(filepath,FileAccessAPI.GENERIC_READ,
FileShare.Read,0,FileMode.Open,
FileFlags.BackupSemantics,0);
if (stream.ToInt32() == -1)
return null;
try
{
WIN32_STREAM_ID sid = new WIN32_STREAM_ID();
int dwStreamHeaderSize = Marshal.SizeOf(sid);
int Context = 0;
bool Continue = true;
while (Continue)
{
int lRead = 0;
Continue = BackupRead(stream,ref sid,dwStreamHeaderSize,
ref lRead,false,false,ref Context);
if (Continue && lRead == dwStreamHeaderSize)
{
if (sid.dwStreamNameSize>0)
{
//Read the stream name
lRead = 0;
IntPtr pName =
Marshal.AllocHGlobal(sid.dwStreamNameSize);
try
{
BackupRead(stream, pName, sid.dwStreamNameSize,
ref lRead, false, false, ref Context);
char[] bName = new char[sid.dwStreamNameSize];
Marshal.Copy(pName,bName,0,sid.dwStreamNameSize);
//Name is of the format ":NAME:$DATA\0"
string sName = new string(bName);
int i = sName.IndexOf(STREAM_SEP, 1);
if (i>-1) sName = sName.Substring(1, i-1);
else
{
//This should never happen.
//Truncate the name at the first null char.
i = sName.IndexOf('\0');
if (i>-1) sName = sName.Substring(1, i-1);
}
//Add the stream to the collection
ADS newADS = new ADS();
newADS.StreamName = sName;
newADS.FullStreamName = filepath + ":" + sName;
newADS.Size = sid.Size.QuadPart();
FoundADS.Add(newADS);
}
finally
{
Marshal.FreeHGlobal(pName);
}
}
int l = 0; int h = 0;
//Skip the stream contents - keep reading
BackupSeek(stream, sid.Size.Low, sid.Size.High,
ref l, ref h, ref Context);
}
else break; //no more streams
}
//required - tell the file we are done with backup.
//BackupRead(stream,ref sid,dwStreamHeaderSize,ref lRead,
// true,false,ref Context);
}
catch
{
return null;
}
finally
{
CloseHandle(stream);
}
return (ADS[])FoundADS.ToArray(typeof(ADS));
}
After having a list of streams, the program searches for the appropriate icon for each extension and then presents it in the ListView object:
ADS[] NTFSStreams = NTFS.ReadStreams(CurrentDir + "\\" + fileSelected);
foreach (ADS NTFSADS in NTFSStreams)
{
//***********************
// Tree Item Object
// ------------------
// 1- StreamName
// 2- Full Stream Name
// 3- Stream Size
// 4- Stream Extention
//***********************
Uri URI = new Uri(NTFSADS.FullStreamName,true);
//Loading Streams
ListViewItem newItem =
new ListViewItem(new string[]{NTFSADS.StreamName,
URI.LocalPath,NTFSADS.Size.ToString(),NTFSADS.Extention},
IconHandler.GetIcon(NTFSADS.Extention));
StreamsView.Items.Add(newItem);
}
The IconHandler is based on Gil_Schmidt's IconHandler Obtaining (and managing) file and folder icons using SHGetFileInfo code:
[Editor Comment: Line breaks used to avoid scrolling.]
/// <summary>
/// Retrieves information on the extension of a file and shows its Icon
/// </summary>
/// <PARAM name="extension">File extension</PARAM>
/// <returns>Mathing Icon</returns>
private static System.Drawing.Icon GetIconFromShell(string Extention)
{
//add '.' if nessesry
if (Extention[0] != '.') Extention = '.' + Extention;
//struct for getting file shell info
SHFILEINFO shinfo = new SHFILEINFO();
//get Shell File Info
SHGetFileInfo(Extention, FILE_ATTRIBUTE_NORMAL, ref shinfo,
(uint)Marshal.SizeOf(shinfo),
SHGFI_ICON | SHGFI_USEFILEATTRIBUTES | SHGFI_LARGEICON);
System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.
Icon.FromHandle(shinfo.hIcon).Clone();
DestroyIcon( shinfo.hIcon ); // Cleanup
return icon;
}
As mentioned, the stream can be treated as a regular file, allowing the user to edit, delete and copy the stream to another location. To open a stream, the CreateFile() function is used to get a handle to the file:
IntPtr stream = CreateFile(SourceFile,FileAccessAPI.GENERIC_READ,
FileShare.Read,0,FileMode.Open,
FileFlags.BackupSemantics,0);
VADSD allows the user to copy streams to and from the Explorer window, by using the Windows Clipboard. The Clipboard is where the data is stored when we copy files, text and images. In .NET the Clipboard object has only two functions: SetDataObject and GetDataObject. When you click "Copy" in the Explorer, Windows fills the Clipboard with information about the location of the file you want to copy and whether you want to delete it after copy (Cut clicked). It uses two formats: FileDrop to store file locations and PreferredDropEffect to store other information.
File locations are stored as a string array and can be retrieved by getting the data on the Clipboard in the FileDrop format:
if (Clipboard.GetDataObject().GetDataPresent("FileDrop"))
{
IDataObject FileDataObject = Clipboard.GetDataObject();
string[] FileLocations =(string[])FileDataObject.GetData("FileDrop");
}
Determining whether the user has clicked Copy or Cut can be achieved by getting the data on the Clipboard in the PreferredDropEffect format:
[Editor Comment: Line breaks used to avoid scrolling.]
if (Clipboard.GetDataObject().GetDataPresent("Preferred DropEffect"))
{
//byte array to hold the data in the clipboard
byte[] ExplorerCutSelect = new byte[4];
((MemoryStream)FileDataObject.GetData("Preferred DropEffect")).
Read(ExplorerCutSelect,0,4);
//"check the first byte
switch (ExplorerCutSelect[0])
{
//delete files after paste- Cut selected
case 2:
//copy operation
//delete operation
break;
//only paste - Copy selected
case 5:
//copy operation
}
}
Finally, copying the stream:
public static bool CopyStream (string Source,string Destination)
{
try
{
FileStream srcStream;
FileStream dstStream;
//open stream
IntPtr Sstream = CreateFile(Source,FileAccessAPI.GENERIC_READ,
FileShare.Read,0,FileMode.Open,
FileFlags.BackupSemantics,0);
IntPtr Dstream = CreateFile(Destination,FileAccessAPI.GENERIC_WRITE,
FileShare.Write,0,FileMode.OpenOrCreate,
FileFlags.BackupSemantics,0);
//make a filestream
srcStream = new FileStream(Sstream,FileAccess.Read,true);
dstStream = new FileStream(Dstream,FileAccess.Write,true);
byte[] srcData = new byte[srcStream.Length];
srcStream.Read(srcData,0,srcData.Length);
//copy
dstStream.Write(srcData,0,srcData.Length);
dstStream.Close();
srcStream.Close();
CloseHandle(Sstream);
CloseHandle(Dstream);
return true;
}
catch (System.IO.IOException ex)
{
MessageBox.Show(ex.Message);
return false;
}
}
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+PgUp/PgDown to switch pages.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 5 Dec 2005 Editor: Rinish Biju |
Copyright 2003 by nashcontrol Everything else Copyright © CodeProject, 1999-2010 Web19 | Advertise on the Code Project |