/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE":
 * I wrote this file. As long as you retain this notice you can do
 * whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return - Sebastian Roll
 * ----------------------------------------------------------------------------
 */
 
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using System.Collections;
 
namespace FileSystemWatch
{
   /// <summary>
   /// Main window with a dataGridView, contextMenu and notifyIcon (systemTray).
   /// dataGridView shows file activities for desired directories.
   /// .Net Framework class System.IO.FileSystemWatcher is used to keep track of each directory.
   /// (one FSW for one directory)
   /// File-Events change, create, delete and rename are tracked.
   /// But file is only shown to user if it passes the file extension filter.
   /// (e.g. if it is not a *.txt file -> do not show)
   /// (e.g. if file is within "directories to exclude" -> do not show)
   /// On fileActivity, a balloon notification may appear in systemTray with infoText set to fileName.
   /// All activites can be logged to file.
   /// A file, shown in dataGridView, can be opened via contextMenu (with associatet application or viewed in explorer).
   /// </summary>
   public partial class MainWindow : Form
   {
      // two arrays with strings. a string is e.g. "C:\myDirectory"
      PathContainer pathsToWatch = new PathContainer();
      PathContainer pathsToExclude = new PathContainer();
      // array with directory watchers (System.IO.FileSystemWatcher)
      // each FSW watches exactly one directory, as contained in "pathsToWatch"
      ArrayList watchers = new ArrayList();
      // option window with filter config and other usefull app-options
      OptionsWindow options = new OptionsWindow();
      // counting incomming FSW events
      UInt32 totalIncommingNotificationEvents = 0;
 
      // ctor
      public MainWindow()
      {
         InitializeComponent();
      }
 
      // on show window: load options from file, 
      // load directories to watch
      // load directorie to exclude from watch
      // setup FileSystemWatcher objects on each directory to watch
      private void MainWindow_Load(object sender, EventArgs e)
      {
         trace("MainWindow_Load()");
         options.load("FSWConfig.txt");
         pathsToWatch.load("FSWPathsInclude.txt");
         pathsToExclude.load("FSWPathsExclude.txt");
         rebuildFileSystemWatchersList(pathsToWatch);
         updateStatusBarText();
 
         // set main window position to last used position,
         // but first check if last position is present in configFile
         if (!options.MainWindowLocation.IsEmpty)
         {
            Location = options.MainWindowLocation;
         }
         if (!options.MainWindowSize.IsEmpty)
         {
            Size = options.MainWindowSize;
         }
      }
 
      // FSW object sent an event: some file changed in certain directory
      private void fileSystemWatcher1_Changed(object sender, System.IO.FileSystemEventArgs e)
      {
         // do nothing if file is our own logfile
         if (isNotificationOnOwnLogFile(e.FullPath))
         {
            return;
         }
 
         // count and log event
         totalIncommingNotificationEvents++;
         trace("Changed()", e.FullPath);
 
         // user wants to be notified on file change?
         if (options.NotifyOnFileChange)
         {
            // yes, add to dataGridView
            addRow(e.FullPath, "Changed");
         }
         else
         {
            // no, just log to file
            trace("Changed() " + e.FullPath, " filtered by options.NotifyOnFileChange");
         }
 
         // show some text in window's status bar
         updateStatusBarText();
      }
 
      // FSW object sent an event: some file was created in certain directory
      private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
      {
         // do nothing if file is our own logfile
         if (isNotificationOnOwnLogFile(e.FullPath))
         {
            return;
         }
 
         // count and log event
         totalIncommingNotificationEvents++;
         trace("Created()", e.FullPath);
 
         // user wants to be notified on file create?
         if (options.NotifyOnFileCreate)
         {
            // yes, add to dataGridView
            addRow(e.FullPath, "Created");
         }
         else
         {
            // no, just log to file
            trace("Created() " + e.FullPath, " filtered by options.NotifyOnFileCreate");
         }
 
         // show some text in window's status bar
         updateStatusBarText();
      }
 
      // FSW object sent an event: some file was deleted in certain directory
      private void fileSystemWatcher_Deleted(object sender, FileSystemEventArgs e)
      {
         // do nothing if file is our own logfile
         if (isNotificationOnOwnLogFile(e.FullPath))
         {
            return;
         }
 
         // count and log event
         totalIncommingNotificationEvents++;
         trace("Deleted()", e.FullPath);
 
         // user wants to be notified on file delete?
         if (options.NotifyOnFileDelete)
         {
            // yes, add to dataGridView
            addRow(e.FullPath, "Deleted");
         }
         else
         {
            // no, just log to file
            trace("Deleted() " + e.FullPath, " filtered by options.NotifyOnFileDelete");
         }
 
         // show some text in window's status bar
         updateStatusBarText();
      }
 
      // FSW object sent an event: some file was renamed in certain directory
      private void fileSystemWatcher_Renamed(object sender, RenamedEventArgs e)
      {
         // do nothing if file is our own logfile
         if (isNotificationOnOwnLogFile(e.FullPath))
         {
            return;
         }
 
         // count and log event
         totalIncommingNotificationEvents++;
         trace("Renamed()", e.FullPath);
 
         // user wants to be notified on file rename?
         if (options.NotifyOnFileRename)
         {
            // yes
            // check if user does not want to see any MS Office AutoSafe fileOperations
            if (options.MsOfficeDiscardAutoSave)
            {
               String fileExt = Path.GetExtension(e.OldFullPath);
               if (fileExt == "")
               {
                  // it is MS Office AutoSafe: just log to file and return
                  trace("Renamed() " + e.FullPath, " filtered by options.MsOfficeDiscardAutoSave");
                  return;
               }
            }
 
            // add to dataGridView
            addRow(e.FullPath, "Renamed from " + e.OldFullPath);
         }
         else
         {
            // no, just log to file
            trace("Renamed() " + e.FullPath, " filtered by options.NotifyOnFileRename");
         }
 
         // show some text in window's status bar
         updateStatusBarText();
      }
 
      // FSW object sent an event: some file opration throws error
      // :) never occurred until now
      private void fileSystemWatcher_Error(object sender, ErrorEventArgs e)
      {
         trace("fileSystemWatcher_Error() " + e.GetException().Message);
         MessageBox.Show(e.GetException().Message, "FSW Error");
      }
 
      // appends a line to dataGridView with given Strings
      // checks if line can be added due to filter-settings in option-window
      private void addRow(String fileName, String reason)
      {
         // user want to filter events by file-extension?
         if (options.UseFileFilter)
         {
            // yes, check if this file-extension is within filter-list
            if (!fileExtensionFilterPassed(fileName))
            {
               // cannot find this extension in extension-list from options window
               // do not add line to dataGridView, just log to file
               trace("addRow() " + fileName, " filtered by fileExtension");
               return;
            }
         }
 
         // user has set exclude-directory for given file?
         String dir = Path.GetDirectoryName(fileName);
         if (pathsToExclude.containsIgnoreCase(dir))
         {
            // yes, do not add line to dataGridView, just log to file 
            trace("addRow() " + fileName, " filtered by pathsToExclude");
            return;
         }
 
         // user does not want to see directory-rename actions?
         if (!options.NotifyOnFolderChange)
         {
            // yes, check if current event points to a directory
            // (current event is e.g. C:/myDir (has no extension, e.g. *.txt))
            if (Directory.Exists(fileName))
            {
               // yes, do not add line to dataGridView, just log to file 
               trace("addRow() " + fileName, " filtered by options.NotifyOnFolderChange");
               return;
            }
         }
 
         // user does not want to see MS-Office TempFile actions?
         // e.g. MS Word always creates a copy of a file to open (e.g. ~$WRD876EF.tmp)
         if (options.MsOfficeDiscardTempFiles)
         {
            // yes, check for temp-file
            String fileWoExt = Path.GetFileNameWithoutExtension(fileName);
            if (fileWoExt.StartsWith("~"))
            {
               // it is a temp-file, do not add line to dataGridView, just log to file 
               trace("addRow() " + fileName, " filtered by options.MsOfficeDiscardTempFiles");
               return;
            }
         }
 
         // user wants to see a balloon tip on systemTray?
         if (options.ShowBaloonTips)
         {
            // yes, setup and fire up a balloon tip. show tip 1000 ms.
            String ballooTitle = "FSW " + reason + ":";
            notifyIcon1.ShowBalloonTip(1000, ballooTitle, fileName, ToolTipIcon.Info);
         }
 
         // add given Strings + self created timeStamp to dataGridView
         int i = dataGridView1.Rows.Add();
         String timeStamp = DateTime.Now.ToString("yyyy'-'MM'-'dd HH':'mm':'ss");
         dataGridView1.Rows[i].Cells["MyColumnTimeStamp"].Value = timeStamp;
         dataGridView1.Rows[i].Cells["MyColumnName"].Value = fileName;
         dataGridView1.Rows[i].Cells["MyColumnReason"].Value = reason;
 
         // user wants to log events to file?
         if (options.LogToFile)
         {
            // yes, log to file
            addLineToLogfile(timeStamp, fileName, reason);
         }
 
         // user wants always to see very last event?
         if (options.AutoScrollDataGrid)
         {
            // yes, bring shown area to added line
            int scrollTo = dataGridView1.Rows.Count - 1;
            dataGridView1.FirstDisplayedScrollingRowIndex = scrollTo;
         }
      }
 
      // appends given strings to a self-made log file.
      // log file name is created on the fly on first call.
      String logFileName = "";
      private void addLineToLogfile(string timeStamp, string fileName, string reason)
      {
         // first call?
         if (logFileName.Length == 0)
         {
            // yes, create unique file name
            logFileName = "FSWLogfile_" + timeStamp.Replace(' ', '_').Replace(':', '_') + ".txt";
            try
            {
               using (StreamWriter sw = new StreamWriter(logFileName))
               {
                  // write info to very first line
                  sw.WriteLine("FileSystemWatcher Logfile " + timeStamp);
               }
            }
            catch (Exception exp)
            {
               MessageBox.Show(exp.Message, "FSW Error");
            }
         }
 
         // append given strings to logfile
         try
         {
            using (StreamWriter sw = new StreamWriter(logFileName, true))
            {
               sw.WriteLine(timeStamp + ";" + fileName + ";" + reason);
            }
         }
         catch (Exception exp)
         {
            MessageBox.Show(exp.Message, "FSW Error");
         }
      }
 
      // append given strings to log file
      private void trace(string timeStamp, string info, string reason)
      {
         // user want to see debug trace info?
         if (options.DebugToFile)
         {
            addLineToLogfile(timeStamp, info, reason);
         }
      }
 
      // append given strings to log file (overload 1)
      private void trace(string info, string reason)
      {
         // user want to see debug trace info?
         if (options.DebugToFile)
         {
            String timeStamp = DateTime.Now.ToString("yyyy'-'MM'-'dd HH':'mm':'ss");
            addLineToLogfile(timeStamp, info, reason);
         }
      }
 
      // append given strings to log file (overload 2)
      private void trace(string info)
      {
         // user want to see debug trace info?
         if (options.DebugToFile)
         {
            String timeStamp = DateTime.Now.ToString("yyyy'-'MM'-'dd HH':'mm':'ss");
            addLineToLogfile(timeStamp, info, "DEBUG");
         }
      }
 
      // returns true if an FSW event belongs to our own created logfile
      // in general we do not respond to those events, because it will
      // result in recursive "logToFile->event->logToFile->event->..."
      private bool isNotificationOnOwnLogFile(String fileName)
      {
         if (options.LogToFile || options.DebugToFile)
            if (logFileName.Length > 0)
               if (fileName.Contains(logFileName))
                  return true;
 
         return false;
      }
 
      // returns true if any filter-string matches given string
      // filter-strings are taken from Options-Window-TextEditBox
      // this is used to discard useless events from user 
      // (e.g. user does only want to see *.doc files)
      // string compare is 'ignore case'
      private bool fileExtensionFilterPassed(String s)
      {
         bool result = false;
         // extract extension from given fileName
         String extension = Path.GetExtension(s);
         // get Options-Window-TextEditBox
         String filter = options.FileFilterList;
         // those chars are useless for comparison
         char[] trimChars = { ' ', '.' };
 
         extension = extension.Trim(trimChars);
 
         // is given string valid?
         if (extension == "")
         {
            // no, just log to file
            trace("fileExtensionFilterPassed() " + s + " filtered by <has no extension>");
            return false;
         }
 
         // make both strings to lower chars
         extension = extension.ToLower();
         filter = filter.ToLower();
 
         // return true if any filter-extension matches given extension
         result = filter.Contains(extension);
 
         return result;
      }
 
      // user touched main window' size
      private void MainWindow_Resize(object sender, EventArgs e)
      {
         // minimize to system tray if minimize button was pressed
         if (WindowState == FormWindowState.Minimized)
         {
            Hide();
         }
 
         // store current size -> to be safed to configFile on application exit
         // (do not store position if minimized or maximized)
         if (WindowState == FormWindowState.Normal)
         {
            options.MainWindowSize = Size;
         }
 
         // user wants always to see very last event?
         if (options.AutoScrollDataGrid)
         {
            // yes, bring dataGridView's shown area to dataGridView's last line
            int scrollTo = dataGridView1.Rows.Count - 1;
            if (scrollTo > 0)
            {
               try
               {
                  // throws exception if there is no room to display a row
                  // e.g. window's overall size is very small
                  dataGridView1.FirstDisplayedScrollingRowIndex = scrollTo;
               }
               catch (Exception exp)
               {
                  Console.WriteLine(exp.Message);
               }
            }
         }
      }
 
      // user touched main window's position
      private void MainWindow_Move(object sender, EventArgs e)
      {
         // store current position -> to be safed to configFile on application exit
         // (do not store position if minimized or maximized)
         if (WindowState == FormWindowState.Normal)
         {
            options.MainWindowLocation = Location;
         }
      }
 
      // user clicked one row on dataGridView
      // 1. put row to "selected" state
      // 2. enable context menu items
      private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
      {
         // get a "where has user clicked" object
         DataGridView.HitTestInfo Hti;
         // setup the object
         Hti = dataGridView1.HitTest(e.X, e.Y);
 
         // user clicked a cell?
         if (Hti.Type == DataGridViewHitTestType.Cell)
         {
            // yes, enable context menu items and select clicked row (row color turns blue)
            contextMenuStrip1.Items["openToolStripMenuItem"].Enabled = true;
            contextMenuStrip1.Items["openContainingFolderToolStripMenuItem"].Enabled = true;
            dataGridView1.ClearSelection();
            ((DataGridViewRow)dataGridView1.Rows[Hti.RowIndex]).Selected = true;
         }
         else
         {
            // no, disable context menu items
            contextMenuStrip1.Items["openToolStripMenuItem"].Enabled = false;
            contextMenuStrip1.Items["openContainingFolderToolStripMenuItem"].Enabled = false;
         }
      }
 
      // user rightClicked and selected an item from context menu
      // -> user wants to open a filed isplayed in that cell
      private void openToolStripMenuItem_Click(object sender, EventArgs e)
      {
         // get name of file, user want to open
         String s = dataGridView1.SelectedRows[0].Cells["MyColumnName"].Value.ToString();
         // start process
         startProcess(s, null);
      }
 
      // user rightClicked and selected an item from context menu
      // -> user wants to open file's containing folder
      private void openContainingFolderToolStripMenuItem_Click(object sender, EventArgs e)
      {
         // get folder to show as an Explorer-Process
         String s = dataGridView1.SelectedRows[0].Cells["MyColumnName"].Value.ToString();
         s = System.IO.Path.GetDirectoryName(s);
         // start Explorer-Process with file's parent folder as argument
         startProcess("explorer.exe", "/e," + s);
      }
 
      // user doubleClicked a cell
      // -> user wants to open the file displayed in that cell
      private void dataGridView1_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
      {
         // check if left mouse button was used
         if (e.Button == MouseButtons.Left)
         {
            // yes, get name of file, user want to open
            int rowIndex = e.RowIndex;
            // check range of index (could be header, i.e. -1)
            if (rowIndex >= 0 && rowIndex < dataGridView1.Rows.Count)
            {
               String s = dataGridView1.Rows[rowIndex].Cells["MyColumnName"].Value.ToString();
               startProcess(s, null);
            }
         }
      }
 
      private void startProcess(String name, String arguments)
      {
         // get a process object
         System.Diagnostics.Process p = new System.Diagnostics.Process();
 
         try
         {
            // setup process' name and args
            p.StartInfo.FileName = name;
            p.StartInfo.Arguments = arguments;
            // start object
            p.Start();
         }
         catch (Exception exp)
         {
            trace("startProcess() " + exp.Message);
            MessageBox.Show(exp.Message, "FSW Error");
         }
      }
 
      // user rightClicked and selected an item from context menu
      // user wants to edit include/exclude directories
      private void editPathsToolStripMenuItem_Click(object sender, EventArgs e)
      {
         // get a directory-edit window object
         EditDirectoriesWindow edw = new EditDirectoriesWindow(this, pathsToWatch, pathsToExclude);
         // show object. On close we will get informed to rebuild our FileSystemWatchers-list
         edw.ShowDialog();
      }
 
      // appends FSW array with a new created FSW
      private void addFileSystemWatcher(String pathToWatch)
      {
         // get a FSW object
         FileSystemWatcher fsw = new FileSystemWatcher();
 
         try
         {
            // setup object
            fsw.BeginInit();
            fsw.Path = pathToWatch;
            // continue setup object
            fsw.IncludeSubdirectories = true;
            fsw.Filter = "*.*";     // we do our own filtering
            fsw.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.Size | NotifyFilters.FileName;
            fsw.Renamed += new RenamedEventHandler(fileSystemWatcher_Renamed);
            fsw.Changed += new FileSystemEventHandler(fileSystemWatcher1_Changed);
            fsw.Created += new FileSystemEventHandler(fileSystemWatcher_Created);
            fsw.Deleted += new FileSystemEventHandler(fileSystemWatcher_Deleted);
            fsw.Error += new ErrorEventHandler(fileSystemWatcher_Error);
            fsw.EnableRaisingEvents = true;
            fsw.SynchronizingObject = this;     // this thread creates the FSW -> this thread must handle FSW events
            fsw.EndInit();
         }
         catch (Exception e)
         {
            trace("addFileSystemWatcher() Cannot apply path: " + pathToWatch + " " + e.Message, "FSW Error");
            MessageBox.Show("Cannot apply path: " + pathToWatch + "\n" + e.Message, "FSW Error");
            fsw.Dispose();
            return;
         }
 
         // append object to array
         watchers.Add(fsw);
 
         // log to file
         trace("addFileSystemWatcher() added path: " + pathToWatch);
      }
 
      // searches inside FSW array for given string
      // if found, associated FSW is removed from array
      private void removeFileSystemWatcher(String pathToWatch)
      {
         // iterate all FSW's
         foreach (FileSystemWatcher f in watchers)
         {
            String s = f.Path.ToString();
 
            // is given string same as FSW's string?
            if (s.CompareTo(pathsToWatch) == 0)
            {
               // yes, remove FSW
               f.Dispose();
               watchers.Remove(f);
               trace("removeFileSystemWatcher() removed path: " + s);
            }
         }
      }
 
      // clear FSW array
      private void removeAllFileSytemWatchers()
      {
         trace("removeAllFileSytemWatchers()");
         foreach (FileSystemWatcher f in watchers)
         {
            f.Dispose();
         }
         watchers.Clear();
      }
 
      // reset and setup FSW array with given array of strings
      public void rebuildFileSystemWatchersList(PathContainer newPaths)
      {
         trace("rebuildFileSystemWatchersList()");
 
         // clear FSW-array
         removeAllFileSytemWatchers();
 
         // overtake given strings to our private member variable
         pathsToWatch = newPaths;
 
         // iterate strings
         foreach (String s in pathsToWatch.paths)
         {
            // append new FSW to FSW-array
            addFileSystemWatcher(s);
         }
 
         // show some text in window's status bar
         updateStatusBarText();
      }
 
      // build a string to be shown in lower left window area
      private void updateStatusBarText()
      {
         // setup info text
         int watcherCount = watchers.Count;
         int notifyCount = dataGridView1.Rows.Count;
         String msg = "Paths Watching: " + watcherCount;
         msg += " | Total Notifications: " + totalIncommingNotificationEvents;
         msg += " | Shown: " + notifyCount;
         msg += " | Filtered: " + (totalIncommingNotificationEvents - notifyCount);
         // show info text
         toolStripStatusLabel1.Text = msg;
      }
 
      // user rightClicked and selected an item from context menu (main window)
      // user wants to close application (this)
      private void exitToolStripMenuItem1_Click(object sender, EventArgs e)
      {
         // clean up, save options and exit
         cleanUpAndSafeConfig();
         Application.Exit();
      }
 
      // user rightClicked and selected an item from context menu (main window)
      // user wants to show option-window
      private void optionsToolStripMenuItem1_Click(object sender, EventArgs e)
      {
         options.ShowDialog();
      }
 
      // user rightClicked and selected an item from context menu (main window)
      // user wants to show help-window
      private void helpToolStripMenuItem_Click(object sender, EventArgs e)
      {
         string msg = "";
         msg += "This program monitors files and folders on your computer or network." + Environment.NewLine;
         msg += "Executed in background, it supervises file and folder changes (create, delete, change)." + Environment.NewLine;
         msg += "Right click in main window, then select 'Edit Paths...' to configure directories you want to watch." + Environment.NewLine;
         MessageBox.Show(msg, "FSW Help", MessageBoxButtons.OK, MessageBoxIcon.Information);
      }
 
      // user rightClicked and selected an item from context menu (systemTrayIcon)
      // user wants to show option-window
      private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
      {
         options.ShowDialog();
      }
 
      // user rightClicked and selected an item from context menu (systemTrayIcon)
      // user wants to show main window (this)
      private void showToolStripMenuItem_Click(object sender, EventArgs e)
      {
         notifyIcon1_DoubleClick(null, null);
      }
 
      // user rightClicked and selected an item from context menu (systemTrayIcon)
      // user wants to close application (this)
      private void exitToolStripMenuItem_Click(object sender, EventArgs e)
      {
         // clean up, save options and exit
         cleanUpAndSafeConfig();
         Application.Exit();
      }
 
      // user wants to close main window
      // can be canceled, if user enabled "close to systemTray"
      private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
      {
         // event comes from user?
         if (e.CloseReason == CloseReason.UserClosing)
         {
            // user do not want to exit but minimize window?
            if (options.MinimizeToTrayOnCloseWindow)
            {
               // yes, minimize and discard event
               e.Cancel = true;
               WindowState = FormWindowState.Minimized;
               Hide();
            }
            else
            {
               // no, clean up and save options
               cleanUpAndSafeConfig();
            }
         }
      }
 
      // user double clicked systemTray icon
      // user wants to see main window (this)
      private void notifyIcon1_DoubleClick(object sender, EventArgs e)
      {
         // bring window to normal state and set fokus
         Show();
         WindowState = FormWindowState.Normal;
         Activate();
      }
 
      // user single clicked systemTray icon
      // user wants to show/hide main window (this)
      private void notifyIcon1_MouseClick(object sender, MouseEventArgs e)
      {
         // left mouse button used?
         if (e.Button == MouseButtons.Left)
         {
            // yes, check if this window is currently minimized
            if (WindowState == FormWindowState.Minimized)
            {
               // yes, bring window to normal state and set fokus
               Show();
               WindowState = FormWindowState.Normal;
               Activate();
            }
            else
            {
               // no, minimize window
               Hide();
               WindowState = FormWindowState.Minimized;
            }
         }
      }
 
      // safe config file
      // remove watchers from system
      // free objects
      private void cleanUpAndSafeConfig()
      {
         trace("cleanUpAndSafeConfig()");
         options.safe("FSWConfig.txt");
         notifyIcon1.Visible = false;
         notifyIcon1.Dispose();
         removeAllFileSytemWatchers();
         notifyIcon1.Dispose();
      }
 
      // user pressed a key
      // check if we should open focused element
      // check if we should minimize window
      private void dataGridView1_KeyDown(object sender, KeyEventArgs e)
      {
         // check if user wants to open file currently focused in dataGridView
         if (e.KeyCode == Keys.Return)
         {
            // check if any row is selected at all
            if (null != ((DataGridView)sender).CurrentRow)
            {
               // get name of file, user want to open
               int rowIndex = ((DataGridView)sender).CurrentRow.Index;
               // check range of index (could be header, i.e. -1)
               if (rowIndex >= 0 && rowIndex < dataGridView1.Rows.Count)
               {
                  String s = dataGridView1.Rows[rowIndex].Cells["MyColumnName"].Value.ToString();
                  startProcess(s, null);
                  // indicate our handling to caller
                  e.Handled = true;
               }
            }
         }
         // check if user wants to minimize application window
         else if (e.KeyCode == Keys.Escape)
         {
            WindowState = FormWindowState.Minimized;
            MainWindow_Resize(sender, (EventArgs)e);
         }
      }
 
      // user deleted row(s)
      private void dataGridView1_UserDeletedRow(object sender, DataGridViewRowEventArgs e)
      {
         // show some text in window's status bar
         updateStatusBarText();
      }
   }
}