Click here to Skip to main content
Click here to Skip to main content

File Searcher in C#

By , 29 Sep 2012
 

Introduction

On a Windows Vista PC, I tried to search my hard disk for a file that contains a certain string. This was not possible using Windows Explorer. So, I decided to write my own file searcher. And, here it is...

What It Does

You must enter a search directory, so the program knows where to search for files and directories. If you check the "Include subdirectories" checkbox, the program will recursively search in all subdirectories of the search directory. The filename specified can be something like "*.wav;*.mp3;Christma??ree.*". The program will list all files and directories matching these filenames.

You can also use some restrictions to restrict the found items. Each restriction must be activated by checking the appropriate checkbox, and the necessary parameter for this restriction can be entered to the right of the checkbox.

  • "Files newer than" will only list the items that have a LastWriteTime greater than the parameter.
  • "Files older than" will only list the items that have a LastWriteTime less than the parameter.
  • "Files containing the string" will only list the items that contain the string parameter you entered.
  • The program will convert this string parameter into a byte sequence, using either ASCII or Unicode encoding (depending on what you select), and then search each file for an occurrence of this byte sequence.

Click the "Start" button to start the search. The found items will be listed below. If the search takes too much time and you want to cancel it, you can click the "Stop" button to stop the search.

If you double-click an item that represents a file, the program will open this file within the application that is associated with the file's extension.

If you right-click an item and select "Open Containing Folder", the program will open the folder containing the file or directory in Windows Explorer.

If you want to write the search results to a text file, enter a delimeter that shall be used to separate items in the text file, and then click the button "Write results to text file...".

Using the Code

This application consists of two main parts:

  1. The MainWindow class does all the user interface stuff.
  2. The Searcher class provides the business logic for searching FileSystemInfo objects.

When the user clicks the "Start" button, the method Searcher.Start is executed. It starts a new thread called SearchThread. This thread searches for files and directories, matching the parameters that were entered by the user. If it finds a matching FileSystemInfo object, it raises an asynchronous FoundInfo event, so that the MainWindow can extract the FileSystemInfo object from the FoundInfoEventArgs, and update its results list. When the thread ends, it sets the m_thread member to null. The Searcher.Start method checks if m_thread is null every time it is executed, so there can never be more than one thread running at the same time.

When the user clicks the "Stop" button, the method Searcher.Stop is executed. It sets the m_stop member to true, so that the SearchThread can recognize this change and stop itself as soon as possible. Notice that this operation is thread-safe, because a boolean variable needs only one operation step to be set.

Important: In the Searcher_FoundInfo event handler, the MainWindow uses its Invoke method to invoke the this_FoundInfo method through a delegate. This way, the MainWindow makes sure that the code for updating the results list is executed in the MainWindow's thread, and not in the Searcher's thread. Calling the this_FoundInfo method directly would cause the application to crash, because the Searcher_FoundInfo event handler is not synchronized to the GUI controls.

private delegate void FoundInfoSyncHandler(FoundInfoEventArgs e);
private FoundInfoSyncHandler FoundInfo;

...

private void MainWindow_Load(object sender, EventArgs e)
{
    ...
    this.FoundInfo += new FoundInfoSyncHandler(this_FoundInfo);
    ...
}

...

private void Searcher_FoundInfo(FoundInfoEventArgs e)
{
    if (!m_closing)
    {
        this.Invoke(FoundInfo, new object[] { e });
    }
}

private void this_FoundInfo(FoundInfoEventArgs e)
{
    CreateResultsListItem(e.Info);
}

The CreateResultsListItem method creates and adds a new ListViewItem to the results list, which shows the data contained in the FilesystemInfo object. A FileSystemInfo object can be either be a FileInfo or a DirectoryInfo, depending on what the Searcher has found. The is operator can be used to decide what kind of object it is. If it's a FileInfo object, the list shall display the file's size in KB:

ListViewItem.ListViewSubItem lvsi = new ListViewItem.ListViewSubItem();
if (info is FileInfo)
{
    lvsi.Text = GetBytesStringKB(((FileInfo)info).Length);
}
else
{
    lvsi.Text = "";
}

When the Searcher's thread ends, it raises a ThreadEnded event, so the MainWindow recognizes when the search has ended. The Searcher_ThreadEnded event handler uses the Invoke method the same way as the Searcher_FoundInfo event handler:

private delegate void ThreadEndedSyncHandler(ThreadEndedEventArgs e);
private ThreadEndedSyncHandler ThreadEnded;

...

private void MainWindow_Load(object sender, EventArgs e)
{
    ...
    this.ThreadEnded += new ThreadEndedSyncHandler(this_ThreadEnded);
    ...
}

...

private void Searcher_ThreadEnded(ThreadEndedEventArgs e)
{
    if (!m_closing)
    {
        this.Invoke(ThreadEnded, new object[] { e });
    }
}

private void this_ThreadEnded(ThreadEndedEventArgs e)
{
    EnableButtons();
    if (!e.Success)
    {
        MessageBox.Show(e.ErrorMsg,
                        "Error",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Exclamation);
    }
}

Points of Interest

You can use this code as a simple example for a multithreading application.

History

  • April 6, 2009
    • Published at CodeProject
  • April 8, 2009
    • Change: Uses the Form.Invoke method instead of a System.Timers.Timer
    • New feature: "Open Containing Folder"
  • April 24, 2009
    • Bug-fix: Solved a dead lock bug that sometimes occured when the user clicks the "Stop" button.
    • New feature: Supports multiple file names, separated by ";". Example: "*.wav;*.mp3".
  • September 29, 2012
    • Upgraded solution to Visual Studio 2012. 
    • New feature: Write the search results to a text file.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Manfred Bittersam
Software Developer
Austria Austria
Member
Manfred Bittersam has been developing industrial software
in C++ (MFC), C# and VB.NET for several years.
He has gone through a lot of changes, in career as well as in private life.
He says he has learned a lot from his mistakes Wink | ;)
Enjoy his free articles!

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 4memberJasmine25012 May '13 - 12:53 
Groovy! I made a web spider about 8 years ago with the same basic idea.
 
http://www.smoothjazzy.com/jazzy_sitemap.html
GeneralMy vote of 5memberAbinash Bishoyi8 Apr '13 - 7:10 
Nice
Questioninsert more functions on ColumnHeader for ListView [modified]memberMember 13044745 Mar '13 - 18:36 
Hi!Manfred,
Congratulations for your work. Sorry for the question that I will make,because I Am a new user in the area of programming using C #. I would like to know how can I put two more information in ColumnHeader (ListView) like this: owner and Start Creat. Because when I press button "Write results to text file..." the result should appear like this example bellow:
 
Path:  	                            Size:     Last modified:  	    start Creat:      Owner:
C:\Temp\500_240132R29_NX6.pch  	    35 KB   06/08/2012 17:03    02/08/2012 16:03     PCH3742
C:\Temp\500_240132R29_NX6_HL1.pch   40 KB   09/08/2012 10:20    02/08/2012 15:03     RCH1234
C:\Temp\500_820110N502_NX6.pch      11 KB   21/07/2012 19:44    02/08/2012 14:03     ABC3456
C:\Temp\500_820110N510_NX6.pch      19 KB   21/07/2012 19:19    02/08/2012 13:03     RBS0012
C:\Temp\500_820110N511_NX6.pch       8 KB   21/07/2012 19:38    02/08/2012 12:03     LRS7885 	
 
I want to keep this formatting of text above because I need to know who is the owner's file and when it was created. Thanks and best regards.
 
Sena_10

modified 6 Mar '13 - 0:45.

Questioncan we display matching lines from files in another grid on selection of file ?memberl6it89613 Jan '13 - 6:58 
Is it possible to add one more grid below results to display file contents highlighting match results line in the file ?
AnswerRe: can we display matching lines from files in another grid on selection of file ?memberManfred Bittersam14 Jan '13 - 7:05 
I don't think it's worth the effort, for 3 reasons:
1) You can open a file by double-clicking it in the results list. If a good text editor (e.g. Notepad++) is associated with the file's extension, you can search for a string and also highlight all occurrences of the string.
2) To optimize performance, this program only searches for the FIRST occurrence of the string in each file. Any further occurrences in the same file are irrelevant, because the program already knows that the file matches the search criteria.
Of course, the program could do a second search when a file is selected by the user, and find ALL occurrences of the string, and display and highlight them. But what if the file has a size of 15GB? The program would eat up your RAM and then crash, unless you implement a very good text editor that can handle such file sizes (far too much effort!).
3) This program does not distinguish between text files and binary files. Therefore it would try to display the contents of a binary file as if it was a text file, and I'm sure you don't want to see that.
Of course, you could give the program a set of file extensions that are to be interpreted as text files, and then display only files that have one of these extensions, but that would make it far too complicated. I want to keep this program as simple as possible.
GeneralI vote 5memberZimnnsRichie1 Oct '12 - 17:45 
Well explained. A good start for me. Thanks Smile | :)
GeneralMy vote of 5memberSergio Andrés Gutiérrez Rojas29 Sep '12 - 13:17 
Good work!!
SuggestionOutput File Results [modified]memberkittell24 Sep '12 - 4:59 
I love this solution that you have created, one suggestion though is to output the results to a tab-delimited text file.
 
Below is working code that I added to your project, of course this could be improved upon but works for my purposes at the moment.
 
EDIT: This should be added in MainWindow.cs just under
private void this_ThreadEnded(ThreadEndedEventArgs e)
		{
			// Enable all buttons except stop button:
			EnableButtons();
 
// Write to tab-delimited text file - Start
try
	{
		saveFileDialog1.Filter = "Text File|*.txt";
		saveFileDialog1.Title = "Save File Search";
 
		if (this.saveFileDialog1.ShowDialog() == DialogResult.OK)
			{
				FileStream fs = new FileStream(@saveFileDialog1.FileName, FileMode.OpenOrCreate, FileAccess.Write);
				StreamWriter m_streamWriter = new StreamWriter(fs);
				m_streamWriter.Flush();
				m_streamWriter.BaseStream.Seek(0, SeekOrigin.Begin);
 
				string sColumn1, sColumn2, sColumn3 = "";
				m_streamWriter.WriteLine("File Path\tFile Size\tLast Modified");
 
				for (int i = 0; i < this.resultsList.Items.Count; i++)
					{
						sColumn1 = resultsList.Items[i].SubItems[0].ToString();
						sColumn1 = sColumn1.Replace("ListViewSubItem: {", "");
						sColumn1 = sColumn1.TrimEnd('}');
 
						sColumn2 = resultsList.Items[i].SubItems[1].ToString();
						sColumn2 = sColumn2.Replace("ListViewSubItem: {", "");
						sColumn2 = sColumn2.TrimEnd('}');
 
						sColumn3 = resultsList.Items[i].SubItems[2].ToString();
						sColumn3 = sColumn3.Replace("ListViewSubItem: {", "");
						sColumn3 = sColumn3.TrimEnd('}');
 
						m_streamWriter.WriteLine(sColumn1 + "\t" + sColumn2 + "\t" + sColumn3);
					}
 
				m_streamWriter.Flush();
				m_streamWriter.Close();
			}
	}
catch (Exception em)
	{
	}
// Write to tab-delimited text file - Stop
David Kittell


modified 24 Sep '12 - 11:41.

GeneralRe: Output File ResultsmemberManfred Bittersam24 Sep '12 - 6:50 
Good idea. Thanks for your post. I will put something like this into the next version of my file searcher. There will be a button "Write results to text file" and a textbox "Delimeter". I think most people will select ";" as delimeter, but TAB will also be available.
I will not put this file writing procedure into the "ThreadEnded" event handler, because most people would be annoyed if there would pop up a dialog window every time they search for files. However, it will be available via the button mentioned above.
GeneralRe: Output File Resultsmemberkittell24 Oct '12 - 8:11 
Thank you for the rather quick response and edit.
 
I'm not sure if anyone else would have a need for it but I also added in a CRC listing for the files as well.
 
using System.Security.Cryptography;
 
if (CRCCheckBox.Checked == true)
{
resultsList.Columns.Add("CRC:", 90, HorizontalAlignment.Left);
lvsi = new ListViewItem.ListViewSubItem();
lvsi.Text = GetCRC(sFileName);
lvi.SubItems.Add(lvsi);
}
 
public static String GetCRC(string filename)
{
FileStream file = new FileStream(filename, FileMode.Open);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(file);
file.Close();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < retVal.Length; i++)
{
sb.Append(retVal[i].ToString("x2"));
}
return sb.ToString();
}
David Kittell
Kittell.net

GeneralMy vote of 5memberJαved28 May '12 - 20:32 
Awesome work Manfred. 5 from me if possible i would give have given 100 Smile | :) .
I tested it with Windows search and you wont believe it finished searching before 5 minutes than Windows search.
cheers. Smile | :)
QuestionHow Can we use this application for searching file in a particular server from a website hosted in intranet.memberRagigopi2 Dec '11 - 19:35 
hi
this is an excellent work....
 
my doubt is
 
how can we search a file in a particular server where the website is hosted.
the main aim of the website is to search the files in the server from different nodes
 
thanks in advance
AnswerRe: How Can we use this application for searching file in a particular server from a website hosted in intranet.memberManfred Bittersam2 Dec '11 - 22:26 
Execute an ASP.NET script on your website. Within this script, you can use the same search algorithms as I do in my C# application.
QuestionHello; question about vbnet..memberfuujinn28 Aug '11 - 21:20 
Hello when i tried to convert to vbnet ; i am getting error at these lines;
 
in mainwindow;
this.FoundInfo += new FoundInfoSyncHandler(this_FoundInfo);
this.ThreadEnded += new ThreadEndedSyncHandler(this_ThreadEnded);
 
in vbnet should be like that;

 
AddHandler FoundInfo, AddressOf this_FoundInfo
AddHandler ThreadEnded, AddressOf this_ThreadEnded
 
error messages;
 
FoundInfo' is not an event of 'FileSearcher.MainWindow'.
ThreadEnded' is not an event of 'FileSearcher.MainWindow'.
 
can u pls help me out to fix this problem thx...
AnswerRe: Hello; question about vbnet..memberManfred Bittersam29 Aug '11 - 6:51 
Look at this article:
Step by Step: Event handling in VB.NET[^]
It will help you.
QuestionHow to highlight the file with "Open containing folder"memberclefranc25 Jan '10 - 2:34 
Can you highlight the file when you use the "Open containing folder" command?
 
Christian
 
Christian Lefrançois
Diffusion Fermont

AnswerRe: How to highlight the file with "Open containing folder"memberManfred Bittersam25 Jan '10 - 8:06 
This would be a nice feature, but I don't know any way to programmatically highlight a special file in Windows Explorer. As far as I know, you can call the "explorer.exe" two ways:
a) You give it a folder path as command line argument (e.g. explorer.exe "C:\Windows"):
-> An instance of Windows Explorer will open, and show the contents of this folder.
(That is what my "FileSearcher.exe" does.)
b) You give it a file path as command line argument (e.g. explorer.exe "E:\test.jpg"):
-> The program associated with the file's extension will open, and show the file.
(Unfortunately, this is no way to open Windows Explorer and highlight the file.)
GeneralGreat App! - Can you add multiple searchmemberBlazeDoggie12 May '09 - 6:16 
Loved the article, very useful. One more thing that would make it even more useful is to be able to search for multiple strings separated by commas and perhaps a qualifier if needed, for example "string1", "string2" or just simply string1, string2.
GeneralRe: Great App! - Can you add multiple searchmemberManfred Bittersam12 May '09 - 8:30 
I don't want to add a search for multiple strings that are separated by a special character, because I think that most people want to search for just 1 string, containing ANY characters they want, even a ; or , or ".
Anyway, you can achieve what you want by replacing a few lines in Searcher.cs:
 
1) Replace line 27:
private static Byte[] m_containingBytes = null;
with:
private static List<Byte[]> m_containingBytes = null;
2) Replace line 89:
m_containingBytes = m_pars.Encoding.GetBytes(m_pars.ContainingText);
with:
String[] parts = m_pars.ContainingText.Split(new Char[] { ';' });
m_containingBytes = new List<byte[]>();
foreach (String part in parts)
{
    String trimmedPart = part.Trim();
    if (trimmedPart != "")
    {
        Byte[] bytes = m_pars.Encoding.GetBytes(trimmedPart);
        m_containingBytes.Add(bytes);
    }
}
3) Replace line 226:
matches = FileContainsBytes(info.FullName, m_containingBytes);
with:
foreach (Byte[] bytes in m_containingBytes)
{
    matches = FileContainsBytes(info.FullName, bytes);
    if (matches)
    {
        break;
    }
}
- After you've done this, you can enter something like "christmas;tree;candle;santa claus",
and the program will search for files that contain
"christmas" or "tree" or "candle" or "santa claus".
GeneralGood articlememberDonsw11 May '09 - 5:23 
Very good work. I like how you used and explained threading. This is a great case for threading, although if you wanted a program to search for files or text in files there are many that do that. One that coems to mind is Agnent Ransack. Very good articel none the less.
 
cheers,
Donsw
My Recent Article : Unit Testing options in Visual Studio 2008

GeneralBackgroundWorkermemberdybs3 May '09 - 5:59 
Hello,
 
Good article, but why not use the System.ComponentModel.BackgroundWorker class instead of System.Threading.Thread? The ProgressChanged event executes on the GUI thread, eliminating the need for the call to Invoke. Still, not bad for an into to multi-threading.
 
Dybs
GeneralRe: BackgroundWorkermemberManfred Bittersam3 May '09 - 6:34 
I must admit that I have never used the System.ComponentModel.BackgroundWorker class before, but I will try it. Thanks for the hint.
GeneralMultiple file typesmemberrctaubert27 Apr '09 - 3:12 
Thank you for adding the ability to search for multiple file types.
GeneralMultiple file typesmemberrctaubert14 Apr '09 - 10:36 
Is it possible to search for multiple file extensions (eg. *.mp3 *.wma)???
AnswerRe: Multiple file typesmemberComputafreak14 Apr '09 - 11:04 
Separate them with semi-colons (eg. *.mp3;*.wma)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 29 Sep 2012
Article Copyright 2009 by Manfred Bittersam
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid