Introduction

By default, the graphical user interface is compatible with the type of application we develop (also called project type in some situation). For example, a pure console application returns information using the console Graphical user interfaces, while windows forms application uses build in forms as a GUI, a web application uses the browser as GUI, and a flash application uses the flash GUI, etc. How can we put all in one application?!?

A better question, that developers have to understand is, what the hell are the differences between different applications types? What is making one application a "console application" and second application a "windows forms" application? Does it make sense that a developer knows how to develop only one specific type, while he can't understand basic code in different type? In short, the answer for the last question is NO.

It is true that a developer can gain expertise in a certain type of applications or GUI, and that with time, he gains experience and he controls the built-in options and tools, for this type. But if he understands what is going on behind the scenes, then he can also develop and change the behavior of the application, and actually create "new" application type.

So what is the difference between project types in Visual Studio (VS)? What is the difference between creating a console project in Visual Studio versus creating windows forms project? The answer is simple, VS provides several ready-made templates, for different application types, in order to save the developer working time. The only substantive difference between different types of applications is the template which we use. Those templates come with build in code, which someone else wrote for us. This code can be found in the application itself, in references, or another source connected to the application. Even if we are not familiar with the template/type of application, we have to understand the significance of choosing a specific type of project or a project template, when we start to work. Moreover. We must understand the code that comes with that template.

How often did you see "professionals" saying something like: "This cannot be done using project type A"? Let's change this misconception!

Theoretically we can start with notepad and develop our application from scratch without using a build-in template! But even in that case we will actually use some build-in code which came with the developing framework (Dot.Net for example). Moreover we can start a project type A, and add to that project elements which usually come with project type B. For example, start a new console application, and open a windows form or a browser, and vice versa.

Our Case study

We need to develop small application, which can be executed in variety of ways, on variety of interfaces. For example, we need to be able to execute the application using: (1) command line (with or without parameters), or by (2) double click the exe file, or (3) from command shell / PowerShell. Moreover, we need our application to return information, using variety of Graphical user interfaces (GUI), according the execution option that we used. For example, if we executed the application using GUI (option 3), then we need to let the user choose which GUI to use according to the parameter he passed to the executing command. If he didn't use any parameter (default), or if he used 'gui' as the parameter's value (option 1, 2), then we open a 'windows form', as our GUI. If the user pass the parameter 'console' and he using a shell interface, than the result will be seen on current shell without opening new GUI (option 2).

The Solution

In our case we want to get an application which uses both console GUI and windows form GUI. Since windows forms is a bit more complex template than console app template it looks easier to base our solution on windows forms application.

Step 1: Start a new project

1.1 Choose type: windows forms (language C#). 

1.2 Name the project ArielyMultiTypesApplication

Step 2: Import unmanaged methods

2.1 Open Program.cs file, which include the Windows forms application entry point.

* In most popular computer systems, a computer program usually has a single entry point. Using Windows forms it is a static method named Main.

2.2 In order to use PInvoke we need to add the use of System.Runtime.InteropServices.

* PInvoke: Platform Invocation Services allows managed code to call unmanaged functions that are implemented in a DLL.

using System.Runtime.InteropServices;

2.3. Import several methods to the "Program" class

[DllImport("kernel32", SetLastError = true)]
private static extern bool AttachConsole(int dwProcessId);
  
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
  
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
  
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
  
[DllImport("kernel32.dll", EntryPoint = "AllocConsole", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern int AllocConsole();


Step 3: Check which type of GUI should be started, and use it

Basically we read the argument posted to the execution command and make several checks using IF/ELSE IF/ELSE in order to choose the GUI.

3.1 Add optionally passing arguments to the Main method by using

static void Main(string[] args)

3.2 Add the basic if/else statements to the Main method

string mode = args.Length > 0 ? args[0] : "gui";
if (mode == "gui") {
    // Open windows form as our GUI
}
else if (mode == "console"){
    if (process.ProcessName == "cmd" || process.ProcessName == "powershell") {
        // Use Current shell as our GUI
    }
    else {
        //Open New console shell as our GUI.
    }
}

How do we open the GUI interface?

As we can see in the image above, we have three options. (1) Open a Windows form, (2) Open a new console shell, (3) Use current shell.

3.3 move the original code into the right place in our new code.

In order to open the Windows form we just use the original code from the "main" method, as this is a windows forms application, we already have the code ready for us in the template.

3.4 For options 2 & 3 we first of all declare several elements: (a) IntPtr element using the imported method GetForegroundWindow, (b) INT element which will get the thread ID. Next import the method GetWindowThreadProcessId, in order to get the thread ID, and declare a Process element which is our process.

IntPtr ptr = GetForegroundWindow();
int u;
GetWindowThreadProcessId(ptr, out u);
Process process = Process.GetProcessById(u);

Once we have our process as an element, we can check if we are using a command line process or a shell process, in order to determine if we need to open a new console shell or use current shell.

3.5 In order to use current shell we attach current process to the console using the imported method AttachConsole. When using this method the console open within our shell, and wait for the user to press a key (just like we use a command "console.readkey()"). Once the sendkey event is catch the application go back to the main shell. In this case we want to go back to the main shell automatically, therefore we send an "enter" key: 

SendKeys.SendWait("{ENTER}");

3.6 In order to open a new console shell we are using the imported method AllocConsole, which allocates a new console for the calling process. A console shell is closing by default once the commands ended. In this case we want the user to be able to read the information and leave the console open. Therefore we use the command Console.ReadLine().

Resources and more information

Code

Forum Questions


Resources