Notepad – –

This article is somewhat unusual in comparison to all previous ones, because it’s more like a conclusion and the summaryzing of the previous articles of this season. This is an example of interaction between the components of which I wrote recently: interprocess communication, command manager, the registration of extensions. Example I named so because it was chosen as an example of Notepad++, but due to the fact that this program only shows the options capability in close to real-life, we get a minus-minus.

You can name the current compilation of the backbone for further development. Now the program can open by clicking on the file with a registered extension, the file opens in new tabs, or, if you ask, then close all other tabs and the file will open in a single tab. There will always be running only one instance at one time.

I think that I should still consider how it works in reality. Focusing only on the differences that have emerged in real life. At the same time and will be seen asdescriptive material is different from a working prototype.

Once again on the “purpose” of the program: run only one instance, opened file sare in a new tab, if the program is already running.
The program deliberately complicated in some places, as will surely be a live project. I will focus on programming in entered these places. So …

The uniqueness of the instance

The example in the article was based on WinForms and everything was quite simple, with WPF a different approach. Arguments passed to the program at startup are processed in the class App, then goes to show the main window MainWindow. When necessary to complete the second instance, we must call the completion of the application class App. This is not a problem, but we must somehow transmit knowledge about how to close the application or not. To do this, select an interface from a base class.

public interface IApp {
    bool IsOriginal { get; }
}

Now there would be a link from the main window of the application.

public MainWindow() {
    if (((IApp) Application.Current).IsOriginal) {
        ...
        InitializeComponent();
        ...
    }
    else {
        Application.Current.Shutdown();
    }
}

Thus the window of the second program and the user does not seem to notice anything, which is run a second instance.

Pending commands

In a real project, you will not use one set of commands. Yes, they will be inherited from an interface, for example ICommand, but there is only one method, so as not to contaminate all the other possible commands.

public interface ICommand {
    void Execute();
}

In this case we consider a principled approach when there is a command with parameters and commands without parameters. In this case, it does not matter how many parameters will be in the command.

public class Command : ICommand {
     public Action Action { get; set; }

     public void Execute() {
         if (Action == null) return;

         Action();
    }
}

public class Command<T> : ICommand {
     public Action<T> Action { get; set; }
     public object ParameterT { get; set; }

     public void Execute() {
         if (Action == null) return;

         Action((T)ParameterT);
     }
}

So far you could not see any problem with the use of commands. Command with parameters made in a generic style, because commands produce each type would not be rational, and simply lazy. A laziness – that’s what should be a programmer to develop!
In a simple command is invoked there is no problem because of the signature you call correctly and without problems. But we have the program starts with initial parameters, they are parsed at the beginning of work and in the form of specific types of data to be transmitted to future performance.

There can be debate, it is easier to store settings in a class or service as long as needed in a particular place, but we believe that there is no such possibility.

Since the beginning of the program no commands are not yet registered, you have to put them in a queue for execution (the order must be preserved). And here begins the most interesting.

CommandManager not parameterized types, but the command looks like

public void Execute<T>(CommandName commandName, Type type, T parameter) {
     var key = CreateKey(commandName, type);
     if (!commands.ContainsKey(key)) return;

     var command = ((Command<T>) commands[key]);
     command.ParameterT = parameter;
     command.Execute();
}

You should clearly specify the type parameter, or it is computed automatically. We select the inner class for pending commands, there must be all the values for this command, so, name, type, and, if necessary, the parameter(s).

public class CommandManager : ICommandManager {
     private class DelayedCommaind<T> {
         public Tuple<CommandName, Type> Key { get; set; }
         public T Parameter { get; set; }
     }

     ...

}

If we make the parameter type as object, there will be no match for the target commands. On the other hand you can not declare a variable of typeDelayedCommaind

public class CommandManager : ICommandManager {
     ...
     private readonly Queue< DelayedCommaind<T> > delayed = new Queue< DelayedCommaind<T> >();
     ...
}

as the class itself is not parameterized and that for T we mean the compiler is not clear. On the way out we will have a new language feature 4.0 – dynamic!

public class CommandManager : ICommandManager {
     private readonly Queue<dynamic> delayed = new Queue<dynamic>();
     private void Execute<T>(DelayedCommaind<T> delayed) {
     if (!commands.ContainsKey(delayed.Key)) return;

     if (delayed.Parameter.GetType() == typeof (NullParameter)) {
         var command = ((Command) commands[delayed.Key]);
         command.Execute();
     }
     else {
         var command = ((Command<T>) commands[delayed.Key]);
         command.ParameterT = delayed.Parameter;
         command.Execute();
    }
}

Actually it turns out that the commands works as proxies, so, for each action does not create its own command and called ready, but with different parameters. I like this approach more than one time create commands and to pass parameters to the constructor (if any).

WCF сервис

Since the server and client are working with a class assembly, it will not autogenereticproxies classes and not have to bother with the data types. Here, too, was not withoutnuance. The parameters of the secondary to the primary instance of the applicationpassed by a class StartParameter. And the interface service method looks like this:

[ServiceContract]
public interface ISingleInstanceService {
    [OperationContract]
    void PerformRemoteActions(List<StartParameter> commands);
}

Class StartParameter, as you might imagine, also must somehow transmit meta-information about the command parameter, and for this type of object is not very suitable. Dynamic type again will help.

[DataContract]
public class StartParameter {
    [DataMember] public CommandName CommandName;
    [DataMember] public dynamic Parameter;
    [DataMember] public bool HasParameter = true;
}

The main thing that a variable passed Parameter serialized data type.

Again there is scope for debate whether this method is limited, or create a bunch of methods according to needs. Or transfer all simple string, and parse the arguments run on the server side.

Commands registry

There really is nothing special, just want to mention the bonuses that get in Win7.
The main bonus – it is certainly quick access to documents is built automatically.

And from the context menu after registration of extension, you can get

public RegisterExtensions() {
    const string extension = ".ntpad";

    var location = Assembly.GetEntryAssembly().Location;
    var builder = new FileAssociateInfoBuilder("Notepad--", location)
         .AddCommand("Open", "-o", PassSelectedFile.ToApplication);
         .AddCommand("OpenExclusivelly", "-ox", PassSelectedFile.ToApplication);
         .AddExtension(extension);

    var service = new FileAssociateService();
    //service.DeleteAssociation(extension);
    if (!service.Exists(extension))
        service.CreateAssociation(builder.Result());
}

Outro

That’s all what I wanted to tell you further.
I hope it was not too messy and all turned out was similar to what I’ve described earlier.
In order to test the program, create a text file and change it the extension to the one shown in the program. Than you can just open them by double-clicking or by right-clicking and watch what happens.

Source Code

Hard’n’heavy

Tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>