C# 后台工作线程自行停止
本文关键字:线程 后台 工作 | 更新日期: 2023-09-27 18:30:32
我用 C# 编写了一个程序,该程序启用了多个全局热键,并根据按下的热键激活 chrome、Firefox、记事本、计算器等窗口。注册全局热键后,我有一个无限的while循环,可以使应用程序保持活动状态。运行程序后,热键突然停止工作。这有时会在几个小时后发生。经过长时间测试我的每一段代码,我发现了问题所在。问题是主线程突然停止工作。该程序仍然活着并在内存中。热键似乎在另一个处于活动状态的线程中注册,即使包含 while-loop 的主线程已死,也能保持程序运行。
然后,我使用后台工作线程并在后台工作线程中移动了 while 循环。它再次发生,这意味着后台工作者突然停止,而热键仍在注册。这不是我第一次使用backgroudworker,我从来没有遇到过后台worker自己退出这样的事情。
发生这种情况时,像这样的消息会出现在Visual Studio的输出窗口中:
The thread 0x1b24 has exited with code 0 (0x0)
线程是否有任何时间限制,以便它们在此之后退出?您对这种情况是如何发生的以及我如何解决它有什么建议吗?
对于全局热键,我使用此处列出的代码:
http://stackoverflow.com/a/3654821/3179989
这是我代码的其余部分:
public static void HotKeyPressed(object sender, HotKeyEventArgs e)
{
string PressedHotkey = e.Modifiers.ToString() + " " + e.Key.ToString();
switch (PressedHotkey)
{
case "Alt D1":
mActivateWindow(mEnumApplications.Chrome);
break;
case "Alt D3":
mActivateWindow(mEnumApplications.CintaNotes);
break;
default:
break;
}
}
private void button1_Click(object sender, EventArgs e)
{
bgWkrHotkey.WorkerSupportsCancellation = true;
bgWkrHotkey.WorkerReportsProgress = true;
bgWkrHotkey.RunWorkerAsync();
}
private void bgWkrHotkey_DoWork(object sender, DoWorkEventArgs e)
{
mHotKeyManager.RegisterHotKey(Keys.Oemtilde, KeyModifiers.Alt);
mHotKeyManager.RegisterHotKey(Keys.D1, KeyModifiers.Alt);
mHotKeyManager.RegisterHotKey(Keys.D3, KeyModifiers.Alt);
mHotKeyManager.HotKeyPressed += new EventHandler<HotKeyEventArgs>(HotKeyPressed);
while (true)
{
Thread.Sleep(50);
}
}
//@@@@@@@@@@@@@@@@@@@@ DLL IMPORTS @@@@@@@@@@@@@@@@@@@@
#region DLL IMPORTS
[DllImport("User32.dll")]
private static extern IntPtr SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
[DllImport("USER32.DLL")]
static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
[DllImport("USER32.DLL")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("USER32.DLL")]
static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("USER32.DLL")]
static extern IntPtr GetShellWindow();
#endregion DLL IMPORTS
public static IDictionary<IntPtr, string> mGetOpenWindows()
{
IntPtr ipShellWindow = GetShellWindow();
Dictionary<IntPtr, string> ipWindows = new Dictionary<IntPtr, string>();
EnumWindows(delegate(IntPtr hWnd, int lParam)
{
if (hWnd == ipShellWindow) return true;
//if (!IsWindowVisible(hWnd)) return true;
int lLength = GetWindowTextLength(hWnd);
if (lLength == 0) return true;
StringBuilder lBuilder = new StringBuilder(lLength);
GetWindowText(hWnd, lBuilder, lLength + 1);
ipWindows[hWnd] = lBuilder.ToString();
return true;
}, 0);
return ipWindows;
}
public static string mGetActiveWindowTitle()
{
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return "";
}
public static bool mActivateWindow(IntPtr ipHandle, string strWindowTitle)
{
StringBuilder Buff = new StringBuilder(256);
SetForegroundWindow(ipHandle);
Stopwatch swTimeout = new Stopwatch();
swTimeout.Start();
while (swTimeout.Elapsed < TimeSpan.FromSeconds(2))
{
ipHandle = GetForegroundWindow();
if ((GetWindowText(ipHandle, Buff, 256) > 0) && (Buff.ToString().ToLower().Contains(strWindowTitle.ToLower())))
return true;
else
{
SetForegroundWindow(ipHandle);
Thread.Sleep(50);
}
}
swTimeout.Stop();
return false;
}
public static bool mActivateWindow(mEnumApplications enumApp)
{
string strWindowTitle = "";
switch (enumApp)
{
case mEnumApplications.Chrome:
strWindowTitle = "Google Chrome";
break;
case mEnumApplications.CintaNotes:
strWindowTitle = "CintaNotes";
break;
default:
break;
}
IntPtr ipHandle = IntPtr.Zero;
string strExactTitle = "";
StringBuilder Buff = new StringBuilder(256);
foreach (KeyValuePair<IntPtr, string> ipWindow in mGetOpenWindows())
{
ipHandle = ipWindow.Key;
strExactTitle = ipWindow.Value;
if (strExactTitle.ToLower().Contains(strWindowTitle.ToLower()))
if (mActivateWindow(ipHandle, strWindowTitle))
return true;
}
return false;
}
public enum mEnumApplications
{
Null,
Chrome,
CintaNotes,
};
我感谢任何帮助。谢谢
查看代码,错误可能不在于您如何调用GetWindowTextLength
。你有:
int lLength = GetWindowTextLength(hWnd);
if (lLength == 0) return true;
因此,如果GetWindowTextLength
有错误,您的函数只会返回。
但是,您如何分配StringBuilder
存在错误。如果您查看 GetWindowTextLength 页面上的注释,您将看到返回的值不包括空终止符。所以当你分配你的StringBuilder
时,你得到一个字符太小了。您的代码应该是:
StringBuilder lBuilder = new StringBuilder(lLength+1); // <-- changed to lLength+1
GetWindowText(hWnd, lBuilder, lLength + 1);
如果不进行该更改,则对 GetWindowText
的调用可能会覆盖缓冲区,从而导致崩溃。
一个潜在的问题是,托管原型适用于 32 位,但不适用于 64 位。例如,您有:
delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
这对于 32 位没关系,因为lParam
是 32 位。但在 64 位中,lParam
是 64 位。该原型应该是:
delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
与你的EnumWindows
原型类似。它应该是:
[DllImport("USER32.DLL")]
static extern bool EnumWindows(EnumWindowsProc enumFunc, IntPtr lParam);
调用它时,请为参数指定IntPtr.Zero
,而不是0
.
奇怪的是,您无法捕获错误。如果您已经修复了我上面指出的内容,但仍然遇到错误,我建议您找错地方。
特别是,绝对没有理由需要热键的单独线程。您应该能够在主程序中定义键,只要主程序正在运行,热键就会起作用。添加线程只会混淆问题。
除此之外,在您准确追踪导致问题的原因之前,我无法提供更多帮助。您需要检查非托管函数调用的每个返回值。您还应考虑添加一些日志记录,并记录每个操作。这样,您可以更轻松地确定错误发生的位置。