在新进程中运行时窗口标题中的“无响应”
Posted
技术标签:
【中文标题】在新进程中运行时窗口标题中的“无响应”【英文标题】:"Not Responding" in window title when running in new process 【发布时间】:2013-02-12 06:15:34 【问题描述】:我有一个长时间运行的方法,必须在 UI 线程上运行。 (Devex - gridView.CopyToClipboard()
)
我不需要 UI 在复制时做出响应,我添加了一个启动屏幕,这样用户就不会觉得无聊了。
当我运行这个程序时,一切都很好。
当我运行另一个程序时,问题就开始了,而该程序又启动了一个新进程并在其上运行该程序。 复制标题几秒钟后显示(未响应)并且鼠标光标显示忙碌,它当然会在几秒钟内清除但我想摆脱它,因为它给用户一种误解的感觉程序有问题。
有什么方法可以设置我创建的进程的“超时”吗?
编辑:
主程序调用如下代码:
fillsProcess = new Process();
fillsProcess.StartInfo.FileName = Application.ExecutablePath;
fillsProcess.Start();
在fillsProcess中,当点击某个按钮时,会调用以下代码:
gridViewToCopy.CopyToClipboard();
这行代码需要一些时间来处理,几秒钟后,fillsProcess 的窗口看起来没有响应,因为此方法在 UI 线程上运行..
编辑第二次:
显然(而且真的很容易理解)
gridViewToCopy.CopyToClipboard();
不是导致此问题的唯一方法。许多 Devex 方法必须在 UI 线程上运行(例如数据排序、数据过滤)
感谢任何提供特定解决方案(有效或无效)的人,但我原来的问题再次出现:
有什么办法可以改变超时时间或以某种方式控制整个“无响应”惨败?
【问题讨论】:
启动画面是不同的程序,当您开始复制时会启动?启动画面的标题? 这看起来很有用,更具体地说是 Jeffrey Hantin 的回答:***.com/questions/1691251/… 您可能希望通过 Developer Express 解决此问题。 我可以设想 DevExpress 服务请求。问:“当我将一万行复制到剪贴板时,我的程序看起来坏了” 答:“你的程序坏了”。Application.DoEvents()
有什么作用吗?我知道它应该有效,但从来没有给我一致的结果。
【参考方案1】:
你可以使用DisableProcessWindowsGhosting
win32函数:
[DllImport("user32.dll")]
public static extern void DisableProcessWindowsGhosting();
这实际上并不能阻止窗口冻结,但可以阻止标题中的“Not Respongind”文本。
【讨论】:
谢谢!这实际上回答了我的问题:) 有没有等价于EnableProcessWindowsGhosting
的东西?
@colmde 不,没有这样的功能。没有办法让它恢复正常。该进程应终止并重新启动!【参考方案2】:
恐怕最简单的解决方案是在 for 循环中创建自己的 CopyToClipboard()
,时不时地执行 Application.DoEvents
,这样可以保持 ui 线程响应。
我猜 DevExpress 的大多数许可证都有可用的源代码,所以如果有的话,你可能大部分都可以复制粘贴。
既然您知道数据,您可能可以创建一个比 DevExpress 使用的泛型简单得多的过程。
像这样:
const int feedbackinterval = 1000;
private void btnCopy_Click(object sender, EventArgs e)
StringBuilder txt2CB = new StringBuilder();
int[] rows = gridView1.GetSelectedRows();
if (rows == null) return;
for (int n = 0; n < rows.Length; n++)
if ((n % feedbackinterval) == 0) Application.DoEvents();
if (!gridView1.IsGroupRow(rows[n]))
var item = gridView1.GetRow(rows[n]) as vWorkOrder;
txt2CB.AppendLine(String.Format("0\t1\t2",
item.GroupCode, item.GroupDesc, item.note_no??0));
Clipboard.SetText(txt2CB.ToString());
【讨论】:
【参考方案3】:这是因为您在主应用程序线程中同步调用了一个长时间运行的方法。由于您的应用程序正忙,它不会响应来自 Windows 的消息,并在完成之前被标记为(未响应)。
要处理这个问题,请异步复制,例如使用任务作为一种最简单的解决方案。
Task task = new Task(() =>
gridView.Enabled = false;
gridView.CopyToClipboard();
gridView.Enabled = true;
);
task.Start();
禁用您的网格,这样任何人都无法更改 GUI 中的值。 应用程序的其余部分保持响应(可能有副作用!)。
【讨论】:
请阅读 cmets。有人建议了他的方法,但没有帮助。 当然这无济于事,因为对set_Enable
、CopyToClopboard
和set_Enable
的调用只不过是从另一个线程执行的 Windows 消息(因为主线程必须是 Windows 应用程序的 STA)。这些方法在窗口线程内执行,只要这三个调用处于挂起状态,就无法响应其他消息。结果将与问题中描述的相同。
此代码可能会引发跨线程 UI 异常。最好在this.BeginInvoke( () => /* code here */ );
中完成上述项目以阻止这种情况发生。
但是如果您在控件上调用 BeginInvoke,则委托在 UI 线程上执行,因此您又回到了原点。【参考方案4】:
您可以启动隐藏的进程,然后检查是否响应并在完成后将其重新显示....您的初始屏幕将显示其仍在“响应”。
Process proc = new Process();
proc.StartInfo.FileName = "<Your Program>.exe"
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
编辑: 您还可以创建一个 Timer 事件来监视其他进程并滚动您自己的超时逻辑
DateTime dStartTime = DateTime.Now;
TimeSpan span = new TimeSpan(0, 0, 0);
int timeout = 30; //30 seconds
private void timer1_Tick(Object myObject, EventArgs myEventArgs)
while (span.Seconds < timeout)
Process[] processList = Process.GetProcessesByName("<YourProcess.exe>");
if (processList.Length == 0)
//process completed
timer1.Stop();
break;
span = DateTime.Now.Subtract(dStartTime);
if (span.Seconds > timeout)
Process[] processList = Process.GetProcessesByName("<YourProcess.exe>");
//Give it one last chance to complete
if (processList.Length != 0)
//process not completed
foreach (Process p in processList)
p.Kill();
timer1.Stop();
编辑2
您也可以使用 pInvoke "ShowWindow" 来完成窗口启动后的隐藏和显示
private const int SW_HIDE = 0x00;
private const int SW_SHOW = 0x05;
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
【讨论】:
对不起。有问题的方法是由单击按钮触发的,而不是在进程启动时触发的。 @E.T.你有一些代码来显示到底发生了什么吗?【参考方案5】:有几种可能的方法
在操作期间隐藏主窗体 以某种方式克隆/序列化控件并将其传递给具有另一个 UI 调度程序的线程 通过gridView.GetSelectedCells()
获取选中的单元格,然后将其内容异步放到剪贴板中
如果您将 GridView
库上传到某个地方会更有帮助,以便我们可以查看内部。
【讨论】:
感谢您的回复,但请查看我的编辑。每次用户执行操作时,我都无法真正隐藏网格:-/【参考方案6】:我不清楚您的用户是否需要看到“无响应”的屏幕。如果没有必要,您可以尝试在该应用程序的主线程关闭后让该应用程序在后台运行;或者您可以最小化应用程序。
如果有必要查看应用程序并使其看起来正在运行,您能否对“复制到剪贴板”功能进行分段,使其成为线程并接受数组或网格视图和索引范围。这样做的好处是您的从属进程上的主线程永远不会挂起。缺点是人们不喜欢在 C# 中使用线程和委托。
【讨论】:
不能那样做。如前所述,该方法不是我的,必须在 UI 线程上运行。 如果我没看错,你的方法的名字是必不可少的 CopyGridToClipboard(或者是 CopyGridToClipBoard)。我会自己动手,因为这听起来是一种非常简单的方法;一旦我在静态类(或实例化)中将自己的静态方法作为静态方法推出,我会将其放入新线程中。如果该方法执行您共享的名称未暗示的某些事情,那可能是有用的信息……不过,听起来您仍然可能喜欢“自己动手”的方法;-)【参考方案7】:好的,您描述的“无响应”和窗口伪影只是在您的 UI 线程上运行长期活动的症状。 UI 线程被阻塞,因此 UI 被冻结。这是无法避免的。老实说,您的应用程序看起来和它一样响应迅速只是“幸运”。
据我所知,这里描述的每一个解决方法都只是一个 hack,用来掩盖你的 UI 线程被冻结的事实。不要这样做。修复您的程序,以免 UI 线程被冻结。
问问自己:我的用户真的需要复制此视图中的所有行吗?可以以某种方式过滤数据以限制行吗?如果没有,有一个名为 MaxRowCopyCount 的属性可以限制复制的行数 - 可以在不破坏工作流程的情况下利用它吗?
最后,如果所有其他方法都失败了,是否可以使用其他介质(可能是中间文件),以便在后台线程上将数据复制到其中?
【讨论】:
【参考方案8】:IsHungAppWindow 中记录的超时无法更改。不要使用全局状态来管理本地问题。
您必须优化导致无响应的部分。例如使用缓存、虚拟网格(DevExpress 称之为“服务器模式”)、分页、将排序委托给执行数据库查询(使用数据库索引)而不是内存排序(无索引)的 ibindinglistview 过滤器或implement IAsyncOperation on your clipboard data所以你只需要在用户粘贴时填充数据。
【讨论】:
以上是关于在新进程中运行时窗口标题中的“无响应”的主要内容,如果未能解决你的问题,请参考以下文章
Shiny App 问题,ShinyDirButton 无响应
我无法让 OpenCV 中的 CV2.waitKey 正常工作。运行 waitKey 后代码无响应