Run a windows form while my console application still running - c#

I'm new at C# programming and i'm lost with a thing that could be simple.
Executing a console application, at a moment i need to call a Windows Form that will show statics of the execution but when i call the form1.ShowDialog(); this stop the console runtime.
How do i keep my console execution alive while i show a Windows form screen ?
class Program
{
static Form1 form = new Form1();
public static bool run = true;
static void Main(string[] args)
{
work();
}
public static void work()
{
form.Show();
while (run)
{
Console.WriteLine("Console still running");
}
}
}

try this it work on me
using System.Windows.Forms;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
public static bool run = true;
static void Main(string[] args)
{
Startthread();
Application.Run(new Form1());
Console.ReadLine();
}
private static void Startthread()
{
var thread = new Thread(() =>
{
while (run)
{
Console.WriteLine("console is running...");
Thread.Sleep(1000);
}
});
thread.Start();
}
}
}
Threading is like "process inside a process" in my own understanding.

See this question. You have to use Form1.Show() because Form1.ShowDialog() pauses execution until the form is closed.
Update This seems to be working (with Application.Run):-
public static Form1 form = new Form1();
public static bool run = true;
[MTAThread]
static void Main(string[] args)
{
new Thread(() => Application.Run(form)).Start();
new Thread(work).Start();
}
public static void work()
{
while (run)
{
Console.WriteLine("Console Running");
}
}

Related

Open WPF application from console and close console window

I've got an hybrid application with either console or WPF functionality. If the WPF application is started or something is done in console window depends on the arguments at start up. I were able to implement this (there are a lot of examples to find at stackoverflow). Now I want that, if the WPF application is started, that the console window will be closed. But this is shown and if I close it, the WPF application is also closed.
This is my current implementation.
using System;
using System.Windows;
namespace MyNamespace
{
class Program
{
[STAThread]
static void Main(string[] args)
{
string option = args[0];
switch (option)
{
case "WPF":
RunApplication();
break;
default:
DoSomething();
break;
}
}
private static void RunApplication()
{
Application app = new Application();
app.Run(new MainWindow());
Environment.Exit(0);
}
private static void DoSomething()
{
// …
}
}
}
If I try to start the application in a new Thread the application is directly closed and the WPF window will not be shown.
using System.Threading;
using System.Threading.Tasks;
private static void RunApplication()
{
new Thread(() => {
Application app = new Application();
app.Run(new MainWindow());
}).Start();
Environment.Exit(0);
}
I have no idea how I could implement this. Is there a possibility to do this?
I could find a solution. According of the accepted answer of this post Show/Hide the console window of a C# console application I hide the console window.
using System;
using System.Runtime.InteropServices;
using System.Windows;
namespace DeploymentPreparer
{
class Program
{
[STAThread]
static void Main(string[] args)
{
string option = args[0];
switch (option)
{
case "WPF":
RunApplication();
break;
default:
DoSomething();
break;
}
}
private static void RunApplication()
{
ShowWindow(GetConsoleWindow(), SW_HIDE);
Application app = new Application();
app.Run(new MainWindow());
}
private static void DoSomething()
{
// ...
}
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_HIDE = 0;
const int SW_SHOW = 5;
}
}
Now I have either the console or the WPF window. If the WPF window is shown the console window is hidden.
I tried the following method that seems to work:
Create a normal Console App. In case of "WPF" argument start the WPF application as a new process. In case of any other argument - call DoSomething()
Example:
using System;
using System.Diagnostics;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
string option = "";
if (args.Length > 0)
{
option = args[0];
}
switch (option)
{
case "WPF":
try
{
using (Process myProcess = new Process())
{
myProcess.StartInfo.UseShellExecute = false;
// Use correct path to the WPF Application
myProcess.StartInfo.FileName = #"C:\Users\Danny\Source\Repo\WpfApp\bin\Debug\WpfApp.exe";
myProcess.Start();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine("Press any key to continue ...");
Console.ReadKey();
}
break;
default:
DoSomething();
break;
}
}
private static void DoSomething()
{
// …
Console.WriteLine("Doing Something ...");
Console.WriteLine("Press any key to continue ...");
Console.ReadKey();
}
}
}

Mutex initialization inside a C# method always returns positive createdNew

I have created simple MutexManager:
public static class MutexManager
{
private static string mutexName
{
get
{
return "MyAppName" + System.Security.Principal.WindowsIdentity.GetCurrent().User.AccountDomainSid;
}
}
public static bool CreateApplicationMutex()
{
bool createdNew;
var mutex = new Mutex(false, mutexName, out createdNew);
return createdNew;
}
}
The problem is that CreateApplicationMutex always returns true on new application instance startup. As long as I had exactly same code in app.cs everything was correct, but after I moved it to MutexManager createdNew is always true. What am I doing wrong?
The following works as expected for me, and returns false on second instance
public static class MutexManager
{
private static string mutexName => "MyAppName" + System.Security.Principal.WindowsIdentity.GetCurrent()
.User?.AccountDomainSid;
public static bool CreateApplicationMutex()
{
new Mutex(false, mutexName, out var createdNew);
return createdNew;
}
}
private static void Main(string[] args)
{
Console.WriteLine(MutexManager.CreateApplicationMutex());
Console.ReadKey();
}
Output
true
false
Make sure you debug your app, and check the mutex name
Update
Winforms
MessageBox.Show(
MutexManager.CreateApplicationMutex()
.ToString());
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
WPF
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
MessageBox.Show(
MutexManager.CreateApplicationMutex()
.ToString());
base.OnStartup(e);
}
}
Once again it works as expected, and cant be reproduced
I had a similar problem, the reason was life time of mutex variable. Following code works fine by me in debug version, but always returns true for created_new in release version:
using System;
static class Program
{
[STAThread]
static void Main(string[] args)
{
string mutex_name = "MyApplicationName_SingleInstanceMutex";
bool created_new = false;
System.Threading.Mutex mutex = new System.Threading.Mutex(false, mutex_name, out created_new);
if (!created_new)
{
System.Windows.Forms.MessageBox.Show("Application is already started!");
return;
}
/* Create & run mainForm here */
}
}
Making mutex static solves the problem:
using System;
static class Program
{
static System.Threading.Mutex mutex = null;
[STAThread]
static void Main(string[] args)
{
string mutex_name = "MyApplicationName_SingleInstanceMutex";
bool created_new = false;
mutex = new System.Threading.Mutex(false, mutex_name, out created_new);
if (!created_new)
{
System.Windows.Forms.MessageBox.Show("Application is already started!");
return;
}
/* Create & run mainForm here */
}
}

Update a form after Application.Run()

Here is what i want to do
// pseudo code
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 myForm = new Form1();
Application.Run(myForm);
while(true)
{
string a = readline();
}
form1.show(a)
In other words , I need the form always show the input. but the code above will stop after 'Application.Run(myForm);'. The reason I don't write such code in the form1 class is the main part of code is run on a machine learning engine written in F#, and because F# doesn't have a good visual designer. So I am trying to create a simple form1.dll, and use it to plot the result over time.
So my problem is I only can initialise the form, but I can't update it over time.
Any hints will be appreciated.
You're trying to do 2 things at the same time, so your application should reflect that by using 2 threads. Next, the Form's Show() method does not accept a string, so you need to implement your own method.
Here's a C# 2.0 WinForms solution. The program runs the thread and processes the console input:
static class Program
{
[STAThread]
private static void Main()
{
// Run form in separate thread
var runner = new FormRunner();
var thread = new Thread(runner.Start) {IsBackground = false};
thread.Start();
// Process console input
while (true)
{
string a = Console.ReadLine();
runner.Display(a);
if (a.Equals("exit")) break;
}
runner.Stop();
}
}
The FormRunner takes care about thread invocation:
internal class FormRunner
{
internal Form1 form = new Form1();
internal void Start()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(form);
}
private delegate void StopDelegate();
public void Stop()
{
if (form.InvokeRequired)
{
form.Invoke(new StopDelegate(Stop));
return;
}
form.Close();
}
private delegate void DisplayDelegate(string s);
public void Display(string s)
{
if (form.InvokeRequired)
{
form.Invoke(new DisplayDelegate(form.Display), new[] {s});
}
}
}
And Form1 just needs something to display:
public void Display(string s)
{
textBox1.Multiline = true;
textBox1.Text += s;
textBox1.Text += Environment.NewLine;
}

Using Microsoft.VisualBasic.ApplicationServices to manage an application's single instance

I have managed to find the following code from StackOverflow:
using Microsoft.VisualBasic.ApplicationServices;
using System.Windows.Forms;
namespace ExciteEngine2.MainApplication {
public class SingleInstanceController: WindowsFormsApplicationBase {
public delegate Form CreateMainForm();
public delegate void StartNextInstanceDelegate(Form mainWindow);
private readonly CreateMainForm formCreation;
private readonly StartNextInstanceDelegate onStartNextInstance;
public SingleInstanceController() {
}
public SingleInstanceController(AuthenticationMode authenticationMode)
: base(authenticationMode) {
}
public SingleInstanceController(CreateMainForm formCreation, StartNextInstanceDelegate onStartNextInstance) {
// Set whether the application is single instance
this.formCreation = formCreation;
this.onStartNextInstance = onStartNextInstance;
IsSingleInstance = true;
StartupNextInstance += this_StartupNextInstance;
}
private void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e) {
if (onStartNextInstance != null) {
onStartNextInstance(MainForm);
// This code will be executed when the user tries to start the running program again,
// for example, by clicking on the exe file.
// This code can determine how to re-activate the existing main window of the running application.
}
}
protected override void OnCreateMainForm() {
// Instantiate your main application form
MainForm = formCreation();
}
//public void Run() {
// string[] commandLine = new string[0];
// base.Run(commandLine);
//}
protected override void OnRun() {
base.OnRun();
}
}
}
And I have this in my Program.cs:
private static Form CreateForm() {
return new AppMDIRibbon();
}
private static void OnStartNextInstance(Form mainWindow)
{
// When the user tries to restart the application again, the main window is activated again.
mainWindow.WindowState = FormWindowState.Maximized;
}
[STAThread]
static void Main(string[] args) {
SingleInstanceController ApplicationSingleInstanceController = new SingleInstanceController(CreateForm, OnStartNextInstance);
ApplicationSingleInstanceController.Run(args);
#region Application Logic
#endregion
}
Now, I have a lot of application logic that I need BEFORE the Run():
#region Application Logic
//Uninstall
foreach (string arg in args) {
if (arg.Split('=')[0] == "/u") {
ApplicationLogger.Info("Uninstallation command received.");
Process.Start(new ProcessStartInfo(Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\msiexec.exe", "/x " + arg.Split('=')[1]));
return;
}
}
SetupXPO();
SetupLogging();
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-GB");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += Application_ThreadException;
try {
ApplicationLogger.Info("Setting Telerik Theme: " + ConfigurationManager.AppSettings["ThemeToUse"]);
ThemeResolutionService.ApplicationThemeName = ConfigurationManager.AppSettings["ThemeToUse"];
}
catch (Exception ex) {
ApplicationLogger.Error("Exception while setting Telerik Theme.", ex);
ThemeResolutionService.ApplicationThemeName = "ControlDefault";
}
DevExpress.UserSkins.OfficeSkins.Register();
DevExpress.UserSkins.BonusSkins.Register();
DevExpress.Skins.SkinManager.EnableFormSkins();
DevExpress.Skins.SkinManager.EnableMdiFormSkins();
if (args.Contains("/dx")) {
Application.Run(new AppMDIRibbonDX());
ApplicationLogger.Info("Application (DX) started.");
}
else {
Application.Run(new AppMDIRibbon());
ApplicationLogger.Info("Application started.");
}
#endregion
How can I setup this logic? I'm using a commandline argument to actually start an alternate form. I'm using a commandline argument to cause an uninstallation and also calling some method to setup DB and logging. Similarly, I'm setting up culture and themes too. All this before the actual application run. Can anyone suggest?
If you simplify the Visual Basic-derived class you linked, you can just replace your current call to Application.Run(). This does depend on how you want to handle subsequent instances.
With the version below, just change you calls of: Application.Run(myForm) to SingleInstanceApplication.Run(myForm);
public sealed class SingleInstanceApplication : WindowsFormsApplicationBase
{
private static SingleInstanceApplication _application;
private SingleInstanceApplication()
{
base.IsSingleInstance = true;
}
public static void Run(Form form)
{
_application = new SingleInstanceApplication {MainForm = form};
_application.StartupNextInstance += NextInstanceHandler;
_application.Run(Environment.GetCommandLineArgs());
}
static void NextInstanceHandler(object sender, StartupNextInstanceEventArgs e)
{
// Do whatever you want to do when the user launches subsequent instances
// like when the user tries to restart the application again, the main window is activated again.
_application.MainWindow.WindowState = FormWindowState.Maximized;
}
}
Then your Main() method contains your "Application Logic"
[STAThread]
static void Main(string[] args) {
#region Application Logic
//Uninstall
foreach (string arg in args) {
if (arg.Split('=')[0] == "/u") {
ApplicationLogger.Info("Uninstallation command received.");
Process.Start(new ProcessStartInfo(Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\msiexec.exe", "/x " + arg.Split('=')[1]));
return;
}
}
SetupXPO();
SetupLogging();
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-GB");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += Application_ThreadException;
try {
ApplicationLogger.Info("Setting Telerik Theme: " + ConfigurationManager.AppSettings["ThemeToUse"]);
ThemeResolutionService.ApplicationThemeName = ConfigurationManager.AppSettings["ThemeToUse"];
}
catch (Exception ex) {
ApplicationLogger.Error("Exception while setting Telerik Theme.", ex);
ThemeResolutionService.ApplicationThemeName = "ControlDefault";
}
DevExpress.UserSkins.OfficeSkins.Register();
DevExpress.UserSkins.BonusSkins.Register();
DevExpress.Skins.SkinManager.EnableFormSkins();
DevExpress.Skins.SkinManager.EnableMdiFormSkins();
if (args.Contains("/dx")) {
SingleInstanceApplication.Run(new AppMDIRibbonDX());
ApplicationLogger.Info("Application (DX) started.");
}
else {
SingleInstanceApplication.Run(new AppMDIRibbon());
ApplicationLogger.Info("Application started.");
}
#endregion
}

Non-terminating / non-blocking Windows Mobile App

I have a console app that I want to run continually in the background. I thought that if I started it up and then told it to wait things would work. But when I have it wait, it freezes the application.
Here is my code:
class Program
{
static public ManualResetEvent StopMain;
static void Main(string[] args)
{
// Hide the cursor.
Cursor.Current = Cursors.Default;
StopMain = new ManualResetEvent(false);
RunHook runHook = new RunHook();
// wait until signalled by Program.StopMain.Set();
StopMain.WaitOne();
}
}
class RunHook
{
private HookKeys hook;
public RunHook()
{
hook = new HookKeys();
hook.HookEvent += EventForHook;
}
private void EventForHook(HookEventArgs e, KeyBoardInfo keyBoardInfo,
ref Boolean handled)
{
if ((keyBoardInfo.scanCode == 4) && (keyBoardInfo.vkCode == 114))
handled = true;
}
}
Any ideas on how to have this run in the background but never terminate?
The behavior you see is expected. You have one thread, and it's in a wait state. To get some form of activity, you have to let the scheduler actually do something. A background thread is one way to achieve this:
static void Main(string[] args)
{
StopMain = new ManualResetEvent(false);
bool exit = false;
new Thread(
delegate
{
new RunHook();
while(!exit) { Thread.Sleep(1); }
}
).Start();
StopMain.WaitOne();
exit = true;
}
Another is to just let the primary thread yield:
static void Main(string[] args)
{
StopMain = new ManualResetEvent(false);
RunHook runHook = new RunHook();
while(!StopMain.WaitOne())
{
Thread.Sleep(1);
}
}
There are certainly other ways, too. Personally I'd do neither of these. Instead I'd add a blocking method to the RunHook class and have it return when it was done or signalled.

Categories