Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / WPF

RunCmd - (A WPF-MVVM Batch File Editor/Runner Utility. Automate your repetitive tasks using commandline batch files)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (10 votes)
27 Aug 2018CPOL7 min read 29K   1.4K   34   2
RunCmd is a windows batch file editor,runner utility. It can be used to automate our repetitive tasks using commandline batch files.

Introduction

RunCmd is a tiny batch file editor/runner utility written in WPF - MVVM pattern. It can be used to create/edit batch files and execute them. RunCmd also acts as a commonplace for storing our batch files. We can quickly find our batch files (using filter textbox) and run them (double clicking on filename or select and click Run).

The MVVM framework is the minimalistic MVVM framework decribed in this article. I will not go into full details of our MVVM implementation as this can be explored in the article at this link.

Background

Many times, we have to perform some repetitive tasks in Windows environment which can be quickly done using command line. (e.g. Here and here are a list of some Windows system applications we can run using command line.)

Also, as a developer, we often find ourselves in need of finding some specific locations or executing some specific programs - which we know we can automate using command line. For example:

  • Automate the launch of our favorite apps and folders - so that our apps & folders are just a double click away every morning.
  • Runing our /bin/Debug/myprogram.exe or /bin/Release/myprogram.exe without attaching the Visual Studio debugger to it.
  • Or running our myprogram.exe with commandline parameters.)
  • Automate launch of Windows Control Panel apps (like "Programs & Features", "IIS Management", "System Properties", "Device Manager", etc.) - this can easily be done by command line/batch files.
  • Launching Visual Studio in admin mode {like I use nircmd elevate to do that}
  • Running NAnt builds or running other command line utilities.
  • These are few uses I've found of this App that are helpful for me. Basically, there can be a number of uses of this simple utility.

Moreover, remembering all the commands might not be savvy either. If we have a utility which can help us create, edit, find and run batch files, this can help us speed up our daily tasks in a considerable way.

RunCmd tries to do this by giving us a commonplace to create, edit, find and run our batch files.

Image 1

Using RunCmd

The usage of RunCmd is quite straightforward.

  1. Home View
    1. Home View is the main workspace of our App. On the left, we will see the list of our batch files. On the right, there is Input Textbox (Batch file Contents) at the top and Output Texbox (Output of running the batch file) at the bottom. We have some buttons (Save, New, Run, etc.) which we can use to perform operations.
    2. We can single click a file in list to select it for editing or running. We can double-click any file in the files list at left to Run it.
    3. Once selected, the batch file contents are shown in the right hand side top textbox. We can edit the contents and Save (Save only Not Run) or Run it or click "New" to create a new file.
    4. Once we run the Batch file, we will see the output of the console in "Output" textbox at the right panel.
  2. Options Window
    • The Options Window has some controls to change the settings. The change in settings are notified to the MainWindow and saved in config file.
  3. Logs View
    • Logs view is just a display of Logs which RunCmd has generated. We may usually want logs if we are directing them to console. Turn off logging from "Options" window if it's not required.
    • What we see here is a list of files. We can double click to launch the file.  Click "Back" button to go back to Home View.

How It Works

As discussed earlier, the MVVM portion of the application has already been explained in this article. If you would like to know about the MVVM framework we are using, please visit the above link. I will cover the functionality we have added to get RunCmd here.

1. Home View

Image 2

We will notice the following in the Document Outline &  Design View of HomeView.xaml page (above):

1.a Left Panel (Batch Files List)

In the left panel, we have ScrollViewer with ListView which binds to the ListCollectionView (named BatFilesView) on the top of batch file names picked from the working directory.

From MSDN:

"You can think of a CollectionView as the layer on top of the binding source collection that allows you to navigate and display the source collection based on sort, filter, and group queries, all without having to manipulate the underlying source collection itself. If the source collection implements the INotifyCollectionChanged interface, the changes raised by the CollectionChanged event are propagated to the views."

So our ListCollectionView (BatFilesView) adds a filter  which is triggerred on the Text Change of FileNames filter textbox (which binds to property BatFilesFilterText in HomeViewModel. Once this property changes - we trigger the filter - which in turn triggers the List to update accordingly.)

1.b Right Panel (Batch File Edit textbox and Output Textbox)

On the right in HomeView, we will notice the Textbox to edit the Name & Contents of our Batch file. We have some basic buttons which do the standard jobs as per names. ("Save" button saves the current contents to fileName specified. "New" button creates a fresh file and sets the SelectedBatFile to the New file.) These are pretty much basic CRUD activities what we can also see in our sample MVVM.

What will be of interest to us here will be how the "Run" button's command executes.  Below is how the code of Run command looks like. What we will notice here is Command simply creates a process by passing in the batch file that we just now saved as an argument to command prompt. How we are running this process is:

  1. First of all, we are running this process in a separate thread as we do not want to block the UI thread. We can run it right here also, just that it will simply block the UI thread and we will only get notified once the process is finished. For long running process (or builds), this is kind of annoying. So we run this process in a separate thread. And while this process thread is running - I've attached my handlers to the process to keep the UI thread updated with the current output or errors:
  2. We attached one handler (SortOutputHandler) to grab all the output of process and append it to our buffer (StringBuilder). We will use this buffer at the end of the process to create our log.
  3. We attached  a handler to append the output to the OutputText.
  4. We also attached a handler to grab the output of Error and append it to OutputText (if in case our process lands in error.)
  5. So then, we simply run the process in a new thread - and try to make sure that our process terminates properly and we log the outputs of process to our log (BuildCompleted handler)

The "Run" Button or our HomeView is relayed to the following Command (in HomeViewModel):

C#
private void ExecRunCmd(object obj)
        {
            if (ConfirmBeforeRun)
            {
                MessageBoxResult confirmRunResult = MessageBox.Show
                ("Are you sure you want to run this file?", "Run Batch File?", 
                MessageBoxButton.OKCancel);
                if (confirmRunResult == MessageBoxResult.Cancel)
                {
                    return;
                }
            }

            SaveOrUpdateCurrentBatFile();
            try
            {
                string[] cmdArgs = new string[]
                    {
                        "cmd /C ",
                        "\""+SelectedBatFile.TextFileName+"\""
                    };

                _process = new Process();
                _process.StartInfo = new ProcessStartInfo(ExeFileName)
                {
                    Arguments = string.Join(" ",cmdArgs),
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    RedirectStandardInput = true,
                    CreateNoWindow = true,
                    WorkingDirectory = Utility.getAppBasePath()
                };

                _process.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
                _process.OutputDataReceived += 
                         new DataReceivedEventHandler((o, d) => AppendText(d.Data));
                _process.ErrorDataReceived += 
                         new DataReceivedEventHandler((o, d) => AppendError(d.Data));
                _process.EnableRaisingEvents = true;
                _process.Exited += new EventHandler(BuildCompleted);

                TxtOutput = string.Empty; //Initialize the output first

                    ThreadStart ths = new ThreadStart(() =>
                    {
                        bool ret = _process.Start();
                        _process.BeginOutputReadLine();
                        _process.BeginErrorReadLine();
                        //is ret what you expect it to be....
                    });
                    _workerThread = new Thread(ths);
                    _workerThread.IsBackground = true;
                    _workerThread.Start();
                    IsCmdRunning = true;

                _workerThread.Join();

            }
            catch (Exception exc)
            {
                throw (exc);
            }
        }

2. Options Window (Popup)

Image 3

Options Window contains the basic controls arranged in a grid. Some lables, checkboxes, buttons to set filenames and Reset button. The functions of these controls are very much simple updating the properties in the OptionsWindowViewModel which in turn saves the properties to Settings file (simple XmlSerialized to \config\AppConfig.cfg file).

The point of interest in this window is the messaging mechanism. As soon as one of the properties is updated, this view publishes an ObjMessage to notify all observers about the change. The Main Window subscribes to the ObjMessage we publish in the Options Window. We can notice the following Handler for our ObjMessage in "HomeViewModel". (This is how it updates itself as soon as it receives the notification of property change.)

For example, the following code in OptionsWindowViewModel publishes an ObjMessage once it is done updating the ConfirmBeforeRun property:

C#
public bool ConfirmBeforeRun
{
    get { return _ConfirmBeforeRun; }
    set
    {
        _ConfirmBeforeRun = value;
        Settings.Instance.ConfirmBeforeRun = value;
        Settings.Instance.Save();
        OnPropertyChanged("ConfirmBeforeRun");
        StatusMessage = string.Format
           ("Will {0} confirm before running batch files!", value ? "" : "not");
        ObjMessage message = new ObjMessage("MainView","ConfirmBeforeRun");
        EventAggregator.Publish(message);
    }
}

Handler for ObjMessage in HomeWindowViewModel:

C#
private void HandleOptionsUpdated(ObjMessage obj)
{
    if (obj.Notification.Equals("MainView"))
    {
        switch (obj.PayLoad.ToString())
        {
            case ("UpdateSavedCommandsPath"):
                SavedCommandsLoc = Settings.Instance.SavedCommandsPath;
                break;
            case ("UpdateExePath"):
                ExeFileName = Settings.Instance.ExePath;
                break;
            case ("ResetSettings"):
                ExeFileName = Settings.Instance.ExePath;
                SavedCommandsLoc = Settings.Instance.SavedCommandsPath;
                ConfirmBeforeRun = Settings.Instance.ConfirmBeforeRun;
                break;
            case ("ConfirmBeforeRun"):
                ConfirmBeforeRun = Settings.Instance.ConfirmBeforeRun;
                break;
            default:
                break;
        }
    }
}

3. Logs View

Image 4

The LogsView is just a simple list display of log files from \log\* directory relative to RunCmd.exe. Double clicking the file will simply call the System to launch the file as below:

C#
private void ExecViewLogCmd(object obj)
{
    System.Diagnostics.Process.Start(SelectedLogFile.TextFileName);
}

We use our standard navigation system to navigate back & forth between:

C#
private void ExecGoBackCmd(object obj)
{
    _eventAggregator.Publish(new NavMessage("HomeView"));
}

Useful Links

History

  • 5th November, 2014: Initial publication

License

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


Written By
Software Developer (Senior)
Singapore Singapore
I love programming, reading, and meditation. I like to explore management and productivity.

Comments and Discussions

 
QuestionWin32 Application? Pin
Aaron Michael28-Aug-18 19:33
Aaron Michael28-Aug-18 19:33 
QuestionGracias Pin
Member 1396323327-Aug-18 5:06
Member 1396323327-Aug-18 5:06 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.