I have a c# method in console app X this starts a process; console app Y (written in the same c# solution).
App Y then fires a vba macro in an Excel 2010 workbook.
For testing purposes in the wkbook VBA I've added some code to force a runtime error 1004.
The winForm uses a process event, triggered using a Forms timer, to kill the Process. It is working as programmed I'd just like to try to make it do a little more.
Why, when I kill the process, is the instance of XL staying open at the point when it finds the error? How do I find a way of getting rid of the instance of XL, if it still exists, when it kills the process, and then posting an error message back to my winForm?
(ps the following code is familiar but the question is not a duplicate)
private int elapsedTime;
private Process p;
private System.Windows.Forms.Timer myTimer;
const int SLEEP_AMOUNT = 1000;//1s
const int MAXIMUM_EXECUTION_TIME = 5000;//5s
private void btRunReport_Click(object sender, EventArgs e) {
btRunReport.Enabled = false;
lbStatusUpdate.Text = "Processing..";
//instantiate a new process and set up an event for when it exits
p = new Process();
p.Exited += new EventHandler(MyProcessExited);
p.EnableRaisingEvents = true;
p.SynchronizingObject = this;
elapsedTime = 0;
this.RunReportScheduler();
//add in a forms timer so that the process can be killed after a certain amount of time
myTimer = new System.Windows.Forms.Timer();
myTimer.Interval = SLEEP_AMOUNT;
myTimer.Tick += new EventHandler(TimerTickEvent);
myTimer.Start();
}
private void RunReportScheduler() {
p.StartInfo.FileName = #"\\fileserve\department$\ReportScheduler_v3.exe";
p.StartInfo.Arguments = 2;
p.Start();
}
private void MyProcessExited(Object source, EventArgs e){
myTimer.Stop();
btRunReport.Enabled = true;
lbStatusUpdate.Text = "Start";
}
void TimerTickEvent(Object myObject, EventArgs myEventArgs) {
myTimer.Stop();
elapsedTime += SLEEP_AMOUNT;
if (elapsedTime > MAXIMUM_EXECUTION_TIME)
{p.Kill();}
else
{myTimer.Start();}
}
it might be the issue with report scheduler which does not have the proper method which closes Excel.
This is such method:
private void releaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
catch (Exception ex)
{
obj = null;
MessageBox.Show("Exception Occured while releasing object " + ex.ToString());
}
finally
{
GC.Collect();
}
}
I've left the original bit of code unchanged but I've used the help from Andrew but mainly the help of a good friend of mine who unfortunately isn't signed up to SO. Excel seems to be dead!. Plus he's coded it in such a way that it passes back an indicator telling the form if it had problems with excel or not. Also gives us the option of building in maximum run times for each excel process.
He used the following SO answer to help get rid of Excel
1.In scheduler program
Move timer there
Implement excel cleaning code in the case there is no errors in vba and in the opposite case when maximum execution time reached (use Kill method)
From the scheduler return 0 to the forms application if excel finished normally or 1 if it was killed
2.In the forms application analyse return value from the scheduler in the ProcessExited event handler and enable button, etc
So, the new scheduler:
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Excel = Microsoft.Office.Interop.Excel;
using System.Timers;
class Program
{
private const int SLEEP_AMOUNT = 1000;
private const int MAXIMUM_EXECUTION_TIME = 10000;
private Excel.Application excelApp =null;
private Excel.Workbook book =null;
private Timer myTimer;
private int elapsedTime;
private int exitCode=0;
[DllImport("user32.dll", SetLastError =true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd,out uint lpdwProcessId);
static int Main(string[] args)
{
Program myProgram = newProgram();
myProgram.RunExcelReporting(1);
return myProgram.exitCode;
}
void myTimer_Elapsed(object sender,ElapsedEventArgs e)
{
myTimer.Stop();
elapsedTime += SLEEP_AMOUNT;
if (elapsedTime > MAXIMUM_EXECUTION_TIME)
{
//error in vba or maximum time reached. abort excel and return 1 to the calling windows forms application
GC.Collect();
GC.WaitForPendingFinalizers();
if (book != null)
{
book.Close(false,Type.Missing, Type.Missing);
Marshal.FinalReleaseComObject(book);
book =null;
}
if (excelApp != null)
{
int hWnd = excelApp.Hwnd;
uint processID;
GetWindowThreadProcessId((IntPtr)hWnd,out processID);
if (processID != 0)
Process.GetProcessById((int)processID).Kill();
excelApp =null;
exitCode = 1;
}
}
else
{
myTimer.Start();
}
}
void RunExcelReporting(int x)
{
myTimer =new Timer(SLEEP_AMOUNT);
elapsedTime = 0;
myTimer.Elapsed +=new ElapsedEventHandler(myTimer_Elapsed);
myTimer.Start();
try{
excelApp =new Excel.Application();
excelApp.Visible =true;
book = excelApp.Workbooks.Open(#"c:\jsauto.xlsm");
excelApp.Run("ThisWorkbook.rr");
book.Close(false,Type.Missing, Type.Missing);
}
catch (Exception ex){
Console.WriteLine(ex.ToString());
}
finally
{
//no error in vba and maximum time is not reached. clear excel normally
GC.Collect();
GC.WaitForPendingFinalizers();
if (book != null)
{
try {
book.Close(false,Type.Missing, Type.Missing);
}
catch { }
Marshal.FinalReleaseComObject(book);
}
if (excelApp != null)
{
excelApp.Quit();
Marshal.FinalReleaseComObject(excelApp);
excelApp =null;
}
}
}
}
And the new forms application:
public partial class Form1 : Form
{
SqlDataAdapter myAdapt = null;
DataSet mySet =null;
DataTable myTable =null;
public Form1()
{ InitializeComponent();}
privatevoid Form1_Load(object sender,EventArgs e){
InitializeGridView();
}
private Process myProcess;
private void btRunProcessAndRefresh_Click(object sender,EventArgs e)
{
myProcess =new Process();
myProcess.StartInfo.FileName =#"c:\VS2010Projects\ConsoleApplication2\ConsoleApplication4\bin\Debug\ConsoleApplication4.exe";
myProcess.Exited +=new EventHandler(MyProcessExited);
myProcess.EnableRaisingEvents =true;
myProcess.SynchronizingObject =this;
btRunProcessAndRefresh.Enabled =false;
myProcess.Start();
}
privatevoid MyProcessExited(Object source,EventArgs e)
{
InitializeGridView();
btRunProcessAndRefresh.Enabled =true;
if (((Process)source).ExitCode == 1)
{
MessageBox.Show("Excel was aborted");
}
else
{
MessageBox.Show("Excel finished normally");
}
}
private void btnALWAYSWORKS_Click(object sender,EventArgs e) {
InitializeGridView();
}
privatevoid InitializeGridView() {
using (SqlConnection conn =new SqlConnection(#"Data Source=sqliom3;Integrated Security=SSPI;Initial Catalog=CCL"))
{
myAdapt =new SqlDataAdapter("SELECT convert(varchar(25),getdate(),120) CurrentDate", conn);
mySet =new DataSet();
myAdapt.Fill(mySet,"AvailableValues");
myTable = mySet.Tables["AvailableValues"];
this.dataGridViewControlTable.DataSource = myTable;
this.dataGridViewControlTable.AllowUserToOrderColumns =true;
this.dataGridViewControlTable.Refresh();
}
}
}
Related
I have a program, that has to comunicate with another program that is called from the first.
I have managed to send the required data from the first to the second program using NamedPipes.
When the second program closes, i need to send some data back to the first program. I set up a NamedPipeServerStream in the first program and from the Closing event of the second program a NamedPipeClientStream. Now when i try to write from ClientStream i get a broken pipe error.
This is the code from the first program:
private void CreateOverlay(object sender, EventArgs e)
{
if (oinstallfolder != null)
{
string processfilename = oinstallfolder.ToString() + "\\SecondProgram.exe";*/
string processfilename = "SecondProgram.exe";
if (File.Exists(processfilename))
{
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Equals("SecondProgram"))
{
this.threadHandleOverlayBounds = new Thread(new ThreadStart(this.ExchangeMapOverlayBoundsServer));
this.threadHandleOverlayBounds.Start();
this.threadHandleOverlayFile = new Thread(new ThreadStart(this.ExchangeMapOverlayFileServer));
this.threadHandleOverlayFile.Start();
ShowWindow(clsProcess.MainWindowHandle, SW_SHOWNORMAL);
SetForegroundWindow(clsProcess.MainWindowHandle);
return;
}
}
try
{
this.threadHandleOverlayBounds = new Thread(new ThreadStart(this.ExchangeMapOverlayBoundsServer));
this.threadHandleOverlayBounds.Start();
Logger.Logger.Write("Log.txt", "Starting Filethread serverside");
this.threadHandleOverlayFile = new Thread(new ThreadStart(this.ExchangeMapOverlayFileServer));
this.threadHandleOverlayFile.Start();
Process.Start(processfilename);
}
catch { }
}
}
}
private void ExchangeMapOverlayFileServer()
{
try
{
using (NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("OverlayFilePipe", PipeDirection.In, 1, PipeTransmissionMode.Byte))
{
string line;
namedPipeServer.WaitForConnection();
StreamReader sr = new StreamReader(namedPipeServer);
line = sr.ReadLine();
namedPipeServer.Disconnect();
namedPipeServer.Close();
}
}
catch(Exception e)
{
Logger.Logger.Write("OverlayGenerator.txt", "namedPipeServer exception: " + e.Message + "\n" + e.StackTrace);
}
}
this is the code from the second program
this.Closing += (s, a) =>
{
Logger.Logger.Write("Log.txt", "Window Closing");
this.threadHandleOverlayFile = new Thread(new ThreadStart(this.HandleWindowClosing));
this.threadHandleOverlayFile.Start();
while (!done)
{
Thread.Sleep(100);
}
};
private void HandleWindowClosing()
{
if (!String.IsNullOrEmpty(this.latestSavedOverlayPath))
{
try
{
using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "OverlayFilePipe", PipeDirection.Out))
{
namedPipeClient.Connect();
StreamWriter sw = new StreamWriter(namedPipeClient);
sw.AutoFlush = true;
sw.WriteLine("testphrase");
namedPipeClient.Close();
this.done = true;
}
}
catch (Exception e) { Logger.Logger.Write("OverlayGenerator.txt", "Exception in Client: " + e.Message + "\n" + e.StackTrace); }
}
else
{
Logger.Logger.Write("OverlayGenerator.txt", "Latest saved OverlayPath = " + this.latestSavedOverlayPath);
}
}
I tried this with Autoflush=true and without. With it, i get a broken pipe exception at the line
sw.WriteLine("testphrase");
and without it, the client side just runs to the end and nothing happens at all.
Where is my mistake?
Thank you.
EDIT
OK, i just don't get it. I made a test program, that is build the same way as the actual one. Two applications, the first starting the second. I start the pipe server in a thread in the first application and the client, also in a thread, in the second. The only difference is, that i run this test project in debug mode, which i can't do for the actual program. Now, in the test program, this matter is realy simple and it works. When i use the exact same approach in the actual program, it doesn't work.
In the testprogram i need to use flush on the streamwriter and i don't get an exception. The syncronization of the threads gets handled by the pipe itself, as it should be if i understood this correctly.
The Master looks like this:
private const string CLIENTPROC = "C:\\Users\\Maik\\Source\\Repos\\PipeTest\\PipeClient\\obj\\x86\\Release\\PipeClient.exe";
private ManualResetEvent threadResetEvent;
private Thread threadHandlePipeSendLast;
private Process procClient;
private string msgPipeSend;
private volatile bool bMsgPipeSend;
public MainWindow()
{
InitializeComponent();
this.threadResetEvent = new ManualResetEvent(false);
System.Diagnostics.Debug.WriteLine("### starting thread");
this.threadHandlePipeSendLast = new Thread(new ThreadStart(this.ExchangeMapOverlayFileServer));
this.threadHandlePipeSendLast.Start();
}
private void ExchangeMapOverlayFileServer()
{
System.Diagnostics.Debug.WriteLine("server thread started");
Thread.CurrentThread.Name = "apocalypse";
try
{
using (NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("ClosingPipe", PipeDirection.In, 1, PipeTransmissionMode.Byte))
{
string line;
namedPipeServer.WaitForConnection();
StreamReader sr = new StreamReader(namedPipeServer);
Thread.Sleep(100);
line = sr.ReadLine();
handleRecvMsgFromPipe(line);
namedPipeServer.Disconnect();
namedPipeServer.Close();
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("### " + e.Message + "\n" + e.StackTrace);
}
}
private void handleRecvMsgFromPipe(string line)
{
this.outbox.Text = line;
}
private void buttonOpenFormClient_Click(object sender, EventArgs e)
{
#if DEBUG
#else
if (this.procClient == null)
{
try
{
this.procClient = Process.Start(new ProcessStartInfo(CLIENTPROC));
}
catch (Exception exc)
{
MessageBox.Show(exc.Message, "Error");
}
}
#endif
}
}
And that's the client side:
private ManualResetEvent threadResetEvent;
private Thread threadHandlePipeSendLast;
private string msgPipeSend;
private volatile bool bMsgPipeSend;
private bool done;
public MainWindow()
{
InitializeComponent();
this.threadResetEvent = new ManualResetEvent(false);
this.Closing += (s, a) =>
{
System.Diagnostics.Debug.WriteLine("+++ FormClosing started.");
this.threadHandlePipeSendLast = new Thread(new ThreadStart(this.HandleWindowClosing));
this.threadHandlePipeSendLast.Start();
while (!done)
{
Thread.Sleep(1000);
}
};
}
private void HandleWindowClosing()
{
try
{
using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "ClosingPipe", PipeDirection.Out))
{
namedPipeClient.Connect();
StreamWriter sw = new StreamWriter(namedPipeClient);
//sw.AutoFlush = true;
sw.WriteLine("last message");
namedPipeClient.WaitForPipeDrain();
namedPipeClient.Close();
this.done = true;
}
}
catch (Exception e) { System.Diagnostics.Debug.WriteLine(e.Message + "\n" + e.StackTrace); }
}
Can anyone tell me, why this works in the test app and not in the actual one? I also tried various things from Thread.Sleep(x) at different points in the programs over Thread.Join() to pipe methods like WaitForPipeDrain (which is just ignored).
I launch a program located on one of my file servers. After launching the program it shows as an Open file in Computer Management.
Is there a way I can close this open file while my program runs so it doesn't show up in Computer Management?
My code is below. I'd be happy to take suggestions on improving my program, but I'm really just looking for a solution to stop all these Open Files from appearing.
Program.cs -- starts the program, handles logic to launch my application
using System;
using System.Windows.Forms;
using System.Diagnostics;
namespace IT_TaskbarApp
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
string programName = "TEG System Helper";
//Process[] proc = Process.GetProcessesByName(programName);
if (Process.GetProcessesByName(programName).Length == 1)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Primary());
}
else
{
foreach (Process p in Process.GetProcessesByName(programName))
{
if (Process.GetCurrentProcess().Id != p.Id)
{
p.CloseMainWindow();
p.Close();
p.Kill();
p.Dispose();
}
}
Main();
}
}
}
}
Primary.cs -- creates an icon in the system icons which I can use to send notifications and easily access utilities within our organization
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
namespace IT_TaskbarApp
{
public partial class Primary : Form
{
private NotifyIcon notifyIcon;
private ContextMenu contextMenu;
private MenuItem[] menuItem = new MenuItem[8];
private IContainer components;
//private Boolean SendNotices = true;
private DateTime startTime = DateTime.Now;
private DateTime currentTime;
private Icon tegroupIcon = new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream("IT_TaskbarApp.Src.tegroup.ico"));
private string prevNotification = "";
private bool isRunning = true;
private BackgroundWorker bgNotify = new BackgroundWorker();
private const string programName = "TEG System Helper";
public Primary()
{
this.FormClosing += Primary_FormClosing; //remove ghost icon in taskbar
ForeColor = Color.Blue;
BackColor = Color.Green;
components = new Container();
contextMenu = new ContextMenu();
for (int i = 0; i < menuItem.Length; i++)
{
menuItem[i] = new MenuItem();
menuItem[i].Index = i;
menuItem[i].Click += new EventHandler(LoadProcess);
}
menuItem[0].Text = programName;
menuItem[1].Text = "Knowledge Base";
menuItem[2].Text = "Policies";
menuItem[3].Text = "Feedback";
menuItem[4].Text = "Global Shop Search";
menuItem[5].Text = "-";
menuItem[6].Text = "Submit Ticket";
menuItem[7].Text = "Send Email";
//initialize contextMenu
contextMenu.MenuItems.AddRange(menuItem);
// Create the NotifyIcon.
notifyIcon = new NotifyIcon(components)
{
Icon = tegroupIcon,
BalloonTipIcon = new ToolTipIcon(),
ContextMenu = contextMenu, //the menu when right clicked
Text = programName,
Visible = true,
BalloonTipTitle = programName,
};
notifyIcon.DoubleClick += new EventHandler(Icon_DoubleClick);
InitializeComponent();
bgNotify.WorkerSupportsCancellation = true;
bgNotify.WorkerReportsProgress = true;
bgNotify.DoWork += NotifyUser;
bgNotify.ProgressChanged += SendNotice;
//bgNotify.RunWorkerCompleted += BgNotify_RunWorkerCompleted; //enable this to perform an action when the thread dies
bgNotify.RunWorkerAsync();
//Thread tNotify = new Thread();
}
#region SupportedFunctions
private void NotifyUser(object Sender, EventArgs e)
{
Console.WriteLine("enter");
while (isRunning)
{
currentTime = DateTime.Now;
#region DisplayCurrentTime
if (currentTime.Hour < 10 || currentTime.Minute < 10)
{
if (currentTime.Hour < 10)
{
if (currentTime.Minute < 10)
{
Console.WriteLine("0{0}:0{1}", currentTime.Hour, currentTime.Minute);
}
else
{
Console.WriteLine("0{0}:{1}", currentTime.Hour, currentTime.Minute);
}
}
else
{
if (currentTime.Minute < 10)
{
Console.WriteLine("{0}:0{1}", currentTime.Hour, currentTime.Minute);
}
}
}
else
{
Console.WriteLine("{0}:{1}", currentTime.Hour, currentTime.Minute);
}
#endregion
FileStream fs = new FileStream("\\\\te-admin\\public\\TaskbarNotices.txt", FileMode.Open);
StreamReader sr = new StreamReader(fs);
string noticeText = sr.ReadToEnd();
sr.Close();
fs.Close();
if (noticeText != "" && noticeText != prevNotification)
{
prevNotification = noticeText;
bgNotify.ReportProgress(1);
}
else
{
bgNotify.ReportProgress(2);
}
Console.WriteLine("Inner Text: {0} TOF: {1}", noticeText, noticeText != "");
Thread.Sleep(10000);
}
}
private void SendNotice(object Sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == 1)
{
Console.WriteLine("notification sent");
this.notifyIcon.BalloonTipText = prevNotification;
this.notifyIcon.ShowBalloonTip(1500);
}
}
private void LoadProcess(object Sender, EventArgs e)
{
if (Sender is MenuItem)
{
MenuItem tempMenu = Sender as MenuItem;
string ProgramTag = "http://";
switch (tempMenu.Index)
{
case 0: //home page
ProgramTag += "teg";
break;
case 1: //docviewer
ProgramTag += "teg/docViewer";
break;
case 2: //policies
ProgramTag += "teg/Policies";
break;
case 3: //feedback
ProgramTag += "teg/Feedback";
break;
case 4: //inventory search
ProgramTag = "http://searchglobalshop/inventory/index.aspx";
break;
case 6: //submit ticket
ProgramTag = "https://timberlandgroup.on.spiceworks.com/portal/tickets";
break;
case 7: //send email
string sendto = "admin#tewinch.com";
string emailSubject = "Assistance Request";
string emailBody = "";
string mailto = string.Format("mailto:{0}?Subject={1}&Body={2}", sendto, emailSubject, emailBody);
ProgramTag = Uri.EscapeUriString(mailto);
break;
}
/*
Try to launch the choice the user made with the default processing method.
Should the default method fail we try to control how the process is run.
We open internet explorer and then we show them what to do otherwise.
*/
#region LaunchSelectedProcess
try
{
if (ProgramTag != "" && ProgramTag != "http://")
Process.Start(ProgramTag);
}
catch (System.ComponentModel.Win32Exception)
{
try
{
if (ProgramTag.StartsWith("http://") || ProgramTag.StartsWith("https://"))
Process.Start("iexplore.exe", ProgramTag);
}
catch (System.ComponentModel.Win32Exception)
{
Process.Start("control.exe", "/name Microsoft.DefaultPrograms");
string message = "";
if (tempMenu.Index <= 6)
{
message = "You must have a default browser set\n\tClick [Set Default Program]\n";
if (Environment.OSVersion.ToString().Contains("NT 10.")) //windows 10
{
message += "\tUnder [Web Browser] Edge is currently set as default\n\tClick on Microsoft Edge\n\tSelect the browser you use";
}
else //windows 7 -- "NT 6.1")
{
message += "Select the browser you use\n\tClick [Set this program as default]";
}
}
else
{
if (Environment.OSVersion.ToString().Contains("NT 10.")) //windows 10
{
message += "Please setup a default email application";
}
}
message += "\n\nIf this issue persists please contact your Administrator.\nPhone: 519-537-6262\nEmail: admin#tewinch.com";
MessageBox.Show(message, "Application Warning", MessageBoxButtons.OK, MessageBoxIcon.Information);
//if ( == DialogResult.OK)
}
}
#endregion
}
}
private void Icon_DoubleClick(object Sender, EventArgs e)
{
Process.Start("http://teg");
}
#endregion
#region BuiltFunctions
private void Primary_FormClosing(object sender, FormClosingEventArgs e)
{
notifyIcon.Icon = null;
notifyIcon.Dispose();
isRunning = false;
Dispose(true);
}
private void InitializeComponent()
{
this.SuspendLayout();
//
// Primary
//
this.Icon = tegroupIcon;
this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.CausesValidation = false;
this.ClientSize = new System.Drawing.Size(120, 23);
this.ControlBox = false;
this.Enabled = false;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Primary";
this.Opacity = 0D;
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
this.ResumeLayout(false);
}
protected override void Dispose(bool disposing)
{
// Clean up any components being used.
if (disposing)
if (components != null)
components.Dispose();
base.Dispose(disposing);
}
#endregion
}
}
Instead of cancelling the program on start up, I kill the other running instances of the program. The idea is that if any issues arise with the program I just launch another instance and resolve the issues. Right now not much can go wrong but we will be developing this program to complete many more tasks in the future.
The only area I can see which would keep a file open is when I pull an Embedded Resource tegroup.ico I was looking to see if I missed something while opening this, but I couldn't see a way to close the ManifestResourceStream after reading it in.
Any tips/suggestions would be wonderful but again, I really just want to know if there's a way I can close these Open Files
Example below
Open File after app launch
I might be trying to solve something which is a known result of using Application.Run() if this is the case then please suggest alternatives I can use. My other ideas would be loading the program into memory and launching it locally, using the .exe on the server as a starting point for this method though.
I believe that Windows doesn't load an entire executable into ram. It isn't just about files from the resource section of a PE file. Portions of the exe are only loaded when referenced and even after loading everything there is to load, Windows will maintain an open file handle until the process closes. Trying to close that handle yourself is a bad idea.
c/c++ allow a "SWAPFILE" flag to be specified that tells windows to put the whole thing into the page file but I don't know how you would do that with c# and I don't know if that would even stop windows from keeping the handle open anyways (I doubt it).
If this is truly important, iffin' I were your exe... I would:
Check a mutex for an existing running instance, exit if exist
Check where I was running from.
If running from temp, set a mutex that I am running and just run.
If not running from temp, copy myself to %temp%, start that copy, and exit.
Good luck.
I wrote the simplified version of my program below. Process A launches a child process (Process B). I use an anonymous pipe to write information about the progress of a method running on process B. Meanwhile I have a function in process A that continually reads from a stream to see if there is a new update coming in from the pipe. If there is, the form on process A is updated to reflect the progress. This works as expected, however I am wondering if there is a better way to accomplish this without having to continually check the stream to see if there are any new updates to the progress.
/////////////////
///Process A ////
/////////////////
public void LaunchProcessB()
{
using (AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream(PipeDirection.In,
HandleInheritability.Inheritable))
{
var _Process = new Process();
_Process.StartInfo.FileName = exeString;
_Process.StartInfo.Arguments = pipeServer.GetClientHandleAsString()
_Process.StartInfo.RedirectStandardOutput = true;
_Process.StartInfo.RedirectStandardInput = true;
_Process.StartInfo.CreateNoWindow = true;
_Process.StartInfo.UseShellExecute = false;
_Process.Start(); //launches process B
pipeServer.DisposeLocalCopyOfClientHandle();
using (StreamReader sr = new StreamReader(pipeServer))
{
try
{
while (true)
{
string temp = sr.ReadLine();
if (temp == null) break;
int result;
if (Int32.TryParse(temp, out result))
ShowDocumentProgress(result);
else ShowProgress(temp);
}
}
catch (Exception)
{
//error occured when reading from stream.
}
}
if (!_Process.Responding && !_Process.HasExited)
{
_Process.Kill();
return;
}
_Process.WaitForExit(10000);
}
}
private void ShowProgressPercent(int percentage)
{
if (percentage > currentPercentage)
{
progressBar.Value = percentage;
}
}
private void ShowProgress(string progressString)
{
labelMessage.Text = progressString;
}
/////////////////
///Process B ////
/////////////////
private StreamWriter _progressWriter;
private PipeStream _progressPipe;
static int Main(string[] args)
{
using (progressPipe = new AnonymousPipeClientStream(PipeDirection.Out, args[0]))
using (_progressWriter = new StreamWriter(_progressPipe))
{
RunLongProcess()
}
}
private void RunLongProcess()
{
//attaches events to PercentProgress and StageProgress methods.
}
private void PercentProgress(int percentage)
{
_progressWriter.WriteLine(percentage.ToString());
_progressPipe.WaitForPipeDrain();
}
private void StageProgress(string stage)
{
_progressWriter.WriteLine(stage);
_progressPipe.WaitForPipeDrain();
}
The while condition is not necessary. Simply read until temp is null. That's the end signal of the stream.
Make this a while(true) loop.
I think you also need to add exception handling to catch the process terminating and severing the pipe. !_Process.HasExited && pipeServer.IsConnected is not enough because it might be true but immediately switch to false after the test.
I also would add a WaitForExit at the end to make sure the system is quiesced before you continue.
Think We have this part in our program:
private void button1_Click(object sender, EventArgs e)
{
while (true) ;
}
If we run the program it will crash and say "Not Responding". How to prevent this. I want the program check itself and if it dose not respond, restart itself.
Note:
We can use BackgroundWorker class. like this:
private readonly BackgroundWorker worker;
public Form1()
{
InitializeComponent();
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += Form1_Load;
worker.ProgressChanged += button1_Click;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
But there is no method or property or event in that class to understand not responding.
Any help will be appreciate.
Edit 3:
I used BackgroundWorker with ThredTimer and checked if process is "Not Responding". This dose not close the program but start a new one.
Certainly this is not a clean code, but it is a way. I'm still looking for how to use BeginInvoke as #micky said Or other way to make this beautiful. Thanks to all of you
private readonly BackgroundWorker _Worker;
public Form1()
{
InitializeComponent();
_Worker = new BackgroundWorker();
_Worker.DoWork += new DoWorkEventHandler(_Worker_DoWork);
_Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_Worker_RunWorkerCompleted);
_Worker.WorkerReportsProgress = true;
_Worker.WorkerSupportsCancellation = true;
}
// BCW starts here.
private void button1_Click(object sender, EventArgs e)
{
_Worker.RunWorkerAsync();
while (true) ;
}
// ThreadTimer will run here.
void _Worker_DoWork(object sender, DoWorkEventArgs e)
{
if (_Worker.IsBusy == true)
{
if (_Worker.CancellationPending)
{
e.Cancel = true;
}
TimerCallback tmrCallBack = new TimerCallback(CheckStatusThreadHealth);
System.Threading.Timer tmr = new System.Threading.Timer(tmrCallBack, null, 10000, 10000);
}
}
// This will call by ThreadTimer and check processes for not responding
public void CheckStatusThreadHealth(object IsBusy)
{
Process application = null;
foreach (var process in Process.GetProcesses())
{
if (process.ProcessName == "WindowsFormsApplication1")
{
application = process;
break;
}
}
if (!application.Responding)
{
// This should end BCW and go to _Worker_RunWorkerCompleted. But it didn't
_Worker.CancelAsync();
// This Restart program but did not close the last one.
Application.Restart();
}
}
// this should run when BCW has error or cancel, But never this happen
void _Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Application.Restart();
return;
}
}
The Answer: Thanks to Jason Williams
I used a watch dog program as a solution and Communicate between two program by this link The code Changed to this:
In Main Program
public partial class Form1 : Form
{
// Use SendMessage API
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);
// define a message
private const int RF_TESTMESSAGE = 0xA123;
// The timer will begin with the form
public Form1()
{
InitializeComponent();
var timer = new Timer();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = 10000; //10 seconds
timer.Start();
}
//Timer look for a Watch Dog Program and send message to it
void timer_Tick(object sender, EventArgs e)
{
try
{
Process WatchDogProccess = Process.GetProcessesByName("Watch Dog")[0];
SendMessage(WatchDogProccess.MainWindowHandle, RF_TESTMESSAGE, IntPtr.Zero, IntPtr.Zero);
}
catch
{
//if Watch Dog is not running, will reach here
}
}
// if we click this button we will got "Not Responding"
private void button1_Click(object sender, EventArgs e)
{
while (true) ;
}
}
In Watch Dog Program
public partial class Form2 : Form
{
//the message that have been define in main program
private const int RF_TESTMESSAGE = 0xA123;
//Recive the message
protected override void WndProc(ref Message message)
{
if (message.Msg == RF_TESTMESSAGE)
{
// this mean the mian program run perfectly
}
else
{
//the main program is not responding
//Main program's name and address
string FileAddress = #"C:\Users\...\MainProgram.exe";
string FileName = "MainProgram";
// This will restart the main program
RestartProgram(FileAddress, FileName);
}
base.WndProc(ref message);
}
// This will restart the main program
public void RestartProgram(string FileAddress, string FileName)
{
//Find the Process
Process[] prs = Process.GetProcessesByName(FileName);
foreach (Process pr in prs)
{
if (!pr.Responding)
{
try
{
pr.Kill();
//then to restart-
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = FileAddress
}
};
process.Start();
}
catch { }
}
}
}
public Form2()
{
InitializeComponent();
}
}
}
You could solve this with a background thread that somehow detects that the ui thread is not responding. But how would this thread reset/restart your ui thread? This could be very difficult to do cleanly, as you would somehow have to reset all the state throughout your app to a known good condition.
So the conventional approach is instead to have a separate "watch dog" process. This can detect that your app is dead, kill its process and then run a new instance.
So how to detect that it is dead? There are many clever ways you can do this, but the simplest and most reliable is to just send a message from your app to the watch dog periodically (e.g. once per second) using a Windows forms timer - if your ui thread is busy it will not process the timer event and so the message won't be sent, and after a few seconds your watch dog will be confident that it is unresponsive, and be able to restart it.
I have a method that allows a word document to be opened in word and waits for word to exit before completing.
If word is not already running all works well.
If word is running the process exits immediately so I can;t wait for exit.
Any ideas how I can wait for the document to close if word is already running?
This is on Windows 8.1
public void ShowExternalReference(string externalRef, bool waitForCompletion)
{
if (externalRef.NotEmpty())
{
var pInfo = new ProcessStartInfo {FileName = externalRef};
// Start the process.
Process p = Process.Start(pInfo);
if (waitForCompletion)
{
// Wait for the window to finish loading.
p.WaitForInputIdle();
// Wait for the process to end.
p.WaitForExit();
}
}
}
you can get the current running Word Process and attach to the events
I got some info here
Here is an example for attaching and putting text into the doc...
Hope this helps.
using Word = Microsoft.Office.Interop.Word;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load_1(object sender, EventArgs e)
{
}
public void ShowExternalReference(string externalRef, bool waitForCompletion)
{
if (externalRef.Length > 0)
{
var pInfo = new ProcessStartInfo { FileName = externalRef };
bool isrunning = false;
Process [] pList = Process.GetProcesses();
foreach(Process x in pList)
{
if( x.ProcessName.Contains("WINWORD"))
{
isrunning = true;
Word.Application myWordApp =
System.Runtime.InteropServices.Marshal.GetActiveObject(
"Word.Application") as Word.Application;
if(myWordApp.ActiveDocument.FullName.Contains(externalRef))
// do something
myWordApp.ActiveDocument.Content.Text = " already open";
}
}
if(!isrunning)
{
// Start the process.
Process p = Process.Start(pInfo);
if (waitForCompletion)
{
// Wait for the window to finish loading.
p.WaitForInputIdle();
// Wait for the process to end.
p.WaitForExit();
}
}
}
}
private void button1_Click(object sender, EventArgs e)
{
string myWordFile = #"C:\Temp\test.docx";
ShowExternalReference(myWordFile, true);
}
private void listView1_ItemChecked(object sender, ItemCheckEventArgs e)
{
listView1.Items[e.Index].Group = listView1.Groups[e.NewValue == CheckState.Checked ? 0 : 1];
}
Process[] pname = Process.GetProcessesByName("winword.exe");
if(pname.Length == 0)
{
//not running..
}
you could loop this through a background thread perhaps to continually test if Word is running, and fire an event back when it isn't