前言

如果要解决“C#winform 点叉叉按钮关闭一form窗体应用后,后台进程仍在运行”的问题,可以直接点击目录跳转到3.4关闭窗体的多种方法。

前面为记录遇到该问题的一些过程。
之前用C#做了一个日志备份的窗体应用程序,最近把这个程序完善了一下,在窗体上显示了配置文件中的一些信息,并且增加了更新按钮,点击会将一些比较重要的配置写到日志文件中。

但是在测试的时候发现了一个问题,运行生成的Release文件中的exe文件(因为最后要放到服务器上使用,不会将整个项目拷贝过去,只拷贝Release文件)。

在第一次运行的时候没什么问题,但是将程序关闭后,修改完配置文件中的线程数再启动的时候,窗体可以正常显示,备份功能也可以正常实现,但是日志中没有写入任何东西,点击更新按钮也没有将配置文件中的信息写入日志当中。下面的是第一次运行的时候写入的,但是后面就不显示了,点击更新按钮也不显示。

一、可能的原因1

1.1猜想1

因为程序里面有使用多线程,我想会不会是在vs上运行程序生成项目的时候,线程数被固定了,后续更改配置文件的线程数与生成时项目中的线程数不一致导致程序出错。

1.2验证

首先生成了一个4线程的项目,然后运行Release文件中的exe文件,发现功能没问题,日志也可以写入。
然后将窗体关闭,将配置文件中的线程数改为3,窗体启动后发现无法正常使用。
然后将窗体关闭,将配置文件中的线程数改为2,亦是如此。
然后将窗体关闭,将配置文件中的线程数改回4,亦是如此。

后续又做了如下验证:

期间有debug,开启线程没有问题,会按照配置文件中修改的参数来开启对应的线程,点击按钮也有执行日志写入功能,只是.log日志文件中没有实际写入,这说明程序应该是没有问题的。

1.3结论

根据上面的验证结果,得出的结论是跟生成的线程数没有关系。

二、可能的原因2

2.1猜想2

我想起之前在更新版本的时候,需要删除之前的版本或者给之前版本的文件加上日期,在应用窗体已经关闭的情况下还是显示该文件被占用无法删除或者修改,打开任务管理器,会发现有好几个该程序的进程正在运行,如实想会不会是因为该程序在上次关闭的时候没有彻底关闭,后来又运行exe的时候导致开了好几个,导致日志无法写入。

2.2验证

运行Release文件中的exe文件,将窗体应用关闭后,发现后台进行并没有关闭

此时不做任何修改,再执行exe文件,窗体启动后发现无法正常使用。
然后手动在任务管理器中结束该进程,重新启动exe,发现功能可以正常使用。

修改配置文件中的线程数,重新启动exe,发现功能可以正常使用。

2.3结论

功能无法正常使用就是因为程序进行没有完全结束,即使关闭了窗体应用,但是程序还在后台运行,如果在每次启动前去任务管理器将该进程关闭,该就可以解决。

但是该方法比较麻烦,而且治标不治本,于是便开始查询为什么会出现这种情况。

三、最终解决办法

3.1出现的原因:

有的说是有许多人在用做c# 做登录窗体时,登录成功后当前登录Form关闭,打开Main窗体,但Main窗体点关闭按钮后程序仍在进程仍旧在运行。
事实上,关闭Main窗体,只是关闭了Main窗体的线程,并没有关闭程序的主线程,即程序的主线程为登录From。

但是我这个只有一个窗口,所以不存在多窗体没有关闭的情况。
我的真实情况是程序里面开线程了,有托管线程(非主线程),无法干净地退出。(自己遇到的情况基本也是如此)

3.2解决办法1

有的说在FormClosing中添加Application.Exit()可以彻底结束进程。
怎么使用FormClosing事件,可以参考:
链接: FormClosing事件在哪,怎么打开FormClosing事件

于是添加了如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("将要关闭窗口,是否继续?", "询问", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
e.Cancel = false;
Application.Exit();
}
else
{
e.Cancel = true;
}
}

但是发现关闭窗体后程序依旧没有彻底关闭。

当我们关闭窗口时,只是停止了当前窗口的消息循环。当Main函数中有消息循环的窗口,这个消息循环结束后,Main函数就基本上完成了历史使命,整个应用程序自然就结束了。

而Application.Exit()方法是终止所有线程上的消息循环,一般情况下,无论在什么地方调用此方法,程序就能退出。

但是如果你在程序中加入了某些耗时甚至是死循环的线程,那么即使是消息循环终止,程序也依然不会结束。

而且这个代码还有一个问题,会导致显示确定退出的两次对话框,第一次单击“确定”第二次单击任何一个都会退出。

因为第一次单击了退出对话框的“确定”按钮后,Application.Exit();会导致触发第二次FrmMain_FormClosing()事件,所以就会显示两个确认退出的对话框,但第二对话框的选择对是否退出没有影响。可以改为判断如果单击了不是确认按钮则e.Cancel = true; ,确定按钮什么都不写或不做判断,如果要加上Application.Exit(); 可以将其添加到窗体的FormClosed事件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("将要关闭窗口,是否继续?", "询问", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
e.Cancel = false;
}
else
{
e.Cancel = true;
}
}
//20221227 窗体关闭
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
Application.Exit();
}

这样就不会出现弹出两次窗口的情况,但是程序没有彻底结束的问题依旧没有解决。

3.3解决办法2

在Form1_FormClosed事件中加入强制结束进程并退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("将要关闭窗口,是否继续?", "询问", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
e.Cancel = false;
}
else
{
e.Cancel = true;
}
}
//20221227 窗体关闭
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
Application.Exit();
//强制结束进程并退出
//System.Diagnostics.Process.GetCurrentProcess().Kill();
System.Environment.Exit(0);
}

3.4关闭窗体的多种方法

在c#中退出WinForm程序包括有很多方法,如:**this.Close(); Application.Exit();Application.ExitThread(); System.Environment.Exit(0); **等他们各自又有一些不一样的地方,下面详细介绍一下。

  • 1.this.Close(); 只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外若有托管线程(非主线程),也无法干净地退出;

  • 2.Application.Exit(); 强制所有消息中止,退出所有的窗体,但是若有托管线程(非主线程),也无法干净地退出;

  • 3.Application.ExitThread(); 强制中止调用线程上的所有消息,同样面临其它线程无法正确退出的问题;

  • 4.System.Environment.Exit(0); 这是最彻底的退出方式,不管什么线程都被强制退出,把程序结束的很干净。

3.5FormClosing事件和FormClosed事件

这里来说一下FormClosing事件和FormClosed事件

FormClosing事件
在窗体关闭时,FormClosing事件发生。此事件会得到处理。从而释放与窗体相关的所有资源。
如果取消此事件,则窗体仍然保持打开状态。
当窗体显示为模式对话框时,单击“关闭”会隐藏窗体并将DialogResult属性设为Cancel。
通过在些事件中设置DialogResult属性可以在用户单击右上角关闭按钮时重写DialogResult的值。

FormClosed事件
在用户或Application类的Close方法或Exit方法关闭窗体后,会发生FormClosed事件。
可以使用此事件释放窗体的一些资源。还可以使用此事件保存输入窗体中的一些信息或者更新父窗体。

Form窗体点击关闭按钮并未关闭进程的解决方法(其他人参考)

相信很多朋友在日常的编程中总会遇到各钟各样的问题,关于Form窗体点击关闭按钮并未关闭进程的解决方法就是很多朋友们都认为很难的一个学习.net的难点,下面就由达内为您介绍一下。

该问题也是因为本人Form项目开发经验不够引起的。

在开发一个小工具的过程中,因为是有多个Form窗体,多个窗体间的跳转都是如下代码:

1
2
3
4
5
private void button1_Click(object sender, EventArgs e) {   
this.Hide();
UrlList form = new UrlList(this.cbuserid.SelectedItem.ToString());
form.Show();
}

这样子,我在UrlList窗体上面点击关闭按钮,想再要调试就会报无法将文件“obj\Debug\AutoPage.exe”复制到“bin\Debug\AutoPage.exe”。文件“bin\Debug\AutoPage.exe”正由另一进程使用,因此该进程无法访问该文件。一定要在任务管理器中关掉该进程后再调试,很是麻烦。自己猜想是因上面的this.Hide()只是隐藏了当前窗口,并没有关闭,所以我在关闭另一个窗口的时候,这个窗口其实还是未关闭的。

解决方法:在窗体的FormClosed事件中关掉所有应用窗口,代码如下:

1
2
3
4
protected virtual void Main_FormClosed(object sender, FormClosedEventArgs e) 
{
Application.Exit();
}

引申:在开发项目时,不管是Web项目还是Form项目,最好所有的窗体都继承基类,这样很多共公的功能就可以在基类中实现了。比如上面的的代码,如果有基类,就不用处理每个窗体的FormClosed事件了,只需处理基类的即可!


相关链接(侵删)

  1. C#winform 点叉叉按钮关闭一form窗体应用后,后台进程仍在运行的解决办法
  2. c#学习:[4]FormClosed事件关闭窗体后事件
  3. Form窗体点击关闭按钮并未关闭进程的解决方法

=================我是分割线=================

欢迎到公众号来唠嗑: