2014-11-16 2 views
1

Я написал программу на C#, которая включает несколько глобальных горячих клавиш и на основе нажатой горячей клавиши, она активирует окна, такие как хром, firefox, блокнот, калькулятор и т. Д. После регистрации глобальных горячих клавиш, У меня есть бесконечный цикл while, который поддерживает приложение. После запуска программы внезапно горячие клавиши перестают работать. Это происходит через несколько часов. После долгого тестирования каждого фрагмента моего кода я нашел проблему. Проблема в том, что внезапно главный поток перестает работать. Программа все еще жива и в памяти. Горячие клавиши, кажется, зарегистрированы в другом потоке, который является живым, и поддерживает работу программы, даже когда основной поток, содержащий цикл while, мертв.C# backgroundworker останавливается сам по себе

Затем я использовал фонового работника и перемещал цикл while в фоном. Это произошло снова, что означает, что фоновая работа внезапно прекратилась, пока горячие клавиши все еще зарегистрированы. Это не мой первый раз, когда я использовал backgroudworker, и я никогда не сталкивался с чем-то подобным, что фоновая работа выходит сама собой.

Когда это происходит, сообщения, подобные этому, появились в выходных окнах 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, 
    }; 

Я признателен за любую помощь. Thanks

+1

У вас был необъяснимый сбой в вашей основной теме, поэтому вы переместили горячие клавиши в «BackgroundWorker»? Похоже, вы должны были диагностировать, что авария в главном потоке. Перемещение к «BackgroundWorker» просто удвоило (по крайней мере) проблему. Тем не менее, я подозреваю, что что-то в вашем горячем ключе нажал обработчик бросает исключение. Положите попытку/поймать его и запишите любое исключение, которое вы получите. –

+0

Кроме того, я бы внимательно посмотрел на ваш обратный вызов «EnumWindows». Выделение памяти для вашего 'StringBuilder' является подозрительным, тем более, что вы назначаете его' lLength', но затем передаете 'lLength + 1' в' GetWindowText'. Это может очень хорошо попытаться перезаписать память, которой у вас нет, и вызвать сбой потока. 'GetWindowTextLength' не зависит от того, включает ли возвращаемое значение нулевой ограничитель. Вполне возможно добавление 1 к возвращаемому значению и использование результата для инициализации вашего 'StringBuilder' решит проблему. –

+0

Спасибо за ваши комментарии. Эта часть кода на самом деле не является моим собственным кодом, и я просто скопировал из Интернета (http://blog.tcx.be/2006/05/getting-list-of-all-open-windows.html). Извините, я должен был процитировать это в моем главном сообщении. Не могли бы вы изменить код и отправить его в качестве ответа, поскольку я не совсем уверен, что вы имеете в виду? Спасибо – NESHOM

ответ

1

Просмотрите код, возможно, ошибка не в том, как вы звоните 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.

Любопытно, что вы не можете поймать ошибку. Если вы исправили то, что я указал выше, и вы все еще получаете ошибку, я бы предположил, что вы ищете не то место.

В частности, нет абсолютно никакой причины, по которой вам нужна отдельная нить для горячих клавиш. Вы должны иметь возможность определять ключи в основной программе, и пока основная программа работает, горячие клавиши будут работать. Добавление потока просто путает проблему.

Помимо этого, я не могу помочь, пока вы не отследите, что именно вызывает проблему. Вам нужно проверить каждое возвращаемое значение из неуправляемых вызовов функций. Вы также должны рассмотреть возможность добавления некоторых протоколов и регистрировать каждое действие. Таким образом, вы можете более легко определить, где происходит ошибка.

+0

Многие благодарны Джим за ваш информативный ответ. Я изменил код на основе ваших комментариев, однако он все равно сбой, когда я нажимаю горячую клавишу (например, Alt + 1). Я не добавил ни одной темы. Я удалил фонового рабочего, как вы рекомендовали. Код, который я запускаю, является упрощенной версией моего основного кода, и я ожидал увидеть лучшие результаты из этого, но, похоже, все хуже. Я загрузил папку решения в свой Dropbox, и мне интересно, можете ли вы быстро просмотреть ее: 'https: // db.tt/3FFRBrpL'. Спасибо вам. – NESHOM

+0

Я забыл упомянуть, что 'Button1' активирует горячие клавиши. Как только они активируются, нажатие 'Alt + 1' делает все горячие клавиши неактивными (из-за сбоя)! 'Button3' просто выполняет метод' ActiveWindows() ', и он работает без каких-либо проблем. – NESHOM

Смежные вопросы