Por que meu programa não irá terminar?
-
10-07-2019 - |
Pergunta
Eu tenho um aplicativo .NET Compact Framework que podem executado em três máquinas Windows (Windows Desktop e duas máquinas WinCE) e nos dispositivos WinCE, o processo nunca termina na saída, mesmo que eu chamo Application.Exit (). Além NET, que utiliza um componente COM (que faz tudo no thread UI). Se eu invadir o depurador após exitting, programas do Visual Studio apenas uma linha e uma pilha de chamadas completamente em branco.
O que poderia causar isso?
Update: O meu processo está a terminar no ambiente de trabalho, mas não as máquinas WinCE. Tentei forçar o processo a terminar com o seguinte código, mas ele não 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();
}
Há também é suposto ser ExitProcess () e GetCurrentProcess () APIs como o seguinte, mas se eu tentar chamá-los, recebo EntryPointNotFoundException. Portanto, eu estou usando TerminateProcess (-1, 0), porque a documentação para a versão desktop do reivindicações GetCurrentProcess que ele simplesmente retorna -1.
[DllImport("coredll.dll")]
static extern int ExitProcess(IntPtr hProcess);
[DllImport("coredll.dll")]
static extern IntPtr GetCurrentProcess();
Mesmo jogando uma exceção não tratada não vai fazê-lo.
Update 2:. O programa mais simples que faz com que o problema simplesmente cria o objeto COM
static void Main()
{
new FastNavLib.MapControl();
}
C ++ programas que usam o componente COM não apresentam esse comportamento, por isso a minha C ++ COM componente deve ter alguma interação bizarra com o .NET framework que vou investigar.
Solução 5
Eu meio-de descobri-lo.
Os meus COM objeto se levanta para obter mensagens WM_TIMER
através de uma janela oculta. Basicamente:
// 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);
(Experts pode apontar Eu não preciso de uma janela temporizador para receber mensagens do temporizador -. O último parâmetro de SetTimer
pode ser configurado para uma função de retorno Na verdade, se eu usar uma chamada de retorno em vez de uma janela timer, o problema desaparece! no entanto, eu tive que usar uma janela temporizador para o trabalho em torno de um bug estranho no WinCE em que SetTimer(NULL,...)
pode causar mensagens WM_TIMER
a recebida por alguém que as chamadas PeekMessage()
.)
Agora, quando o último objeto COM que usa o timer é destruído, a janela do temporizador e temporizador são também destruíram:
KillTimer(gTimerWindow, gTimerID);
DestroyWindow(gTimerWindow);
Infelizmente, DestroyWindow()
nunca retorna. Eu supor que há algum tipo de impasse no DestroyWindow
, embora não seja claro por que a pilha de chamadas está em branco quando faço uma pausa no Visual Studio. Talvez porque o objeto COM é destruído automaticamente em vez de por Marshal.ReleaseComObject()
, ele é destruído no segmento finalizador, e DestroyWindow
não pode ser chamado a partir do segmento finalizador no WinCE. Como de costume Microsoft não especifica o perigo em sua documentação, afirmando apenas "Não use DestroyWindow em um segmento para destruir uma janela criada por um segmento diferente."
A solução era simples:. Não destroem a janela temporizador em tudo, como o sistema operacional destrói automaticamente quando o processo é encerrado
Fun fato:. O problema com DestroyWindow
não ocorre se eu chamo SetTimer(NULL,...)
vez de SetTimer(gTimerWindow,...)
, mas não ocorrer se eu não chamo SetTimer
em tudo
Outras dicas
Parece que você tem alguns segmentos ainda em execução na sua aplicação.
Certifique-se de terminar cada segmento de criança antes de sair o principal.
Apenas um palpite - Certifique-se CoUninitialize()
é chamado antes de sair?
Além disso, em vez de invadir um depurador criar um despejo de memória e depurar isso. Não sei como isso funciona na CE, mas isso é O que foi que eu recomendo no Windows.
Se um aplicativo não sair Isso normalmente significa que há uma alça ainda em aberto que não pode ser fechado a partir do espaço do usuário. E isso significa que há um motorista de buggy.
este post sobre detalhes.
Seu objeto COM é criar um thread em segundo plano e esse segmento não está sendo encerrado. É provável que isso é porque o objeto COM não está sendo lançado em seu código.
Tente chamar Marshal.ReleaseComObject antes de sair, para que o seu aplicativo de teste simples seria algo como isto:
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);
}