Disable C# Form until external Excel application is closed - c#

I have a button on a C# Windows forms application that runs the code below:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
string path = "D:\MyCsvFile.csv";
Process.Start(path);
}
Basically, the user needs to be able to make changes to a CSV file. The user is most comfortable making these changes in Microsoft Excel. I found the above code online from someone who has many years of experience working with MS Office products and C#. This guy explained that the code below works but is more error prone than using "Process.Start()" as shown above.
string path = "D:\MyCsvFile.csv";
var ExcelApp = new Excel.Application();
ExcelApp.Workbooks.OpenText(path, Comma: true);
ExcelApp.Visible = true;
This guy explains that "Process.Start()" works because Windows is set up to use Excel as the default program for opening CSV files.
Anyways, when the user clicks the button above (btnAddDataToCSV), I need my C# form to become disabled (all the buttons grayed out) until the user closes the Excel workbook that is displaying the CSV file. Potentially, I may need the C# program to read the CSV file immediately after the user closes Excel.
Ho do I do this?
I assume... I need to do something like... "disable C# form... then Wait for Excel close event... then once excel close event happens... enable C# form and read CSV file."
Below is the code I have for reading the CSV file:
string path = "D:\MyCsvFile.csv";
var reader = new StreamReader(File.OpenRead(path));
List<string> listA = new List<string>();
List<string> listB = new List<string>();
List<string> listC = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
listA.Add(values[0]);
listB.Add(values[1]);
listC.Add(values[2]);
}

I think that you need to use an event handler for process that exits.
Something like this:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
//disable form
string path = "D:\MyCsvFile.csv";
using (myProcess = new Process())
{
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.Start(path);
}
}
public void Excel_Exit(object sender, System.EventArgs e){
//enableForm
}
Process.Exited example in documentation: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.exited?view=netcore-3.1

I was unable to get the solution above to work even though the solution is similar to Microsoft's solution.
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.exited?view=netcore-3.1
I found this:
Why is my process's Exited method not being called?
It says:
"I've come across examples that place new Process() in a using clause. Do not do that if you want to use the Exited feature. The using clause destroys the instance along with any event handles on Exited."
It says:
using(var process = new Process())
Should be:
var process = new Process();
This makes no since because the link above that I provided for Microsoft is an example of how to use the "Process.Exited Event" and it uses "using(var process = new Process())"
But sure enough... for some reason when I tried "var process = new Process();" it worked... This is the code that worked:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
var myProcess = new Process();
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.StartInfo.FileName = "D:\\MyCsvFile.csv";
myProcess.Start();
}
public void Excel_Exit(object sender, System.EventArgs e)
{
MessageBox.Show("Success!!");
}
Here is the code that does not work... But I don't understand why because it is similar to Microsoft's example.
private Process myProcess;
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
using (myProcess = new Process())
{
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.StartInfo.FileName = "D:\\MyCsvFile.csv";
myProcess.Start();
}
}
public void Excel_Exit(object sender, System.EventArgs e)
{
MessageBox.Show("Success!!");
}
When you close excel... the event never fires... Can someone explain this? Surely Microsoft's example isn't wrong.

Related

Basic use of Linklabel tool in winforms gives Exception Handling Error [duplicate]

I recently started to work with dynamic components, and it worked fine until I started with dynamic linked label. So here's my Problem: I want to open a website through a label link but every time I try to do that, it happens to break the program and give me the error: System.ComponentModel.Win32Exception.
private void CreateDynamicLinkedLabel() {
LinkLabel mylinklab = new LinkLabel();
mylinklab.Text = "asdasdasda";
mylinklab.AutoSize = true;
mylinklab.LinkClicked += new LinkLabelLinkClickedEventHandler(mylinklab_Clicked);
Controls.Add(mylinklab);
}
private void mylinklab_Clicked(object sender, LinkLabelLinkClickedEventArgs e) {
Process.Start("http://www.google.com");
}
I also tried this:
private void mylinklab_Clicked(object sender, LinkLabelLinkClickedEventArgs e) {
Process.Start("chrome.exe","http://www.google.com");
}
i even included using System.Diagnostics;
I tried to figure out why it wont work, but every Youtube Video looks like my code.
Maybe there is another way to open a link, but i cant figure it out.
I assume that you are targeting net core 3+. In this case you need to explicitly set UseShellExecute=true to get behaviour as it was in net framework. In net core there was a breaking changes in the default options for new process. More details https://github.com/dotnet/runtime/issues/17938
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = url,
UseShellExecute = true
};
Process.Start (psi);
Add this in your method in your code:
Process.Start(new ProcessStartInfo() { FileName = url, UseShellExecute = true });

Dynamic Linked Label cannot open a website

I recently started to work with dynamic components, and it worked fine until I started with dynamic linked label. So here's my Problem: I want to open a website through a label link but every time I try to do that, it happens to break the program and give me the error: System.ComponentModel.Win32Exception.
private void CreateDynamicLinkedLabel() {
LinkLabel mylinklab = new LinkLabel();
mylinklab.Text = "asdasdasda";
mylinklab.AutoSize = true;
mylinklab.LinkClicked += new LinkLabelLinkClickedEventHandler(mylinklab_Clicked);
Controls.Add(mylinklab);
}
private void mylinklab_Clicked(object sender, LinkLabelLinkClickedEventArgs e) {
Process.Start("http://www.google.com");
}
I also tried this:
private void mylinklab_Clicked(object sender, LinkLabelLinkClickedEventArgs e) {
Process.Start("chrome.exe","http://www.google.com");
}
i even included using System.Diagnostics;
I tried to figure out why it wont work, but every Youtube Video looks like my code.
Maybe there is another way to open a link, but i cant figure it out.
I assume that you are targeting net core 3+. In this case you need to explicitly set UseShellExecute=true to get behaviour as it was in net framework. In net core there was a breaking changes in the default options for new process. More details https://github.com/dotnet/runtime/issues/17938
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = url,
UseShellExecute = true
};
Process.Start (psi);
Add this in your method in your code:
Process.Start(new ProcessStartInfo() { FileName = url, UseShellExecute = true });

Console within form Visual C#

I want to get a console window within my form. Basically when you click button1, it runs a batch script(test.exe). I don't want a separate batch window but instead I want it to show up within my form.
I figure there are probably two ways of doing this, either 1, somehow embedding the console within my form, or 2, set StartInfo.CreateNoWindow = true; when you click button1 and get the output to funnel into a listbox to simulate a console within my form.
I am just a little stuck because I have found methods for doing both but my own testing with the various other methods people have suggested, nothing has worked. But either way, my user needs to be able to send input back to the console.
Which method would be simpler and how would I go about it?
I believe the best way to do this is to redirect output. Basically things will still execute as you want, but you will get the output wherever you want/need.
using System;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
namespace ConsoleOutput_test
{
public partial class Form1 : Form
{
Process sortProcess;
private static StringBuilder sortOutput = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
sortProcess = new Process();
sortProcess.StartInfo.FileName = "C:\\Windows\\System32\\cmd.exe";
// Set UseShellExecute to false for redirection.
sortProcess.StartInfo.CreateNoWindow = true;
sortProcess.StartInfo.UseShellExecute = false;
// Redirect the standard output of the sort command.
// This stream is read asynchronously using an event handler.
sortProcess.StartInfo.RedirectStandardOutput = true;
sortProcess.StartInfo.RedirectStandardInput = true;
sortProcess.StartInfo.RedirectStandardError = true;
sortOutput = new StringBuilder("");
// Set our event handler to asynchronously read the sort output.
sortProcess.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
sortProcess.ErrorDataReceived += new DataReceivedEventHandler(SortErrorHandler);
// Redirect standard input as well. This stream
// is used synchronously.
sortProcess.StartInfo.RedirectStandardInput = true;
// Start the process.
sortProcess.Start();
// Start the asynchronous read of the sort output stream.
sortProcess.BeginOutputReadLine();
while (!sortProcess.HasExited)
{
Application.DoEvents(); // This keeps your form responsive by processing events
}
}
private void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (txtConsole.InvokeRequired) { txtConsole.BeginInvoke(new DataReceivedEventHandler(SortOutputHandler), new[] { sendingProcess, outLine }); }
else
{
txtConsole.AppendText(Environment.NewLine + outLine.Data);
}
}
private void SortErrorHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (txtConsole.InvokeRequired) { txtConsole.BeginInvoke(new DataReceivedEventHandler(SortErrorHandler), new[] { sendingProcess, outLine }); }
else
{
txtConsole.AppendText(Environment.NewLine + outLine.Data);
}
}
private void button2_Click(object sender, EventArgs e)
{
sortProcess.StandardInput.WriteLine(txtOutput.Text);
txtOutput.Text = "";
}
}
}

C#,Winform - Setting arguments for Process class with Drawing.Image

I'm generating a picture in the picturebox
pictureBox1.Image = Image.FromStream(imageActions.GetImage(reader["ID_no"].ToString()));
It is working perfectly, but I am also creating an option for the user to edit it via any application (Let's example macromedia) So I created a button and did the thing.
private void button2_Click(object sender, EventArgs e)
{
Process photoViewer = new Process();
photoViewer.StartInfo.FileName = #"C:\Program Files\Macromedia\Fireworks 8\Fireworks.exe";
photoViewer.StartInfo.Arguments = ___________;
photoViewer.Start();
}
I understand that in the photoViewer.StartInfo.Arguments = you can put here the path of the image, but in my case. the image is stored in the Database as Image datatype. any ideas?
In order to load the image in an external application, you will first of all need to save it to the disk.
After the application has closed you will need to load the updated image to display to the user.
The Image property of the PictureBox control has a Save method you can call:
string tempFile = System.IO.Path.GetTempFileName();
pictureBox1.Image.Save(tempFile);
You can then pass the tempFile value as a parameter to the photoViewer process (I used MSPaint as a Proof of Concept):
Process photoViewer = new Process();
photoViewer.StartInfo.FileName = #"C:\Windows\System32\MSPaint.exe";
photoViewer.StartInfo.Arguments = tempFile;
photoViewer.EnableRaisingEvents = true;
photoViewer.Exited += photoViewer_Exited;
photoViewer.Start();
The two lines of photoViewer.EnableRaisingEvents = true and photoViewer.Exited += photoViewer_Exited; will tell your application when the photoViewer process exits, and this is a good place for you to load the image and display to your users.
private void photoViewer_Exited(object sender, EventArgs e)
{
pictureBox1.Image = Image.FromFile(tempFile);
}
Note: string tempFile will need to be a class member variable so it can be accessed in the two functions.

How to make a process restart over and over again when checkbox is checked?

I want to create an auto restarter for my server. I have added a checkbox and made all required checks but I don't have any idea how to make that process restart upon crash.
Here is my code:
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
string world = textBox1.Text;
string auth = textBox2.Text;
if (checkBox1.Checked == true)
{
if (textBox1.Text.Trim().Length == 0 || textBox2.Text.Trim().Length == 0)
{
MessageBox.Show("Please check if you selected the path for worldserver and authserver");
}
else
{
//here i need something to restart those 2 processes after crash/close
}
}
}
The simplest way to start a process is to use the Start static method of the Process class:
Process.Start("yourapp.exe");
For access to more specific options, you can set up a ProcessStartInfo object instead:
var startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "yourapp.exe";
startInfo.Arguments = "-arg1 val1";
var exeProcess = Process.Start(startInfo);
exeProcess.Start();
To check whether the process in question is still running, you can use this:
var matchingProcesses = Process.GetProcessesByName("yourapp");
var isRunning = matchingProcesses.Length > 0;
And you can put that in a method and poll every few seconds or milliseconds (depending on how fast you want to respond):
var aTimer = new Timer();
aTimer.Elapsed += new ElapsedEventHandler(YourMethod);
aTimer.Interval = 1000;
aTimer.Enabled = true;
These classes are found in the System.Diagnostics and System.Timers namespaces, respectively.
Though it's not C# (but better, it works with almost any program), you can do an auto restarter in batch (or in bash, but I won't put the code here), and it will be enough for most cases:
#echo off
:start
myprogram.exe %*
if exist myprogam.lock goto start
inside your program:
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if(checkBox1.Checked)
{
using (System.IO.File.Create("myprogam.lock"));
}
else
{
System.IO.File.Delete("myprogam.lock")
}
}
you should also create the file in your main or init code.
bonus: you can delete the file if your program exits cleanly (or on some errors), and it won't restart.
to use, just put the first code in a .bat file you put in your program folder, and use this to start the program.

Categories

Resources