I have this simple piece of code in C# with GTK#:
Main.cs:
using System;
using Gtk;
namespace Apu{
class MainClass{
public static void Main(string[] args){
Application.Init();
new ShowForm();
Application.Run();
}
}
}
ShowForm.cs
public partial class ShowForm: Gtk.Window{
public ShowForm(): base(Gtk.WindowType.Toplevel){
MessageDialog md = new MessageDialog(
this,
DialogFlags.DestroyWithParent,
MessageType.Error,
ButtonsType.None,
"Test"
);
md.SetPosition(Gtk.WindowPosition.CenterAlways);
md.Title = "Test window";
md.AddButton("Don't stop", ResponseType.Ok);
md.AddButton("Stop", ResponseType.Cancel);
ResponseType result = (ResponseType)md.Run();
if (result.Equals(ResponseType.Cancel)) {
Console.WriteLine("Quit!");
md.DestroyEvent += delegate {
Application.Quit();
};
/*md.DeleteEvent += delegate {
Application.Quit();
};*/
}
md.Destroy();
}
}
Console outputs Quit!, but the program doesn't quit. Neither DestroyEvent nor DeleteEvent works. Can anyone explain why? This is my first app in c#, my first time using gtk#. I use monodevelop as my IDE.
EDIT
Application.Exit() gives error: Gtk.Application does not contain a definition for 'Exit'.
If you wish to close the process, try Environment.Exit(0)
Try:
md.Destroyed += delegate {
Application.Quit();
};
Related
iam quite desperate here. I couldn't find any example code for this in C#.
I want to rename BrowserSubProcess.exe and i want it to embed my main exe, if possible.
I am aware of this solution;
https://github.com/cefsharp/CefSharp/issues/1149#issuecomment-225547869
Rename CefSharp.BrowserSubprocess winforms
but i couldn't implemented it. I need sample program or code to understand. I hope #amaitland will see this and helps me.
I embed the BrowserSubProcess Program.cs to my Program.cs so it is embedded now.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (args.Count() < 5)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new LoginForm());
}
else
{
MyBrowserSubProcess(args);
}
}
static int MyBrowserSubProcess(string[] args)
{
Debug.WriteLine("BrowserSubprocess starting up with command line: " + String.Join("\n", args));
SubProcess.EnableHighDPISupport();
int result;
var type = args.GetArgumentValue(CefSharpArguments.SubProcessTypeArgument);
var parentProcessId = -1;
// The Crashpad Handler doesn't have any HostProcessIdArgument, so we must not try to
// parse it lest we want an ArgumentNullException.
if (type != "crashpad-handler")
{
parentProcessId = int.Parse(args.GetArgumentValue(CefSharpArguments.HostProcessIdArgument));
if (args.HasArgument(CefSharpArguments.ExitIfParentProcessClosed))
{
Task.Factory.StartNew(() => AwaitParentProcessExit(parentProcessId), TaskCreationOptions.LongRunning);
}
}
// Use our custom subProcess provides features like EvaluateJavascript
if (type == "renderer")
{
var wcfEnabled = args.HasArgument(CefSharpArguments.WcfEnabledArgument);
var subProcess = wcfEnabled ? new WcfEnabledSubProcess(parentProcessId, args) : new SubProcess(args);
using (subProcess)
{
result = subProcess.Run();
}
}
else
{
result = SubProcess.ExecuteProcess();
}
Debug.WriteLine("BrowserSubprocess shutting down.");
return result;
}
private static async void AwaitParentProcessExit(int parentProcessId)
{
try
{
var parentProcess = Process.GetProcessById(parentProcessId);
parentProcess.WaitForExit();
}
catch (Exception e)
{
//main process probably died already
Debug.WriteLine(e);
}
await Task.Delay(1000); //wait a bit before exiting
Debug.WriteLine("BrowserSubprocess shutting down forcibly.");
Environment.Exit(0);
}
}
And my BrowserSubprocessPath is my main exe.
settings.BrowserSubprocessPath = System.AppDomain.CurrentDomain.FriendlyName;
I finally managed to rename this sub process! Haven't found any solution how to do it through the CefSharp API, but found my own worked solution.
So, In your code that uses CefSharp add one setting to the Cef Settings, before Cef.Initialize()
using CefSharp;
using CefSharp.Wpf;
using System.IO;
using System.Reflection;
using System.Windows;
public App()
{
var settings = new CefSettings
{
BrowserSubprocessPath = Path.Combine(GetAppPath(), $#"runtimes\win-x64\native{ GetAppName() }.exe")
};
Cef.InitializeAsync(settings);
}
private static string GetAppPath()
{
return new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName;
}
private static string GetAppName()
{
return Assembly.GetExecutingAssembly().GetName().Name;
}
After this go to the bin\Debug\net6.0-windows\runtimes\win-x64\native\ and rename CefSharp.BrowserSubprocess.exe to Name you want to use.
Done. Now it will use this file with custom name you need.
P.S. For the auto name set you can always use Post-Build event with command to rename the file after project built and set the name same as your assembly name. I use this approach for my needs.
I am new to using FLAUI and Automation Testing and would like to use it to test my system. At the moment I am using a Thread.Sleep() to wait till the application launches to then find the Login textbox. Is there a more efficient way to do this rather than using Thread.Sleep()?
At the moment i launch the application and use Thread.sleep(10000) to wait until the applicationis fully launched and that the logIn textbox is find-able before clicking on the control to input the password to enter the application. However I understand that Thread.Sleep is the worst way to tell the system to wait especially in automated tests. Could anyone offer any other things i could test out?
It is always the best to use Retry mechanism and wait until your main window loads and controls are visible. For example, after calling Application.Launch you can retry up to 30 seconds to find main window, and txtLogin in it:
Retry.WhileException(() =>
{
using (var automation = new UIA3Automation())
{
Window mainWindow = Application.GetMainWindow(automation, TimeSpan.FromSeconds(60));
Assert.IsNotNull(Mainwindow, "Main window is not found");
TextBox loginTextBox = mainWindow.FindFirstDescendant(x => x.ByAutomationId("txtLogin")).AsTextBox();
Assert.IsNotNull(loginTextBox, "txtLogin is not found");
}
}, TimeSpan.FromSeconds(30), null, true);
The question already has good answers, but I found another way to wait for any element (including main window) using the Retry class in FlaUI.Core.Tools.Retry class
[TestFixture]
public class SmokeTests
{
private Application _theApp;
private UIA3Automation _automation;
private Window _mainWindow;
private const int BigWaitTimeout = 3000;
private const int SmallWaitTimeout = 1000;
[SetUp]
public void Setup()
{
_theApp = FlaUI.Core.Application.Launch(new ProcessStartInfo("YOUR_APPLICATION.exe", "/quickStart"));
_automation = new UIA3Automation();
_mainWindow = _theApp.GetMainWindow(_automation);
}
[TearDown]
public void Teardown()
{
_automation?.Dispose();
_theApp?.Close();
}
[Test]
public void Foo()
{
// This will wait until the element is available, or timeout passed
var examplesWrapPanel = WaitForElement(() => _mainWindow.FindFirstDescendant(cf => cf.ByAutomationId("ExamplesWrapPanel")));
// This will wait for the child element or timeout
var exampleButton = WaitForElement(() => examplesWrapPanel?.FindFirstDescendant(cf => cf.ByAutomationId("Another Automation Id")).AsButton());
// Do something with your elements
exampleButton?.WaitUntilClickable();
exampleButton?.Invoke();
}
private T WaitForElement<T>(Func<T> getter)
{
var retry = Retry.WhileNull<T>(
() => getter(),
TimeSpan.FromMilliseconds(BigWaitTimeout));
if (!retry.Success)
{
Assert.Fail("Failed to get an element within a wait timeout");
}
return retry.Result;
}
}
}
private void RunProc()
{
Process.Start("exeName");
}
public async Task StartProcessAsync()
{
var result= await Task.Run(()=>RunProc());
//optional
Task.Delay(new TimeSpan.FromSeconds(5));
}
Did you try this solution?
public static void LaunchApplication(string exePath, string arguments, bool waitForExit, bool waitForStart, int waitForStartTimeout)
{
ProcessStartInfo thisProcessInfo = new ProcessStartInfo();
thisProcessInfo.CreateNoWindow = true;
thisProcessInfo.UseShellExecute = false;
thisProcessInfo.RedirectStandardOutput = false;
thisProcessInfo.FileName = exePath;
thisProcessInfo.Arguments = arguments;
using(Process thisProcess = Process.Start(thisProcessInfo))
{
if(waitForStart)
thisProcess.WaitForInputIdle(waitForStartTimeout);
if(waitForExit)
thisProcess.WaitForExit();
}
}
So, I have strange question (maybe so stupid), but...
So, my task.
I have same class which gives me same functionality. So, in the main program, which I realize (yes, it's client-server app)) , I want to dynamically create ".exe wrapper" for this class - simplest code like this:
class Program
{
private SameClass mySameClass;
static void Main(string[] args)
{
mySameClass = new mySameClass(args);
Console.Readline();
}
}
In general, I want to create main app which creates slaves in the independent proccesses via dynamically code generation.
So, how to make it and control it?
Thank you.
So SameClass is supposed to contain the same functions and functionality as the process you want to have run multiple times... I guess what you need are Threads.
Not too sure what you mean by dynamically but let's say you have an event that triggers whenever you need a new SameClass process. Just run
SameClass newClone = new SameClass(args);
Trhead _thread = new Thread(new ThreadStart(newClone.Start));
_trhead.Start();
You can probably do this a little more elegant and refactor within SameClass but it's pretty hard to understand your question, so I guess this is the best I can do you hopefully answer your question.
Found solution based on CodeDom. Yes, It doesn't need reflection, sorry.
Code example:
using System;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Diagnostics;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var compiler = new CSharpCodeProvider();
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.CompilerOptions = "/platform:x64";
parameters.GenerateExecutable = true;
CompilerResults results = compiler.CompileAssemblyFromSource(parameters,
#"using System;
class Program {
public static void Main(string[] args) {
Console.WriteLine(""Hello world!"");
Console.ReadLine();
}
}");
var testProcess = new Process();
testProcess.StartInfo.FileName = results.CompiledAssembly.CodeBase;
testProcess.Start();
Console.WriteLine("I've run slave!");
Console.ReadLine();
}
}
}
So there's a program i saw, coded in c#. I keep getting errors on it. System.IndexOutOfRangeException is the main one, its happening at "args[0]". This is the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Skype4COMUserProfile
{
class Program
{
private static SKYPE4COMLib.Skype skype = new SKYPE4COMLib.Skype();
[STAThread]
static void Main(string[] args)
{
if (!skype.Client.IsRunning)
{
Environment.Exit(1);
}
skype.Client.OpenUserInfoDialog(args[0]);
}
}
}
I will be very grateful if someone could tell me how to fix this. thank you in advance!
Well that will fail if args is empty. Presumably you're meant to start the program by specifying a user name, or something like that.
You could always check for that:
if (args.Length == 0)
{
// Show an error dialog here
return;
}
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public static class Program
{
[STAThread]
public static void Main()
{
using (var browser = new WebBrowser())
{
browser.Navigate(string.Empty);
browser.Document.InvokeScript("execScript", new object[] { "function set_obj(obj) { window.obj = obj }" });
browser.Document.InvokeScript("execScript", new object[] { "function say_hello() { window.obj.WriteLine('Hello world') }" });
browser.Document.InvokeScript("set_obj", new object[] { new Obj() });
browser.Document.InvokeScript("say_hello");
browser.Document.InvokeScript("setTimeout", new object[] { "say_hello()", 100 });
Console.ReadKey();
}
}
}
[ComVisible(true)]
public sealed class Obj
{
public void WriteLine(string message)
{
Console.WriteLine(message);
}
}
An immediate invocation of the method say_hello works fine, but when I postpone it using setTimeout, it is not invoked. Why? Is there any workaround?
As user #controlflow pointed, I need a message loop in my application to make setTimeout work. Adding the following line helps:
Application.Run(new Form { Controls = { browser }, WindowState = FormWindowState.Minimized, ShowInTaskbar = false });
Don't put the parentheses after say_hello, because you're not trying to call it there, but pass it as a delegate to a function. So try:
browser.Document.InvokeScript("setTimeout", new object[] { "say_hello", 100 });
Also, are there any errors in the console?
Update:
Try:
browser.Document.InvokeScript("setTimeout(say_hello, 100);");
Also try:
browser.Document.InvokeScript("setTimeout", new object[] { "say_hello", "100" });
Whatever the issue is, there's probably a JavaScript error being swallowed somewhere. Try to write out the rendered markup and script and run it in a normal web page in browser.
You should change the following line
browser.Document.InvokeScript("say_hello");
to
browser.Document.InvokeScript("say_hello()");
It throws a javascript exception, and probably it's the reason for the next command not to execute.