Pregunta

Tengo una aplicación .NET Compact Framework que puede ejecutarse en tres máquinas Windows (Windows de escritorio y dos máquinas WinCE) y en los dispositivos WinCE, el proceso nunca termina al salir, incluso si llamo Application.Exit (). Además de .NET, usa un componente COM (que hace todo en el hilo de la interfaz de usuario). Si me meto en el depurador después de salir, Visual Studio muestra solo un hilo y una pila de llamadas completamente en blanco.

¿Qué podría causar esto?

Actualización: mi proceso finaliza en el escritorio pero no en las máquinas WinCE. Intenté forzar el proceso para que terminara con el siguiente código, pero no funciona:

[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();
}

También se supone que hay API ExitProcess () y GetCurrentProcess () como las siguientes, pero si intento llamarlas, obtengo EntryPointNotFoundException. Por lo tanto, estoy usando TerminateProcess (-1, 0) porque la documentación para la versión de escritorio de GetCurrentProcess afirma que simplemente devuelve -1.

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

Incluso lanzar una excepción no controlada no lo hará.

Actualización 2: el programa más simple que causa el problema simplemente crea el objeto COM.

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

Los programas C ++ que usan el componente COM no exhiben este comportamiento, por lo que mi componente COM C ++ debe tener una interacción extraña con el marco .NET que investigaré.

¿Fue útil?

Solución 5

Lo resolví de alguna manera.

Mi objeto COM se configura para recibir mensajes WM_TIMER a través de una ventana oculta. Básicamente:

// 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);

(Los expertos pueden señalar que no necesito una ventana de temporizador para recibir mensajes de temporizador; el último parámetro de SetTimer se puede establecer en una función de devolución de llamada. De hecho, si uso una devolución de llamada en su lugar de una ventana de temporizador, el problema desaparece. Sin embargo, tuve que usar una ventana de temporizador para solucionar un error extraño en WinCE en el que SetTimer (NULL, ...) puede causar WM_TIMER mensajes recibidos por alguien que llama a PeekMessage () .)

Ahora, cuando se destruye el último objeto COM que usa el temporizador, el temporizador y la ventana del temporizador también se destruyen:

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

Desafortunadamente, DestroyWindow () nunca regresa. Supongo que hay algún tipo de punto muerto en DestroyWindow , aunque no está claro por qué la pila de llamadas está en blanco cuando hago una pausa en Visual Studio. Quizás porque el objeto COM se destruye automáticamente en lugar de Marshal.ReleaseComObject () , se destruye en el subproceso finalizador y no se puede llamar a DestroyWindow desde el subproceso finalizador en WinCE . Como de costumbre, Microsoft no explica el peligro en su documentación, declarando solo "No use DestroyWindow en un hilo para destruir una ventana creada por un hilo diferente".

La solución fue simple: no destruya la ventana del temporizador en absoluto, ya que el sistema operativo la destruye automáticamente cuando finaliza el proceso.

Dato curioso: el problema con DestroyWindow no ocurre si llamo a SetTimer (NULL, ...) en lugar de SetTimer (gTimerWindow, ... ) , pero ocurre si no llamo a SetTimer en absoluto.

Otros consejos

parece que todavía tiene algunos subprocesos ejecutándose en su aplicación.

Asegúrese de haber terminado cada subproceso hijo antes de salir del principal.

Solo una corazonada: asegúrese de que se llame a CoUninitialize () antes de salir. Además, en lugar de entrar en un depurador, cree un volcado por caída y depúrelo. No estoy seguro de cómo funciona esto en CE, pero eso es lo que recomendaría en Windows.

Si una aplicación no sale, eso generalmente significa que todavía hay un identificador abierto que no se puede cerrar desde el espacio del usuario. Y eso significa que hay un controlador con errores.

Consulte esta publicación sobre detalles.

Su objeto COM está creando un hilo en el fondo y ese hilo no está terminando. Es probable que esto se deba a que el objeto COM no se libera en su código.

Intente llamar a Marshal.ReleaseComObject antes de salir, para que su aplicación de prueba simple tenga este aspecto:

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);
} 
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top