내 프로그램이 종료되지 않는 이유는 무엇입니까?
-
10-07-2019 - |
문제
3 개의 Windows 머신 (데스크탑 Wind .NET 외에도 하나의 COM 구성 요소 (UI 스레드의 모든 작업)를 사용합니다. 종료 후 디버거에 침입하면 Visual Studio는 하나의 스레드와 완전히 빈 콜 스택 만 표시됩니다.
이것을 원인 할 수있는 것은 무엇입니까?
업데이트 : 내 프로세스는 데스크탑에서 종료되었지만 Wince Machines는 아닙니다. 프로세스가 다음 코드로 종료하도록 강요하려고했지만 작동하지 않습니다.
[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을 얻습니다. 따라서 GetCurrentProcess의 데스크탑 버전에 대한 문서는 단순히 -1을 반환한다고 주장하기 때문에 TerminateProcess (-1, 0)를 사용하고 있습니다.
[DllImport("coredll.dll")]
static extern int ExitProcess(IntPtr hProcess);
[DllImport("coredll.dll")]
static extern IntPtr GetCurrentProcess();
처리되지 않은 예외를 던지더라도 그렇게하지는 않습니다.
업데이트 2 : 문제를 일으키는 가장 간단한 프로그램은 COM 객체를 만듭니다.
static void Main()
{
new FastNavLib.MapControl();
}
COM 구성 요소를 사용하는 C ++ 프로그램은이 동작을 나타내지 않으므로 C ++ COM 구성 요소는 조사 할 .NET 프레임 워크와 기괴한 상호 작용이 있어야합니다.
해결책 5
나는 그것을 알아 냈다.
내 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의 Finalizer 스레드에서 호출 할 수 없습니다. 평소와 같이 Microsoft는 문서의 위험을 설명하지 않으며, "다른 스레드로 생성 된 창을 파괴하기 위해 한 스레드에서 DestroyWindow를 사용하지 마십시오"라고 말합니다.
솔루션은 간단했습니다. 프로세스가 종료 될 때 OS가 자동으로 파괴되므로 타이머 창을 전혀 파괴하지 마십시오.
재미있는 사실 : 문제 DestroyWindow
내가 전화하면 발생하지 않습니다 SetTimer(NULL,...)
대신에 SetTimer(gTimerWindow,...)
, 그러나 그러나 그것은 하다 내가 전화하지 않으면 발생합니다 SetTimer
조금도.
다른 팁
애플리케이션에서 여전히 실행중인 스레드가있는 것 같습니다.
메인 스레드를 종료하기 전에 모든 어린이 실을 종료했는지 확인하십시오.
그냥 직감 - 확인하십시오 CoUninitialize()
출구 전에 호출 되나요? 또한 디버거로 나누는 대신 충돌 덤프를 만들고 디버깅하십시오. 이것이 CE에서 어떻게 작동하는지 잘 모르겠지만 그것이 Windows에서 추천 할 것입니다.
앱이 종료되지 않으면 일반적으로 사용자 공간에서 닫을 수없는 핸들이 여전히 열려 있음을 의미합니다. 그리고 그것은 버기 드라이버가 있다는 것을 의미합니다.
보다 이 게시물 세부 사항에 대해.
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);
}