2009-10-29 6 views
2

У меня есть приложение .NET Compact Framework, которое может работать на трех машинах Windows (настольные окна и две машины WinCE) и на устройствах WinCE, процесс никогда не заканчивается при выходе, даже если я вызываю Application.Exit(). Помимо .NET, он использует один COM-компонент (который делает все в потоке пользовательского интерфейса). Если я выйду из отладчика после выхода, Visual Studio покажет только один поток и полностью пустой стек вызовов.Почему моя программа не заканчивается?

Что может быть причиной этого?

Обновление: Мой процесс завершается на рабочем столе, но не на компьютерах WinCE. Я пытался заставить процесс прекратить с помощью следующего кода, но он не работает:

[DllImport("coredll.dll")] 
static extern int TerminateProcess(IntPtr hProcess, uint uExitCode); 

static public void ExitProcess() 
{ 
    if (Platform.IsWindowsCE) 
     TerminateProcess(new IntPtr(-1), 0); 
    Application.Exit(); 
} 

Там также должны быть ExitProcess() и GetCurrentProcess() API-интерфейсы, подобные следующим, но если я пытаюсь позвоните им, я получаю EntryPointNotFoundException. Поэтому я использую TerminateProcess (-1, 0), потому что документация для настольной версии GetCurrentProcess утверждает, что она просто возвращает -1.

[DllImport("coredll.dll")] 
static extern int ExitProcess(IntPtr hProcess); 
[DllImport("coredll.dll")] 
static extern IntPtr GetCurrentProcess(); 

Даже выброс необработанного исключения не сделает этого.

Обновление 2: простейшая программа, которая вызывает проблему, просто создает объект COM. Программы

static void Main() 
{ 
    new FastNavLib.MapControl(); 
} 

C++, которые используют COM-компонент не проявляют такое поведения, поэтому мой C++ COM компонент должен иметь какое-то причудливое взаимодействие с платформой .NET, который я буду исследовать.

ответ

0

Я бы-понял.

Мой объект COM устанавливает себя, чтобы получить WM_TIMER сообщений через скрытое окно. В основном:

// Register window class 
WNDCLASS wc; 
memset(&wc, 0, sizeof(wc)); 
wc.lpfnWndProc = &WinCeTimerProc; 
wc.lpszClassName = _T("FastNavTimerDummyWindow"); 
// Create window 
gTimerWindow = CreateWindow(wc.lpszClassName, wc.lpszClassName, 
    WS_OVERLAPPED, 0, 0, 100, 100, NULL, NULL, GetModuleHandle(NULL), NULL); 
gTimerID = SetTimer(gTimerWindow, 88, gTimerIntervalMs, NULL); 

(Эксперты могли бы указать, что я не нужен окно таймера для приема сообщений таймера - последний параметр SetTimer может быть установлен на функцию обратного вызова.Действительно, если я использую обратный вызов вместо окна таймера, проблема исчезает! Тем не менее, я должен был использовать окно таймера для работы вокруг странной ошибки в WinCE, в котором SetTimer(NULL,...) может вызвать WM_TIMER сообщения, полученные кем-то, что вызывает PeekMessage().)

Теперь, когда последний COM объект, который использует таймер уничтожается , таймер и окно таймера также уничтожены:

KillTimer(gTimerWindow, gTimerID); 
DestroyWindow(gTimerWindow); 

К сожалению, DestroyWindow() никогда не возвращается. Я предполагаю, что существует какой-то тупик в DestroyWindow, хотя непонятно, почему стек вызовов пуст, когда я приостанавливаюсь в Visual Studio. Возможно, потому, что объект COM уничтожается автоматически, а не Marshal.ReleaseComObject(), он уничтожается в потоке финализатора, а DestroyWindow не может быть вызван из потока финализатора на WinCE. Как обычно, Microsoft не указывает на опасность в своей документации, заявив только «Не используйте DestroyWindow в одном потоке, чтобы уничтожить окно, созданное другим потоком».

Решение было простым: не разрушать окно таймера вообще, так как ОС автоматически уничтожает его, когда процесс завершается.

Забавный факт: проблема с DestroyWindow не происходит, если я называю SetTimer(NULL,...) вместо SetTimer(gTimerWindow,...), но делает происходит, если я не позвоню SetTimer вообще.

1

Просто догадка - убедитесь, что CoUninitialize() вызывается перед выходом? Кроме того, вместо того, чтобы входить в отладчик, создайте аварийный дамп и отлаживайте это. Не уверен, как это работает на CE, но это то, что я рекомендовал бы в Windows.

+0

Возможно, что-то попробовать, когда мои приложения на C++ не прекращаются (что происходит раздражающе часто), но .NET-приложениям не нужно это требовать. – Qwertie

+0

Как вы делаете аварийный дамп и отлаживаете его, и почему это будет лучше, чем использование отладчика VS? – Qwertie

+0

http://support.microsoft.com/kb/241215 – psychotik

1

Если приложение не выходит из него, это означает, что есть еще один открытый ключ, который нельзя закрыть из пользовательского пространства. А это значит, что есть багги.

См. this post о подробностях.

+0

Единственное специальное устройство, используемое приложением, это COM-порт, и процесс не завершается, даже если я не открываю COM-порт. – Qwertie

+0

Ой, подождите, я также играю звуки с waveOutOpen и т. Д. Полагаю, я мог бы изучить это как причину. – Qwertie

+0

Нет, комментирование вызова waveOutOpen не помогает. – Qwertie

3

похоже, что у вас есть потоки, все еще запущенные в вашем приложении.

Перед тем, как выйти из основного, убедитесь, что вы закончили каждый дочерний поток.

+0

Как я уже упоминал, мое однопоточное приложение. Я написал весь код в приложении, включая компонент COM, поэтому должен знать. Поскольку процесс успешно завершается на рабочем столе, возможно, существует некоторая разница между «правилами завершения процесса» на WinXP и WinCE? – Qwertie

+0

нет, нет никаких других правил. Компонент COM создает поток и этот поток не прерывается. – ctacke

1

Ваш COM-объект создает поток в фоновом режиме и этот поток не завершается. Вероятно, это связано с тем, что объект COM не освобождается в коде.

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

static void Main() 
{ 
    // create the COM object 
    var obj = new FastNavLib.MapControl(); 

    // simulate doing stuff 
    Thread.Sleep(1000); 

    // release the COM object 
    Marshal.ReleaseComObject(obj); 
} 
+0

Мой COM-объект не создает нити. Я также подтвердил, что COM-объект выпущен нормально, используя OutputDebugString в конце метода ATL FinalRelease(). Теперь я узнал, что проблема почему-то вызвана созданием окна для получения сообщений WM_TIMER, поэтому я опубликую ответ об этом. – Qwertie