Launch winform with webrowser url from separate application - c#

essentially what I want to do is I have one .exe .net program I made with a button in it. I want the click event in that button to launch my second .exe file, and change the WebBrowser URL in that second application. Is this possible?
The urls will be html pages on my computer.

So you need to share some data between 2 programs, one way to do it:
private void btnLaunchBrowser_Click(object sender, EventArgs e)
{
string yourExePath = "WhateverIsThePath.exe";
string url = "YourLocalUrlHere";
var browserWinformsApp = System.Diagnostics.Process.Start(yourExePath, url);
// Here you can control over the second Process, you can even
// Force it to Close by browserWinformsApp.Close();
}
In your Second App (Browser one), update Program.cs to accept parameters:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string passedUrl = args[0];
Application.Run(new Form1(passedUrl));
}
}
Finally, update your Form's constructor to accept a string url:
private string browserUrl;
public Form1(string url)
{
InitializeComponent();
browserUrl= url;
}

Related

C# WinForms crashes with no Exception

I have a C# WinForms application.
I'm using the Unity Container nuget package to inject my service classes. If I get the service class in the Program.cs and make a call to my web api to authenticate a username and password using odata, it works successfully. If I call Application.Run(myForm), where myForm is a Telerik RadForm, and asynchronously run the same call, the application closes with no exceptions thrown.
I am using Application.ThreadException and registering Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException) but this does not catch anything.
The line that is causing the application to crash is a call to System.Net.Http.HttpClient.PostAsync(). This call is made from within the service class located in a separate .Net Standard 2.0 Class Library. The main application is a .NET Framework 4.7.2 Windows Application and the UI controls are located in a .NET Framework 4.7.2 Class Library. Again, this call is successful if called outside of the Telerik RadForm.
From within the Telerik RadForm I have tried to execute the call using:
private async void btnLogin_Click(object sender, System.EventArgs e)
var t = new Thread(() => Runner.DoWork(new Credentials(u, p, CLIENT_ID)))
An asynchronous call to a static method without using await
I did get a different result when I called LoginAsync(...).Result on the service class. This caused the application to enter a thread lock state, but did not crash the app.
UPDATE:
There are two RadForms; a DashboardForm and a LoginForm. Program.cs launches the Dashboard which checks for an existing Bearer token. If the token does not exist, the Dashboard displays the Login using .ShowDialog() to prevent using the Dashboard until authenticated. The Login then uses the service class described above.
If instead of launching the Dashboard in Program.cs using Application.Run() I launch the Login, the service call to authenticate is successful.
I then tried launching the Login, from the Dashboard in a new thread, but this resulted in the same problem described above.
Why does the System.Net.Http.HttpClient.PostAsync() crash the application (with no exceptions) if it is called from a Form displayed by another Form?
Program
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
WinInjectionHelper.Register(UnityConfig.RegisterComponents);
Application.ThreadException += Application_ThreadException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.Run(new DashboardForm());
}
private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
MessageBox.Show("Big error...");
}
Dashboard
public partial class DashboardForm : Telerik.WinControls.UI.RadForm, IDashboard
{
private readonly IProductService _service;
public DashboardForm()
{
this._service = WinInjectionHelper.Generate2<IProductService>();
this.InitializeComponent();
}
private void DashboardForm_Load(object sender, EventArgs e)
{
if (this.NotAuthenticated())
{
var frm = new LoginForm();
if (frm.ShowDialog() != DialogResult.OK)
{
this.Close();
}
}
else
{
//TODO: display error to user
this.Close();
}
}
}
Login
public partial class LoginForm : Telerik.WinControls.UI.RadForm
{
private const string CLIENT_ID = "...";
private readonly IAuthenticationService _service;
public LoginForm()
{
this._service = WinInjectionHelper.Generate2<IAuthenticationService>();
this.InitializeComponent();
}
private void btnCancel_Click(object sender, System.EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
private async void btnLogin_Click(object sender, System.EventArgs e)
{
var u = this.txtUsername.Text.Trim();
var p = this.txtPassword.Text.Trim();
if (string.IsNullOrWhiteSpace(u))
{
MessageBox.Show("Username is required!", "NOTICE", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
if (string.IsNullOrWhiteSpace(p))
{
MessageBox.Show("Password is required!", "NOTICE", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
try
{
var jwt = await this._service.LoginAsync(new Credentials(u, p, CLIENT_ID));
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, u),
new Claim(ClaimTypes.Email, u),
new Claim(ClaimTypes2.AccessToken, jwt.AccessToken),
}, "JWT");
((GenericPrincipal)Thread.CurrentPrincipal).AddIdentity(identity);
this.DialogResult = DialogResult.OK;
this.Close();
}
catch
{
MessageBox.Show("An error occurred while processing your request.", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
You can check the Windows Event Viewer and look under application to find the crash logs. I had the same issue with an application I was working on and this was the only place that had any information on it. It actually helped me resolve the issue which was related to targeting a specific .NET Framework version. Click on the bottom left Window button and type in "event viewer" in there an then click on the icon that shows up.
Easiest way to do it is to run the application and have it crash and then immediately go to event viewer so it will be at the top of the list.
I resolved the issue by calling the Login form first and then displaying the Dashboard before closing the Login and changed program.cs so that closing the Login did not exit the application. This does require calling Application.Exit() when the application does need to close.
Program
internal static class Program
{
[STAThread]
private static void Main()
{
...
new LoginForm().Show();
Application.Run();
}
}

c#, Open a specific windows form from a parameter outside the executable

I am new to c# and .net app development. Currently I built small windows form project. This project has multiple '.cs' forms. I complied the project to an executable and call it from a .net application with the command below. Everything works fine.
System.Diagnostics.Process.Start("C:\Users\WEI\Desktop\Release\DepartmentManagement.exe");
I have a new requirement to open a specific form dynamically when the executable is run. I hard coded the form I want to open by default inside "Program.cs" like below. In this example below I hard coded it to open "ProductionSystem" form by default. I want to make that dynamic.
namespace TestWinform
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ProductionSystem());
}
}
}
I want to pass a value to the executable while executing the app. If for example I want to open the form "ProductionDowntime.cs" by default not the "ProductionSystem" then I would like to pass 'ProductionDowntime' as parameter basically making it dynamic.
System.Diagnostics.Process.Start("C:\Users\WEIRepService\Desktop\Release\DepartmentDowntime.exe 'ProductionDowntime' ");
I want to control this from outside the executable(not from within). Please help. Thanks in advance.
Try it with a factory method that would encapsulate form instantiating logic
static class Program
{
static Form CreateFormToOpen(string formName)
{
switch (formName)
{
case "ProductionDowntime":
return new ProductionDowntime();
// insert here as many case clauses as many different forms you need to open on startup
default:
return new ProductionSystem(); // ProductionSystem is a default form to open
}
}
[STAThread]
static void Main(string[] args)
{
string formName = args.Length > 0 ? args[0] : null; // extract form name from command line parameter
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(CreateFormToOpen(formName)); // use factory method to instantiate form you need to open
}
}
}
In program.cs you can change
static void Main()
to
static void Main(string[] args)
then use whatever is in args to trigger the right form.
You'd call your program like this
System.Diagnostics.Process.Start("C:\Users\WEI\Desktop\Release\DepartmentManagement.exe TriggerForm1");
where TriggerForm1 is basically the string that would be available in args
using System;
using System.Windows.Forms;
namespace CommandLineParameters_47230955
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Form1());
if (args.Length > 0)
{
switch (args[0].ToLower())
{
case "triggerform1":
Application.Run(new Form1());
break;
case "triggerform2":
Application.Run(new Form2());
break;
default:
break;
}
}
}
}
}
a piece of cake with using System.Reflection :) I did a small test based on an answer I found here which Kay Programmer has explained incredibly well
Basically you are calling the from from the form's name through its String value. That way you can assign pragmatically any value you like to it.
The "query" part is searching for the specific name of the Form by applying the String argument that we are passing into it and thus at the end using the result in order to initialize the Form for us without using its specific Class name :)
to adapt your example with it try this:
using System.Reflection; // add this ;)
namespace TestWinform
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args) // change this ;)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (args.Length > 0) // check here ;)
{
var _formName = (from t in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
where t.Name.Equals(args[0])
select t.FullName).Single();
var _form = (Form) Activator.CreateInstance(Type.GetType(_formName)); // get result and cast to Form object in order to run below
if (_form != null)
_form.Show();
}
else
{
//no argument passed, no form to open ;)
}
}
}
}
Like #blaze_125 mentioned in his answer you can SWITCH / CASE if you need to send multiple form names and apply a function logic to this.
For example
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Form1());
if (args.Length > 0)
{
OpenForm(args[0]);
}
}
public static void OpenForm(string FormName)
{
var _formName = (from t in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
where t.Name.Equals(FormName)
select t.FullName).Single();
var _form = (Form)Activator.CreateInstance(Type.GetType(_formName));
if (_form != null)
_form.Show();
}

How to monitor another .exe and perform action if it closes

Currently have a program that opens another program. I need to perform an action should that program close. I am pretty new to C# so could use some help getting me pointed in the correct direction.
EDIT: So I am able to get the current form to open the external program. This form then needs to close and another form needs to have a function to perform an action should the loader.exe close. This is what I have in the first form. How do I code the other form to know if this program has ended.
public static class Program
{
public static bool OpenManager { get; set; }
public static int DeskNumber { get; set; }
/// The main entry point for the application.
[STAThread]
static void Main(string[] arguments)
{
/// Do not move this!
OpenManager = false;
MYvariable = 0;
/// ----------------
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Loader());
if (OpenManager)
{
if (MYvariable == 1)
{
System.Diagnostics.Process startProgram = System.Diagnostics.Process.Start("loader.exe", "-r 52");
startProgram.EnableRaisingEvents = true;
Application.Run(new Manager());
Assuming you use the Process class to start the other program, you can subscribe to the Exited event of the Process instance:
Process myProcess = Process.Start("myprogram.exe", "arguments");
myProcess.EnableRaisingEvents = true;
myProcess.Exited += (sender, e) =>
{
// Do what you want to do when the process exited.
}
Or, to do it more explicit, declare an explicit event handler that is called when the process finishes:
public void OnProcessExited(object sender, EventArgs e)
{
// Do what you want to do when the process exited.
}
public void StartProcess()
{
Process myProcess = Process.Start("myprogram.exe", "arguments");
myProcess.EnableRaisingEvents = true;
myProcess.Exited += OnProcessExited;
}
if you are using the Process class within c# to open and keep a track on the opened program you can handle its exited event like so:
App.Exited += AppOnExited;
private static void AppOnExited(object sender, EventArgs eventArgs)
{
// Do your action here
}
App being the name of your process object.
dont forget you may need to remove the handler when your done like so:
App.Exited -= AppOnExited;
If the application returns an int code you can use:
Process P = Process.Start("App.exe");
P.WaitForExit();
int code = P.ExitCode;
An application that has return:
public static int Main(string[] args)
{
return (int);
}
Will set P.ExitCode to the return value when it closes.

Pass arguments to running application

I am making an image uploader (upload image to image hosting website) and I'm having some issues passing an argument (image location to an already running application)
First of all let's say MyApp.exe is always running
Whenever I right click on an image I have added an item in the default windows context menu that says "Upload image".
When that's clicked it needs to pass the location to the already running application.
My program.cs:
static class Program
{
[DllImport("user32.dll")]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, UIntPtr
wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
[STAThread]
static void Main(params string[] Arguments)
{
if (Arguments.Length > 0)
{
//This means that the the upload item in the context menu is clicked
//Here the method "uploadImage(string location)"
//of the running application must be ran
}
else
{
//just start the application
Application.Run(new ControlPanel());
}
}
}
Note that the ControlPanel class doesn't have a visible form, only a tray icon is present since a form is not needed.
Could I get any help on how to do this?
I have figured it out, so awesome thanks for the person who posted the http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/a5bcfc8a-bf69-4bbc-923d-f30f9ecf5f64 link, this is exactly what I was looking for!
Here's a the full solution:
static class Program
{
[STAThread]
static void Main(params string[] Arguments)
{
SingleInstanceApplication.Run(new ControlPanel(), NewInstanceHandler);
}
public static void NewInstanceHandler(object sender, StartupNextInstanceEventArgs e)
{
string imageLocation = e.CommandLine[1];
MessageBox.Show(imageLocation);
e.BringToForeground = false;
ControlPanel.uploadImage(imageLocation);
}
public class SingleInstanceApplication : WindowsFormsApplicationBase
{
private SingleInstanceApplication()
{
base.IsSingleInstance = true;
}
public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
{
SingleInstanceApplication app = new SingleInstanceApplication();
app.MainForm = f;
app.StartupNextInstance += startupHandler;
app.Run(Environment.GetCommandLineArgs());
}
}
}
Thanks alot all, and especially the person who posted that link I mentioned above but I guess he deleted his answer?
Regards,
Kenny
Well you will have to establish a communication channel for other applications to post the images to. This communication channel can be one of the following - not a complete list just samples:
A directory that is watched by your app and the file is added once it is added to the directory.
A port where other applications can send information to.
A self-hosted web service that accepts the images.
A TCP port that receives the images.
A named pipe.
....
As you see there are several possibilities. The right one for you depends on your scenario. The file system is an option that can be implemented easily using a FileSystemWatcher for a sample see here.
A self-hosted web sevice exposes a web service that can receive images. See here for a sample.
IMHO, these are the two options that are easiest. But ... there are several more.
For the TCP port see Tim's post.
Assuming that you have control over the execution environment, the listening application could just expose an endpoint using WCF or even a raw TCP socket. That way, any other application can connect to it in a dynamic but structured fashion.
Even though both the sender and receiver are on the same machine, using a network transport solution (like WCF or TCP) is a great way to safely send data across processes.
Here's an example of how to do it in TCP with c#: http://www.switchonthecode.com/tutorials/csharp-tutorial-simple-threaded-tcp-server
WCF can be a bit more complicated (due in part to it's flexibility, and also due to serialization restrictions) but there is plenty of documentation online on how to use it. WCFis a more object-oriented solution because proxy classes can be generated that allow you to make strongly-typed calls to actual objects, versus just sending messages.
To avoid the running of second instance after passing the command line arguments to existing instance, I added below code snippet.
static class Program
{
[STAThread]
static void Main(params string[] Arguments)
{
Form1 MainForm;
bool bInstanceFlag;
Mutex MyApplicationMutex = new Mutex(true, "MyApp_Mutex", out bInstanceFlag);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (!bInstanceFlag)
{
MainForm = new Form1();
SingleInstanceApplication.Run(MainForm, NewInstanceHandler);
}
else
{
MainForm = new Form1();
SingleInstanceApplication.Run(MainForm, NewInstanceHandler);
MainForm.Close();
}
}
public static void NewInstanceHandler(object sender, StartupNextInstanceEventArgs e)
{
MainForm.AddItem = e.CommandLine[1];
e.BringToForeground = false;
}
public class SingleInstanceApplication : WindowsFormsApplicationBase
{
private SingleInstanceApplication()
{
base.IsSingleInstance = true;
}
public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
{
SingleInstanceApplication app = new SingleInstanceApplication();
app.MainForm = f;
app.StartupNextInstance += startupHandler;
app.Run(Environment.GetCommandLineArgs());
}
}
}
I added some small additions to the previous solution to reference a setter on the form in order to pass the arguments to it.
So first of all, create a static reference to the initial instance of the form (MainForm).
Then, on subsequent sending of arguments, the NewInstanceHandler can use the saved reference to the form to access it's public methods/properties (in my case, a setter called AddItem).
An easy way to test this out is to add a public property to your form with a setter to change the text property of the form (the title text).
[STAThread]
static Form1 MainForm;
static void Main(params string[] Arguments)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm = new Form1();
SingleInstanceApplication.Run(MainForm, NewInstanceHandler);
}
public static void NewInstanceHandler(object sender, StartupNextInstanceEventArgs e)
{
MainForm.AddItem = e.CommandLine[1];
e.BringToForeground = false;
}
public class SingleInstanceApplication : WindowsFormsApplicationBase
{
private SingleInstanceApplication()
{
base.IsSingleInstance = true;
}
public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
{
SingleInstanceApplication app = new SingleInstanceApplication();
app.MainForm = f;
app.StartupNextInstance += startupHandler;
app.Run(Environment.GetCommandLineArgs());
}
}

getting string from c# command line and passing it to wpf window

Very newbie question.
I want to overwrite the Main in my WPF app so if I double-click on a file, it will be loaded. My main function is:
[STAThread]
static void Main(string[] args)
{
FileConvert.App app = new FileConvert.App();
app.InitializeComponent();
if (args.Length > 0)
{
Window1 wnd1 = (Window1)(app.MainWindow);
wnd1.SetProjectFile(args[0]);
}
app.Run();
My problem is that wnd1 is null. How do I get access to this window so I can pass it the filename to load?
Thanks!
Instead of overwriting the Main method, try overriding the OnStartup method in App.xaml.cs:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
if (e.Args.Length > 0)
((Window1) MainWindow).SetProjectFile(e.Args[0]);
}
}

Categories