Почему моя программа не заканчивается? - PullRequest
2 голосов
/ 29 октября 2009

У меня есть приложение .NET Compact Framework, которое может работать на трех компьютерах с Windows (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();
}

Также должны быть API-интерфейсы ExitProcess () и GetCurrentProcess (), подобные приведенным ниже, но если я пытаюсь вызвать их, я получаю исключение 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, такого поведения не проявляют, поэтому мой компонент COM на C ++ должен иметь какое-то странное взаимодействие с платформой .NET, которую я буду исследовать.

Ответы [ 5 ]

3 голосов
/ 02 ноября 2009

похоже, что в вашем приложении все еще работают потоки.

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

1 голос
/ 07 июня 2010

Ваш 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);
} 
1 голос
/ 30 октября 2009

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

См. этот пост о деталях.

1 голос
/ 29 октября 2009

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

0 голосов
/ 08 июня 2010

Я как-то разобрался.

Мой 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.

...