2016-11-28 5 views
0

Я следил за «Простым окном» tutorial с сайта WinProg.WTSRegisterSessionNotification, вызывающий процесс зависания

Когда код из учебника скомпилирован без библиотеки времени выполнения C, все работает так, как планировалось. Окно создается и визуально отображается пользователю.

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

Однако, когда я связываюсь с библиотекой WTS и добавляю вызов функции WTSRegisterSessionNotification, процесс продолжается на локальном компьютере после закрытия соответствующего окна.

Такое поведение наблюдается только при возврате из WinMain после вызова WTSRegisterSessionNotification.

Я предполагаю, что WTSRegisterSessionNotification создает какой-то рабочий поток, который никогда не будет уведомлен о выходе. Возврат из WinMain, похоже, не приводит к вызову ExitProcess, вероятно, потому, что код компилируется без библиотеки времени выполнения C.

Проблема может быть устранена вызовом ExitProcess перед возвратом из WinMain. Но это не похоже на правильный способ справиться с ситуацией.

Мой вопрос: есть ли функция WTS API, которую я пропускаю, которая должна/должна быть вызвана перед возвратом из WinMain, чтобы убедиться, что процесс завершится?

Пример кода:

#include <windows.h> 
#include <wtsapi32.h> 

const char g_szClassName[] = "myWindowClass"; 

// Step 4: the Window Procedure 
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{ 
    switch (msg) 
    { 
    case WM_WTSSESSION_CHANGE: 
     if (wParam == WTS_SESSION_LOCK) 
      OutputDebugString("current session got locked"); 
     else if (wParam == WTS_SESSION_UNLOCK) 
      OutputDebugString("current session got unlocked"); 
     break; 
    case WM_CLOSE: 
     DestroyWindow(hwnd); 
     break; 
    case WM_DESTROY: 
     WTSUnRegisterSessionNotification(hwnd); 
     PostQuitMessage(0); 
     break; 
    default: 
     return DefWindowProc(hwnd, msg, wParam, lParam); 
    } 
    return 0; 
} 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
        LPSTR lpCmdLine, int nCmdShow) 
{ 
    WNDCLASSEX wc; 
    HWND hwnd; 
    MSG Msg; 

    //Step 1: Registering the Window Class 
    wc.cbSize = sizeof(WNDCLASSEX); 
    wc.style = 0; 
    wc.lpfnWndProc = WndProc; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 
    wc.hInstance = hInstance; 
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 
    wc.lpszMenuName = NULL; 
    wc.lpszClassName = g_szClassName; 
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); 

    if (!RegisterClassEx(&wc)) 
    { 
     MessageBox(NULL, "Window Registration Failed!", "Error!", 
        MB_ICONEXCLAMATION | MB_OK); 
     return 0; 
    } 

    // Step 2: Creating the Window 
    hwnd = CreateWindowEx(
     WS_EX_CLIENTEDGE, 
     g_szClassName, 
     "The title of my window", 
     WS_OVERLAPPEDWINDOW | WS_VISIBLE, 
     CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, 
     NULL, NULL, hInstance, NULL); 

    if (hwnd == NULL) 
    { 
     MessageBox(NULL, "Window Creation Failed!", "Error!", 
        MB_ICONEXCLAMATION | MB_OK); 
     return 0; 
    } 

    if (!WTSRegisterSessionNotification(hwnd, NOTIFY_FOR_THIS_SESSION)) 
    { 
     MessageBox(NULL, "Register Session Notification Failed!", "Error!", 
        MB_ICONEXCLAMATION | MB_OK); 
     return 0; 
    } 

    ShowWindow(hwnd, nCmdShow); 
    UpdateWindow(hwnd); 

    // Step 3: The Message Loop 
    while (GetMessage(&Msg, NULL, 0, 0) > 0) 
    { 
     TranslateMessage(&Msg); 
     DispatchMessage(&Msg); 
    } 
    return Msg.wParam; 
} 

Компилятор командной строки:

/GS- /TC /GL /analyze- /W4 /Gy /Zc:wchar_t /Gm- /O1 /Ob2 /Fd"Release\vc100.pdb" /fp:fast /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /errorReport:prompt /WX- /Zc:forScope /GR- /Gd /Oy /Oi /MD /Fa"Release\" /nologo /Zl /Fo"Release\" /Os 

Linker Командная строка:

/OUT:"C:\Users\treintje\Documents\Visual Studio 2015\Projects\sample\Release\sample.exe" /MANIFEST:NO /LTCG /NXCOMPAT /PDB:"C:\Users\treintje\Documents\Visual Studio 2015\Projects\sample\Release\sample.pdb" /DYNAMICBASE:NO "kernel32.lib" "user32.lib" "wtsapi32.lib" /ALLOWISOLATION /MACHINE:X86 /ENTRY:"WinMain" /OPT:REF /INCREMENTAL:NO /PGD:"C:\Users\treintje\Documents\Visual Studio 2015\Projects\sample\Release\sample.pgd" /SUBSYSTEM:WINDOWS /MANIFESTUAC:NO /ManifestFile:"Release\sample.exe.intermediate.manifest" /OPT:ICF /ERRORREPORT:PROMPT /NOLOGO /NODEFAULTLIB /TLBID:1 
+0

Вы пытались вызвать 'WTSUnRegisterSessionNotification' перед' DestroyWindow'? –

+0

вы должны всегда вызывать 'ExitProcess' (если вы не используете CRT) - и я предлагаю протестировать вашу программу на win 10 (1607) без' WTSUnRegisterSessionNotification' - это выход из процесса, если вы не вызываете 'Exitprocess' – RbMm

ответ

1

Проблему можно было бы избежать, вызвав ExitProcess перед возвращением из WinMain. Но это не похоже на правильный способ справиться с ситуацией.

процесс выхода MUST прямого или косвенного вызова ExitProcess. это абсолютно правильный путь и обязательный. когда вы используете CRTWinMain не действительно точка входа в заявку - она ​​называется от WinMainCRTStartup, которая называется ExitProcess. если вы не используете CRT - вы MUST прямого самовывозом вызова ExitProcess

начинаются с победы 10 (на 1607 сборки точно, но может быть и в предыдущих версиях) существует новая функция - «Параллельный загрузчик» для длл нагрузки. так что теперь, когда какая-либо dll, загруженная в ваш процесс (кроме ntdll, kernel32 и kernelbase), создает рабочие потоки для загрузки DLL в «параллельные».так что даже если вы запускаете очень просто программу - скажем, один MessageBox в WinMain, но не вызывайте Exitprocess - ваш процесс не выйдет, но все еще жив 30-60 секунд - рабочие потоки загрузчика dll (LdrpWorkCallback) имеют 30 секундный тайм-аут простоя и после этого выхода

+0

Принимаются как ответ, это подтверждение, которое я искал в некоторой степени. Было бы очень полезно, если бы вы могли обратиться к какой-либо документации, в которой содержится более подробная информация об использовании ExitProcess при компиляции кода Win32 без библиотеки времени выполнения C. – treintje

+0

@treintje - я не знаю о документации, но думаю. когда процесс выхода? когда 1) все нитки выходят или 2) какой-то поток в вызове процесса ExitProcess 3) любой вызов потока TerminateProcess (это плохой случай) - но 1) мы не можем управлять. даже если у нас есть однопоточная система приложений, система может создавать рабочие потоки в нашем процессе (начиная с победы 10 - почти всегда) - поэтому мы не можем быть уверены, что только один поток в процессе. – RbMm

+0

, когда управление возвратом точки входа exe - мы возвращаемся к kernel32.BaseThreadInitThunk, который вызвал ExitThread, но не ExitProcess (система не может вызвать ExitProcess здесь - может быть, нам не нужен процесс завершения, а другой поток существует в процессе, даже если первый выход потока - процесс не должен умирать всегда после этого) - так что вариант для завершения процесса? только 2) нужно вызвать ExitProcess. – RbMm

Смежные вопросы