I have written a program in C# that enables several global hotkeys and based on the hotkey that is pressed, it activates a windows like chrome, firefox, notepad, calculator, etc. After registering the global hotkeys, I have a infinite while-loop that keeps the application alive. After running the program, suddenly the hotkeys stop working. This sometime happens after several hours. After long time testing every piece of my code, I found the issue. The issue is that suddenly the main thread stops working. The program is still alive and in memory. The hotkeys seems to be registered in another thread that is alive and keeps the program running even when the main thread that includes the while-loop is dead.
I then used a backgroundworker and moved the while-loop in the backgroundworker. It happened again, meaning that backgroundworker suddenly stopped while the hotkeys are still registered. This is not my first time using backgroudworker and I never came across something like this that backgroundworker exits by itself.
When this happens, messages like this are appeared in the output windows of Visual Studio:
The thread 0x1b24 has exited with code 0 (0x0)
Is there any time-limit for threads so that they will exit after that? Do you have any suggestion on how this is happening and how I can fix it?
For global hotkeys I use the code listed here:
http://stackoverflow.com/a/3654821/3179989
and this is the rest of my code:
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,
};
I appreciate any help. Thanks
Reviewing the code, the error probably isn't in how you're calling
GetWindowTextLength
. You have:So if
GetWindowTextLength
has an error, your function just returns.There is, however, an error in how you're allocating the
StringBuilder
. If you look at the comments on the GetWindowTextLength page, you'll see that the value returned does not include the null terminator. So when you allocate yourStringBuilder
, you're getting it one character too small. Your code should be:If you don't make that change, then it's possible that a call to
GetWindowText
will overwrite your buffer, and that will cause a crash.A potential problem is that your managed prototypes are okay for 32 bit, but not for 64 bit. For example, you have:
That's okay for 32-bit because an
lParam
is 32 bits. But in 64-bit,lParam
is 64 bits. That prototype should be:Similar thing with your
EnumWindows
prototype. It should be:And when you call it, specify
IntPtr.Zero
for the parameter, rather than0
.It's curious that you can't trap the error. If you've fixed the things that I pointed out above and you're still getting the error, I would suggest that you're looking in the wrong place.
In particular, there is absolutely no reason that you need a separate thread for the hot keys. You should be able to define the keys in the main program, and as long as the main program is running, the hot keys will work. Adding a thread just confuses the issue.
Beyond that, I can't be of much more help until you track down exactly what's causing the problem. You need to check every return value from unmanaged function calls. You also should consider adding some logging, and log every action. That way you can more easily determine where the error is occurring.