Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C#

Get the Best of Both Worlds: Command Line and GUI

Rate me:
Please Sign up or sign in to vote.
4.97/5 (30 votes)
29 Jul 2023CPOL9 min read 46.8K   82   15
Rapidly develop a console app with a lot of options and give a command line program a GUI
The article introduces Command Line GUI, a shared library inspired by the Plossum library, which allows rapid development of console apps with parameters and options, providing a GUI without additional programming. It demonstrates how to develop plugins for existing command line programs or console apps utilizing Plossum, using a data model class and attributes to describe options, and how Command Line GUI can handle multiple plugins.

Introduction

You might have developed some command line programs (console apps in .NET) which might be utilities or business applications of batch processing, etc. Often the console app takes a few parameters and options, and you need to write codes to parse the arguments. I believe many of us might have written some ad-hoc parsers for such purpose. It is better to have a shared library to take care of the parsing job, especially if you have different combinations of more than 5 parameters. I had used the Plossum library by Peter Palotas in a few projects for parsing command line options. And this library had inspired to develop Command Line GUI.

This article introduces:

  1. How to rapidly develop a console app with a few parameters and many options. Fonlow.CommandLineGui.Core.dll is compatible with the Plossum Library, with additional features.
  2. The console app could have a GUI without the need for further programming.
  3. For an existing command line program on any platform, you can even give it a GUI.

Remarks

The source codes of Command Line GUI contain some fragments of the Plossum library, in Core/PlossumFragment, for giving you the same API of declarative programming. If you had been using the Plossum library, you could easily migrate to Command Line GUI and remove the dependency on the Plossum library.

Image 1

Command Line Options with Plossum

If you have read the Powerful and simple command line parsing in C# and used Plossum in your console apps, you may skip this chapter and go to the next one.

This demo program below gives you a snapshot of using Plossum in a console app through declarative programming.

C#
using System;
using Plossum.CommandLine;
using Fonlow.CommandLine;

namespace MyPlossum
{
    class Program
    {
        static int Main(string[] args)
        {
            var options = new Options();
            CommandLineParser parser = new CommandLineParser(options);
            Console.WriteLine(parser.ApplicationDescription);
            parser.Parse();
            Console.WriteLine(options.DurationInSecond);
           
            if (options.Help)
            {
                Console.WriteLine(parser.UsageInfo.GetOptionsAsString(78));
                return 0;
            }
            
            if (parser.HasErrors)
            {
                Console.WriteLine(parser.UsageInfo.ToString(78, true));
                return 1;
            }
            Console.WriteLine(String.Format("{0} is executed.", parser.ApplicationName));
            return 0;
        }
    }
    [CommandLineManager(ApplicationName = "MyPlossum", 
     Description = "Demonstrate the power of Plossum", 
     EnabledOptionStyles = OptionStyles.Windows, 
     Copyright="Fonlow (c) 2013", Version="1.1")]
    [CommandLineOptionGroup("detail", Name = "Detail")]
    [CommandLineOptionGroup("other", Name = "Other")]
    public class Options
    {
        [CommandLineOption(Aliases = "F", 
         Description = "Function name, e.g., /F=FirstFunction")]
        public string Function { get; set; }
        [CommandLineOption(Description = "URL. 
         e.g., /Url=http://csharpoptparse.sourceforge.net/",  GroupId = "detail")]
        public string Url { get; set; }
        [CommandLineOption(Aliases = "du", Name = "Duration", 
         Description = "Duration in second.", RequireExplicitAssignment = true, 
         DefaultAssignmentValue = 3600.08d, GroupId = "detail")]
        public double DurationInSecond { get; set; }
        [CommandLineOption(Aliases = "h", Description = "Shows this help text", 
                           GroupId = "other")]
        public bool Help
        {
            get;
            set;
        }
        [CommandLineOption(Aliases = "OE", 
         Description = "enum text", GroupId = "detail")]
        public MyEnum OkEnum
        {
            get;
            set;
        }
    }
    public enum MyEnum { None, Hello, World, Plossum };

So you need to define a data object class Options describing the options as data model, and decorate the properties with CommandLineOptionAttribute, and the command line parser of Plossum library will do the heavy lifting of parsing arguments into the Options object.

[MyPlosum.jpeg]

Command Line GUI

You may be asking why we need a GUI for an existing command line program.

Background

Back to many years ago, I had found Robocopy.exe for synchronizing files between 2 drives. This command line program is very powerful with a few dozens options, and comes with even a manual robocopy.doc in addition to the online help. It is apparently not convenient for many people including me to remember the options or look up the manual. And there is actually Robocopy GUI from Microsoft, and there are quite a few other similar programs if you search the Internet, however, none of them satisfied me. So I had developed Better Robocopy GUI in 2009.

Generally, IT admins like Robocopy, and casual users and non-technical people like Robocopy GUI. I am a software developer and a casual user of Robocopy, so I need a Better Robocopy GUI (dead link in codeplex.com).

Sometime, you might have developed a command line program called DoWonderfulThings, and the IT admins or the support guys use it for daily operations; then Alice from the Marketing Department would like to DoWonderfulThings as well, however, she is apparently not the kind of person who would appreciate remembering and typing command line options. So you are going to write DoWonderfulThings GUI as Derk Benisch in Microsoft had done for Robocopy. You get paid to develop DoWonderfulThings GUI anyway. Next time you will develop DoBetterThings, and then DoBetterThings GUI, and so on. If you don’t mind repeating such development process, and carry out such jobs again and again, you may not be interested in what is described below.

Soon after releasing Better Robocopy GUI, I found that it was fairly easy to refactor the program’s structure to make it support other command line programs of which people may desire to have GUI, then I hand created a fork of Better Robocopy GUI called Command Line GUI. And Robocopy related functionality becomes the primary plug-in of Command Line GUI, while you will be able to develop plugin for other existing command line programs.

A typical plugin needs to define a data model describing options as properties and decorate each property with some attributes understood by PropertyGrid which will render each property with proper GUI controls.

Image 2

C#
namespace Fonlow.CommandLineGui.Robocopy
{
    public class Options
    {
        const string TOP_CATEGORY = " ";
        const string COPY_OPTIONS = "Copy Options";
        const string LOGGING_OPTIONS = "Logging Options";
        const string RETRY_OPTIONS = "Retry Options";
        const string FILE_SELECTION_OPTIONS = "File Selection Options";

        [Category(COPY_OPTIONS)]
        [Description("Copies subdirectories (excluding empty ones).")]
        [DisplayName("/S")]
        [DefaultValue(false)]
        public bool SlashS
        {
            get;set;
        }

        const CopyFlags fullCopy = CopyFlags.A | CopyFlags.D | 
              CopyFlags.O | CopyFlags.S | CopyFlags.T | CopyFlags.U;
        const CopyFlags secCopy = CopyFlags.D | CopyFlags.A | CopyFlags.T | CopyFlags.S;

        [Category(COPY_OPTIONS)]
        [Description("Copies the file information specified by copyflags, 
        which can be any combination of the following :" + "\n\r" +
        "D – file Data. S – file Security (NTFS ACLs)." + "\n\r" +
        "A – file Attributes. O – file Ownership information." + "\n\r" +
        "T – file Timestamps. U – file aUditing information." + "\n\r" +
        "Source and destination volumes must both be NTFS to copy Security, 
                        Ownership or Auditing information.")]
        [DisplayName("/COPY:")]
        [DefaultValue(CopyFlags.None)]
        [NameValueNoSpace]
        [Editor(typeof(CopyFlagsEditor), typeof(UITypeEditor))]
        public CopyFlags SlashCopy
        {
            get;set;
        }

Basically, DisplayNameAttribute gives the option name, DescriptionAttribute defines content to be displayed in the hint area of the PropertyGrid, and DefaultValueAttribute will let PropertyGrid know whether to render a modified option value in Bold, while CategoryAttribute groups options, and EditorAttribute introduces a custom editor for a type of option, such as flagged enumeration.

This is how Command Line GUI v1.x works. And v2.0 had undergone major structure changes and face-lift, so that giving a command line program a GUI has become even easier.

Command Line GUI with Plossum

As you have seen so far, Command Line GUI and Plossum share the same design concept of using a data model class as well as attributes to describe options. It sounds natural that a console application utilizing Plossum may easily get into Command Line GUI through further decoration with DisplayNameAttribute, DefaultValueAttribute and EditorAttribute, etc. However, obviously what is described by those BCL attributes overlap with what is described by named properties of Plossum’s CommandLineOptionAttribute.

BCL attributes Named properties of CommandLineOptionAttribute
DisplayName Name or Alias
Description Description
DefaultValue DefaultAssignmentValue
Category GroupId

PropertyGrid requires those BCL attributes to render GUI controls, while Plossum needs CommandLineOptionAttribute to parse command line options. And having redundant descriptions of those properties of the Options data model class does not look nice and productive, though working.

To make life easier, in Command Line GUI v2.0 and v3.0, the development work of a plugin will need CommandLineOptionAttribute only, so you may define the properties of Option this way:

C#
 [CommandLineOption(Name = "A-", GroupId = OptionGroups.COPY_OPTIONS,
     Description = "Turns off the specified attributes in copied files.
                    \n\rThe following attributes can be turned off:\n\r" +
                   "R – Read only S – System N – Not content indexed\n\rA –
Archive H – Hidden T – Temporary")]
 [Editor(typeof(RashFlagsEditor), typeof(UITypeEditor))]
 public Rashcneto SlashAMinus
 {
     get;
     set;
 }

 [CommandLineOption(Name = "CREATE", GroupId = OptionGroups.COPY_OPTIONS,
     Description = "Creates a directory tree structure
     containing zero-length files only (that is, no file data is copied).")]
 public bool SlashCreate { get; set; }

Then Command Line GUI will generate PropertyGrid consumable BCL attributes for the options type at run time dynamically.

Prerequisites

To develop a console app utilizing Plossum or write a plugin for Command Line GUI, you need to download Command Line GUI binaries or its source codes.

File Name Description
Fonlow.CommandLineGui.Core.dll Core library, for command line programs
Fonlow.CommandLineGui.Gui.dll GUI components
Antlr4.Runtime.v4.0.dll Antlr .NET
RobocopyParameters.dll Robocopy plugin
CommandLineGui.exe Main executable

You may checkout the source codes and particularly check project MyPlossum and project RobocopyParameters in the Examples solution folder.

Develop Plugin for Existing Native Command Line Application

Using Robocopy as example, you may follow these steps:

Step 1: Create a class library for Robocopy.

Step 2: Add project reference to Fonlow.CommandLineGui.Core.dll, and optionally Fonlow.CommandLineGui.Gui.dll if you need some custom editors for some options.

Step 3: Write a parameters data model class describing each fixed parameter or option through a property; decorate each property presenting a fixed parameter with FixedParameterAttribute; decorate each property representing an option with CommandLineOptionAttribute; and optionally decorate with an EditorAttribute for introducing a custom editor.

Step 4: Register the assembly of the class library in CommandLineGui.exe.config. And copy RobocopyParameters.dll to the program directory of Command Line GUI.

The following codes are for Robocopy:

C#
[CommandLineManager(ApplicationName = "Robocopy",
 Description = "Robocopy options", RequireExplicitAssignment = true)]
public class RobocopyOptions
{
    [FixedParameter(Category = OptionGroups.TOP_CATEGORY,
     Description = @"Source Directory (drive:\path or \\server\share\path).",
        DisplayName = "Source", Order = 1, DefaultValue = "SourceDir")]
    public string Source { get; set; }

    [FixedParameter(Category = OptionGroups.TOP_CATEGORY,
     Description = @"Destination Dir  (drive:\path or \\server\share\path).",
       DisplayName = "Destination", Order = 2, DefaultValue = "DestDir")]
    public string Destination { get; set; }

    [FixedParameter(Category = OptionGroups.TOP_CATEGORY,
     Description = "File(s) to copy  (names/wildcards: default is \"*.*\").",
     DisplayName = "Files", Order = 3, DefaultValue = "FilesSeparatedBySpace")]
    public string[] Files { get; set; }

    [CommandLineOption(Name = "?",
        Description = "Usage info and help.")]
    public bool Help
    { get; set; }

    [CommandLineOption(Name = "S", GroupId = OptionGroups.COPY_OPTIONS,
        Description = "Copies subdirectories (excluding empty ones).")]
    public bool SlashS
    {
        get;
        set;
    }

    [CommandLineOption(Name = "E", GroupId = OptionGroups.COPY_OPTIONS,
        Description = "Copies all subdirectories (including empty ones).")]
    public bool SlashE
    {
        get;
        set;
    }

Hints

  1. Command Line GUI will instantiate the first class decorated by CommandLineManagerAttribute in the assembly, so please make sure you define only one such class in the assembly.
  2. If you want to define rules of inclusion, exclusion and combination among options, you may check the source codes of the Robocopy plugin for some clues.

Develop Plugin for a Console App that Utilizes Plossum

You had developed a .NET console app DoWonderfulThings.exe that utilizes Plossum, now your customers want a GUI.

Step 1: Add project reference to Fonlow.CommandLineGui.Core.dll, and optionally Fonlow.CommandLineGui.Gui.dll if you need some custom editors for some options; and remove reference to Plossum.dll.

Step 2: Register DoWonderfulThings in CommandLineGUI.exe.config. And copy DoWonderfulThings.exe to the program directory of Command Line GUI.

In short, you just need to rebuild the assembly with new references, then you get GUI.

You Need Only Plossum Attributes without Referencing Plossum.dll

During the development of Command Line GUI v2.0, I had found a few minor defects in Plossum 0.4 which prevent Command Line GUI from using it. For console applications, Plossum 0.4 had actually served its original purpose perfectly. Now Plossum is running in a Winforms program, and the codes could not deal with such new environment, so I had to create a fork from Plossum 0.4 and fixed those minor defects. If you are interested in what I had changed, you may go to https://sourceforge.net/p/commandlinegui/code/91/tree/branches/Plossum/ and compare with https://sourceforge.net/p/plossum/code/25/tree/trunk/.

In addition to the minor fixes, in Command Line GUI v2.0, I had removed Plossum’s dependency on C5.dll. Peter had made it clear in 2007 that he had chosen C5.dll because it was powerful and easy to use comparing with .NET Framework 2. However, Command Line GUI v2.0 targets .NET Framework 4, so the advantage C5.dll has been fading in this landscape. Therefore, you should be using Plossum v0.5 which is compatible with v0.4 at API level while having NO dependency on C5.dll.

In Command Line GUI v3.0, I had rewritten the command line arguments parser on top of ANTLR, and only a small fragment of Plossum codes is kept for giving you the same API of declarative programming. So you need Plossum.dll no more but Antlr4.Runtime.v4.0.dll. The API in Fonlow.CommandLineGui.Core.dll is compatible with Plossum Library, and additionally supports fixed parameters, array, and options rules of exclusion, inclusion and combination.

Configuration

The configuration is stored in CommandLineGui.exe.config.

XML
<applicationSettings>
  <Fonlow.CommandLineGui.Settings>
    <setting name="PluginAllocationMethod" serializeAs="String">
      <!-- Registration for handling multiple command line programs
      or Mono for only 1 program listed in the first item of AsssemblyNames.-->
      <value>Registration</value>
    </setting>
    <setting name="AssemblyNames" serializeAs="Xml">
      <!--Each assembly name should not contain
          file extension name such as dll or exe.-->
      <value>
        <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <string>RobocopyParameters</string>
          <string>MyPlossum</string>
        </ArrayOfString>
      </value>
    </setting>
  </Fonlow.CommandLineGui.Settings>
</applicationSettings>

<userSettings>
  <Fonlow.CommandLineGui.Settings>
    <!--This setting is effective only if PluginAllocationMethod is Registration.
        And the actual value is in AppData of the user profile area.-->
    <setting name="AssemblyNameOfCommand" serializeAs="String">
      <value>RobocopyParameters</value>
    </setting>
  </Fonlow.CommandLineGui.Settings>
</userSettings>

Mono

You may set Command Line GUI to run with only one program through assigning PluginAllocationMethod with value Mono.

Registration

If you want one instance of Command Line GUI to handle multiple plugins, you may assign PluginAllocationMethod with value Registration. In addition, you need to copy respective assemblies to the program directory of Command Line GUI and register each in setting AssemblyNames each of which should NOT contain file extension name such as dll or exe.

Summary

Command Line GUI project includes a library compatible with Plossum so you can use declarative programming to define options and skip writing a parser. This library supports more complex combinations of options, as seen in Robocopy.

Command Line GUI renders GUI controls for command line programs which are built on the API in Fonlow.CommandLineGui.Core.dll.

So you will get the best of both worlds: command line and GUI.

Points of Interests

If you Google "C# command line arguments parser", you will find a long list of codes or libraries for parsing command line arguments. You may check if they:

  1. Support strong typing options.
  2. Support rules of inclusion, exclusion and combination.
  3. Introduce least foot print in your application codes while giving professional looks.
  4. Have comprehensive test suits to ensure the correctness in common formats of Windows command line programs.

References

Image 3

History

  • 9th September, 2013: Initial version
  • 29th July, 2023: Update

License

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


Written By
Software Developer
Australia Australia
I started my IT career in programming on different embedded devices since 1992, such as credit card readers, smart card readers and Palm Pilot.

Since 2000, I have mostly been developing business applications on Windows platforms while also developing some tools for myself and developers around the world, so we developers could focus more on delivering business values rather than repetitive tasks of handling technical details.

Beside technical works, I enjoy reading literatures, playing balls, cooking and gardening.

Comments and Discussions

 
QuestionGood idea, but there is another approach Pin
MSBassSinger3-Aug-23 5:42
professionalMSBassSinger3-Aug-23 5:42 
AnswerRe: Good idea, but there is another approach Pin
Sergey Alexandrovich Kryukov4-Aug-23 7:53
mvaSergey Alexandrovich Kryukov4-Aug-23 7:53 
GeneralRe: Good idea, but there is another approach Pin
MSBassSinger4-Aug-23 8:02
professionalMSBassSinger4-Aug-23 8:02 
AnswerRe: Good idea, but there is another approach Pin
Sergey Alexandrovich Kryukov4-Aug-23 10:06
mvaSergey Alexandrovich Kryukov4-Aug-23 10:06 
GeneralRe: Good idea, but there is another approach Pin
MSBassSinger4-Aug-23 10:17
professionalMSBassSinger4-Aug-23 10:17 
AnswerRe: Good idea, but there is another approach Pin
Sergey Alexandrovich Kryukov4-Aug-23 12:44
mvaSergey Alexandrovich Kryukov4-Aug-23 12:44 
AnswerRe: Good idea, but there is another approach Pin
Zijian4-Aug-23 10:41
Zijian4-Aug-23 10:41 
GeneralRe: Good idea, but there is another approach Pin
MSBassSinger4-Aug-23 11:27
professionalMSBassSinger4-Aug-23 11:27 
GeneralMy vote of 5 Pin
Volynsky Alex18-May-14 4:59
professionalVolynsky Alex18-May-14 4:59 
QuestionI like it Pin
haoyujie9-Feb-14 15:56
haoyujie9-Feb-14 15:56 
AnswerRe: I like it Pin
Zijian9-Feb-14 17:06
Zijian9-Feb-14 17:06 
GeneralRe: I like it Pin
haoyujie9-Feb-14 20:57
haoyujie9-Feb-14 20:57 
GeneralRe: I like it Pin
haoyujie9-Feb-14 21:22
haoyujie9-Feb-14 21:22 
GeneralMy vote of 5 Pin
BrunoHewitt16-Sep-13 1:05
BrunoHewitt16-Sep-13 1:05 
GeneralMy vote of 5 Pin
fredatcodeproject9-Sep-13 0:05
professionalfredatcodeproject9-Sep-13 0:05 

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.