Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restarting an application in the same process doesn't work on Windows #339

Closed
TylerLeonhardt opened this issue Mar 4, 2020 · 32 comments
Closed

Comments

@TylerLeonhardt
Copy link

Take a look at my code here:

https://github.com/TylerLeonhardt/GraphicalTools/blob/4c06220400d4f6b64f3231528905753912db6cad/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs#L23-L88

It's not too complex it mostly does:

            Application.Init();
            var top = Application.Top;
            top.RemoveAll();

            // Creates the top-level window to show
            var win = new Window(applicationData.Title ?? "Out-ConsoleGridView")
            {
                X = 0,
                Y = 1, // Leave one row for the toplevel menu
                // By using Dim.Fill(), it will automatically resize without manual intervention
                Width = Dim.Fill(),
                Height = Dim.Fill()
            };
            top.Add(win);

            // Creates a menubar, the item "New" has a help menu.
            var menu = new MenuBar(new MenuBarItem []
            {
                new MenuBarItem("_Actions (F9)", 
                    applicationData.PassThru
                    ? new MenuItem []
                    {
                        new MenuItem("_Accept", "", () => { if (Quit("Accept", ACCEPT_TEXT)) top.Running = false; }),
                        new MenuItem("_Cancel", "", () =>{ if (Quit("Cancel", CANCEL_TEXT)) _cancelled = true; top.Running = false; })
                    }
                    : new MenuItem []
                    {
                        new MenuItem("_Close", "", () =>{ if (Quit("Close", CLOSE_TEXT)) top.Running = false; })
                    })
            });
            top.Add(menu);
            // add more stuff to win...

            Application.Run();

This code is run within PowerShell (since you can write PowerShell cmdlets in C#)... so it's code that gets run in the same .NET process over and over again.

On macOS and Linux, this code works as expected. I can launch it (and quit it) over and over again and it shows the display over and over again.

On Windows, the first time works, but the second time it hangs without any ability to kill what's running:

image

Any ideas what might be happening here?

@BDisp
Copy link
Collaborator

BDisp commented Mar 5, 2020

Hi, I couldn't debug your repository. I think the top.RemoveAll() is causing the hanging and not only. The code top.Running = false causes program to terminate. I've made a little example that runs all the time is needed and only terminate when close the initial top.

class Program {
	static void Main ()
	{
		Application.Init ();

		var top = Application.Top;

		var win = new Window (null) {
			X = 0,
			Y = 0,
			Width = Dim.Fill (),
			Height = Dim.Fill ()
		};

		var button = new Button (5, 5, "Run App");
		button.Clicked = () => {
			ApplicationData applicationData = new ApplicationData () {
				Title = "Application-Data",
				PassThru = true
			};

			ConsoleGui consoleGui = new ConsoleGui ();
			consoleGui.Start (applicationData, top);
		};
		win.Add (button);

		top.Add (win);

		Application.Run ();
	}

	public class ApplicationData {
		public string Title { get; set; }
		public bool PassThru { get; set; }
	}

	internal class ConsoleGui {
		private const string ACCEPT_TEXT = "Are you sure you want to select\nthese items to send down the pipeline?";
		private const string CANCEL_TEXT = "Are you sure you want to cancel?\nNothing will be emitted to the pipeline.";
		private const string CLOSE_TEXT = "Are you sure you want to close?";

		private bool _cancelled;

		public void Start (ApplicationData applicationData, Toplevel top)
		{
			var tframe = top.Frame;
			var ntop = new Toplevel (tframe);

			// Creates the top-level window to show
			var win = new Window (applicationData.Title ?? "Out-ConsoleGridView") {
				X = 0,
				Y = 1, // Leave one row for the toplevel menu
					   // By using Dim.Fill(), it will automatically resize without manual intervention
				Width = Dim.Fill (),
				Height = Dim.Fill ()
			};
			ntop.Add (win);

			// Creates a menubar, the item "New" has a help menu.
			var menu = new MenuBar (new MenuBarItem []
			{
				new MenuBarItem("_Actions (F9)",
					applicationData.PassThru
					? new MenuItem []
					{
					new MenuItem("_Accept", "", () => { if (Quit("Accept", ACCEPT_TEXT)) Application.RequestStop(); }),
					new MenuItem("_Cancel", "", () =>{ if (Quit("Cancel", CANCEL_TEXT)) _cancelled = true; Application.RequestStop(); })
					}
					: new MenuItem []
					{
					new MenuItem("_Close", "", () =>{ if (Quit("Close", CLOSE_TEXT)) Application.RequestStop(); })
					})
			});

			ntop.Add (menu);
			// add more stuff to win...

			Application.Run (ntop);
		}
	}
	private static bool Quit (string v, string aCCEPT_TEXT)
	{
		MessageBox.Query (50, 7, v, aCCEPT_TEXT, "Ok");
		return true;
	}
}

@TylerLeonhardt
Copy link
Author

@BDisp I made similar changes to what you suggested in this commit.

However, the issue still persists sadly.

I did update my docs in my branch on how to get started if you don't mind giving it a try:
https://github.com/TylerLeonhardt/GraphicalTools/tree/fix-windows-relaunch-issue#development

I think the difference is... I completely kill the gui (going back to a PowerShell prompt) and try to reshow a new gui.

Maybe while you try to get my code working, I'll try to get your code to not work 😅

@TylerLeonhardt
Copy link
Author

TylerLeonhardt commented Mar 6, 2020

I can repro. Here's my issue:

using System;
using Terminal.Gui;

namespace guiCSStaticRepro
{
    class Program
    {
        static void Main()
        {
            Application.Init();

            var top = Application.Top;

            while (true)
            {
                Console.WriteLine("type anything");
                Console.ReadKey();

                ApplicationData applicationData = new ApplicationData()
                {
                    Title = "Application-Data",
                    PassThru = true
                };

                ConsoleGui consoleGui = new ConsoleGui();
                consoleGui.Start(applicationData, top);
            }
        }

        public class ApplicationData
        {
            public string Title { get; set; }
            public bool PassThru { get; set; }
        }

        internal class ConsoleGui
        {
            private const string ACCEPT_TEXT = "Are you sure you want to select\nthese items to send down the pipeline?";
            private const string CANCEL_TEXT = "Are you sure you want to cancel?\nNothing will be emitted to the pipeline.";
            private const string CLOSE_TEXT = "Are you sure you want to close?";

            private bool _cancelled;

            public void Start(ApplicationData applicationData, Toplevel top)
            {
                var tframe = top.Frame;
                var ntop = new Toplevel(tframe);

                // Creates the top-level window to show
                var win = new Window(applicationData.Title ?? "Out-ConsoleGridView")
                {
                    X = 0,
                    Y = 1, // Leave one row for the toplevel menu
                           // By using Dim.Fill(), it will automatically resize without manual intervention
                    Width = Dim.Fill(),
                    Height = Dim.Fill()
                };
                ntop.Add(win);

                // Creates a menubar, the item "New" has a help menu.
                var menu = new MenuBar(new MenuBarItem[]
                {
                new MenuBarItem("_Actions (F9)",
                    applicationData.PassThru
                    ? new MenuItem []
                    {
                    new MenuItem("_Accept", "", () => { if (Quit("Accept", ACCEPT_TEXT)) Application.RequestStop(); }),
                    new MenuItem("_Cancel", "", () =>{ if (Quit("Cancel", CANCEL_TEXT)) _cancelled = true; Application.RequestStop(); })
                    }
                    : new MenuItem []
                    {
                    new MenuItem("_Close", "", () =>{ if (Quit("Close", CLOSE_TEXT)) Application.RequestStop(); })
                    })
                });

                ntop.Add(menu);
                // add more stuff to win...

                Application.Run(ntop);
            }
        }
        private static bool Quit(string v, string aCCEPT_TEXT)
        {
            MessageBox.Query(50, 7, v, aCCEPT_TEXT, "Ok");
            return true;
        }
    }
}

It works the first time... but then the second time it doesn't.

@BDisp
Copy link
Collaborator

BDisp commented Mar 6, 2020

It's working all the time without exit with .net461. However, it's not working with netcore2.0. But you must use Application.Run() in your main and use another way to make your loop working inside that running. However, a toplevel handler or window handler that could be trigger whenever they are activated, would be a good place to put your loop.

@TylerLeonhardt
Copy link
Author

It's working all the time without exit with .net461. However, it's not working with netcore2.0.

Unfortunately, I need it to work in .NET Core :(

But you must use Application.Run() in your main

But Application.Run() triggers gui.cs to take over right? I don't want gui.cs to take over until I've run Start. Then I wanna be able to quit gui.cs altogether, and then relaunch it using Start in the same process.

@SteveL-MSFT
Copy link

If it works in net461, but not netcore20, then we may need to get the dotnet folks involved.

@BDisp
Copy link
Collaborator

BDisp commented Mar 7, 2020

After all, it doesn't work with .net461 either. When I tested with netcore2.0 I used Terminal.Gui obtained through NuGet and not through the source project used when I tested with .net461. The reason it works well when testing with .net461 is due to the fact that there is already a Pull Request #297 at 2fecd50 on my master that solves this behavior with the addition of the CheckLayoutNeeded method in the Core file. By the way, if you can please test it through my fork at https://github.com/BDisp/gui.cs.
I modified the code so that gui.cs does not take over after returning from Application.Run(), but it only works with my repository. For NuGet you have to wait for any eventual update.

class Program {
	static void Main ()
	{
		while (true) {
			Console.WriteLine ("type anything");
			Console.ReadKey ();

			ApplicationData applicationData = new ApplicationData () {
				Title = "Application-Data",
				PassThru = true
			};

			ConsoleGui consoleGui = new ConsoleGui ();
			consoleGui.Start (applicationData);
			Console.Clear ();
		}
	}

	public class ApplicationData {
		public string Title { get; set; }
		public bool PassThru { get; set; }
	}

	internal class ConsoleGui {
		private const string ACCEPT_TEXT = "Are you sure you want to select\nthese items to send down the pipeline?";
		private const string CANCEL_TEXT = "Are you sure you want to cancel?\nNothing will be emitted to the pipeline.";
		private const string CLOSE_TEXT = "Are you sure you want to close?";

		private bool _cancelled;

		public void Start (ApplicationData applicationData)
		{
			Application.Init ();

			var top = Application.Top;

			// Creates the top-level window to show
			var win = new Window (applicationData.Title ?? "Out-ConsoleGridView") {
				X = 0,
				Y = 1, // Leave one row for the toplevel menu
				       // By using Dim.Fill(), it will automatically resize without manual intervention
				Width = Dim.Fill (),
				Height = Dim.Fill ()
			};
			top.Add (win);

			// Creates a menubar, the item "New" has a help menu.
			var menu = new MenuBar (new MenuBarItem []
			{
					new MenuBarItem("_Actions (F9)",
					    applicationData.PassThru
					    ? new MenuItem []
					    {
					    new MenuItem("_Accept", "", () => { if (Quit("Accept", ACCEPT_TEXT)) Application.RequestStop(); }),
					    new MenuItem("_Cancel", "", () =>{ if (Quit("Cancel", CANCEL_TEXT)) _cancelled = true; Application.RequestStop(); })
					    }
					    : new MenuItem []
					    {
					    new MenuItem("_Close", "", () =>{ if (Quit("Close", CLOSE_TEXT)) Application.RequestStop(); })
					    })
			});

			top.Add (menu);
			// add more stuff to win...

			Application.Run ();
		}
	}

	private static bool Quit (string v, string aCCEPT_TEXT)
	{
		MessageBox.Query (50, 7, v, aCCEPT_TEXT, "Ok");
		return true;
	}
}

@BDisp
Copy link
Collaborator

BDisp commented Mar 7, 2020

@SteveL-MSFT It does not works in net461 either. See my explanation above, please. Sorry for the mistake.

@TylerLeonhardt
Copy link
Author

Thanks BDisp! I'll give it a try

@TylerLeonhardt
Copy link
Author

TylerLeonhardt commented Mar 7, 2020

@BDisp That fixes the issue!! (See #339 (comment)) Thanks for the pointer. I'll ping @migueldeicaza on twitter to see if he could look at that PR.

That would enable Out-ConsoleGridView on Windows :)

btw here's the blog post:
https://devblogs.microsoft.com/powershell/introducing-consoleguitools-preview/

@TylerLeonhardt
Copy link
Author

If you're on Twitter, I would love to give you a shout out for all your help!

@BDisp
Copy link
Collaborator

BDisp commented Mar 7, 2020

Thank you. Yes I am in Twitter @BDisp. By the way, could you check this strange behavior with Windows Terminal #332, please?

@TylerLeonhardt
Copy link
Author

TylerLeonhardt commented Mar 7, 2020

You know what... I messed up. @BDisp your master branch fixes the problem... but redraw-when-setting-coordinates branch does not... (in fact it also seems to cause dialogs to not receive ENTER)

On Windows in conhost not the new Windows Terminal

@TylerLeonhardt
Copy link
Author

could you check this strange behavior with Windows Terminal #332, please?

Just duped an issue to that one about mouse not working.

@BDisp
Copy link
Collaborator

BDisp commented Mar 7, 2020

Did you notice the window drawing behavior too, with spaces in vertical limits?

@TylerLeonhardt
Copy link
Author

Yeah very strange... you might need to engage folks in Windows Terminal - https://github.com/Microsoft/Terminal

@TylerLeonhardt
Copy link
Author

TylerLeonhardt commented Mar 7, 2020

You know what... I messed up. @BDisp your master branch fixes the problem... but redraw-when-setting-coordinates branch does not... (in fact it also seems to cause dialogs to not receive ENTER)

On Windows in conhost not the new Windows Terminal

but anyway back to this issue... yeah I think it's odd that your master worked for me (in regular conhost PowerShell I was able to run Out-ConsoleGridView over and over no problem)

(side note, your master branch seems to also fix PowerShell/ConsoleGuiTools#49 which is odd to me)

@TylerLeonhardt
Copy link
Author

Your example in this comment #339 (comment)

Doesn't seem to work with the output of your redraw-when-setting-coordinates branch. Can you confirm?

csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="NStack.Core" Version="0.13.0" />
    <PackageReference Include="Terminal.Gui" Version="0.25.0" />
  </ItemGroup>

</Project>

@BDisp
Copy link
Collaborator

BDisp commented Mar 7, 2020

Ok. I already figured out. It's the button-repaint branch that solve that behavior

@TylerLeonhardt
Copy link
Author

Are you sure? I just tried with that branch your code from #339 (comment)
with my csproj from #339 (comment) and still see the bad behavior.

@BDisp
Copy link
Collaborator

BDisp commented Mar 7, 2020

You must use <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" /> instead <PackageReference Include="Terminal.Gui" Version="0.25.0" /> because the NuGet package does not have that solution yet.

@TylerLeonhardt
Copy link
Author

I packed your changes (after changing the version to 0.27.0):

dotnet pack -o C:\Users\tyler\Downloads\nuget Terminal.sln

and then pulled it in via:

<PackageReference Include="Terminal.Gui" Version="0.27.0" />

while adding C:\Users\tyler\Downloads\nuget to my NuGet.config.

It should be the same thing as referencing <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />

@BDisp
Copy link
Collaborator

BDisp commented Mar 7, 2020

Since it's working with my master perhaps it's had to do with some update needed to do for some my pr, after the updates in the upstream/master.

@TylerLeonhardt
Copy link
Author

"This branch (@BDisp's master) is 116 commits ahead, 16 commits behind migueldeicaza:master."

That's quite a difference!

@BDisp
Copy link
Collaborator

BDisp commented Mar 7, 2020

Many of them are not related to the "Terminal.Gui" project but to the related example projects. So, any advice are welcome.

@TylerLeonhardt
Copy link
Author

Gotcha... it'd be nice if we could narrow down the changes that you've added to "Terminal.Gui"

@BDisp
Copy link
Collaborator

BDisp commented Mar 9, 2020

I am experiencing a strange problem with this situation. I created a new branch based on the upstream/master and I can always restart the application in the same process, so miguel's repository is prepared to correct this behavior. Repack the Terminal using upstream/master instead of mine and check. If you manage to run successfully, then the problem may be related to an incorrect version of the package that may have been packed with some source code problem other than the one that is currently available. For example, he may have changed the repository after creating the package. If I said blunder I apologize.

@TylerLeonhardt
Copy link
Author

Let me give it a try!

@TylerLeonhardt
Copy link
Author

Wow you're totally right. It does work!

@TylerLeonhardt
Copy link
Author

image

I guess there are a few commits after the release!

@TylerLeonhardt
Copy link
Author

I guess we can close this 😅 and then wait for a new release.

cc @migueldeicaza

@BDisp
Copy link
Collaborator

BDisp commented Mar 9, 2020

Of course. I'm also preparing more pr's with some features too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants