2013-03-24 11 views

Odpowiedz

6
#ifndef MAKEULONGLONG 
#define MAKEULONGLONG(ldw, hdw) ((ULONGLONG(hdw) << 32) | ((ldw) & 0xFFFFFFFF)) 
#endif 

#ifndef MAXULONGLONG 
#define MAXULONGLONG ((ULONGLONG)~((ULONGLONG)0)) 
#endif 

bool CloseProcessMainThread(DWORD dwProcID) 
{ 
    DWORD dwMainThreadID = 0; 
    ULONGLONG ullMinCreateTime = MAXULONGLONG; 

    HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 
    if (hThreadSnap != INVALID_HANDLE_VALUE) { 
    THREADENTRY32 th32; 
    th32.dwSize = sizeof(THREADENTRY32); 
    BOOL bOK = TRUE; 
    for (bOK = Thread32First(hThreadSnap, &th32); bOK; 
     bOK = Thread32Next(hThreadSnap, &th32)) { 
     if (th32.th32OwnerProcessID == dwProcID) { 
     HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, 
            TRUE, th32.th32ThreadID); 
     if (hThread) { 
      FILETIME afTimes[4] = {0}; 
      if (GetThreadTimes(hThread, 
          &afTimes[0], &afTimes[1], &afTimes[2], &afTimes[3])) { 
      ULONGLONG ullTest = MAKEULONGLONG(afTimes[0].dwLowDateTime, 
               afTimes[0].dwHighDateTime); 
      if (ullTest && ullTest < ullMinCreateTime) { 
       ullMinCreateTime = ullTest; 
       dwMainThreadID = th32.th32ThreadID; // let it be main... :) 
      } 
      } 
      CloseHandle(hThread); 
     } 
     } 
    } 
#ifndef UNDER_CE 
    CloseHandle(hThreadSnap); 
#else 
    CloseToolhelp32Snapshot(hThreadSnap); 
#endif 
    } 

    if (dwMainThreadID) { 
    PostThreadMessage(dwMainThreadID, WM_QUIT, 0, 0); // close your eyes... 
    } 

    return (0 != dwMainThreadID); 
} 
+1

Wygląda dobrze, dziękuję! –

+6

Ta odpowiedź byłaby lepsza, gdyby zawierała pewne wyjaśnienia na temat tego, co robi kod i jak działa. W szczególności, w jaki sposób określasz "główny" wątek określonego procesu w przypadku, gdy proces ten ma wiele wątków? Wydaje mi się, że opierasz się na czasie tworzenia wątku. To niekoniecznie jest niezawodną heurystyką; ważne jest, aby wskazać, gdzie ludzie mogą dokładnie rozważyć zalety i wady tego podejścia. Odpowiedzi zawierające kod * tylko * nie są odpowiedziami. –

2

Nie można wysłać wiadomości do określonego wątku. Wiadomości są umieszczane w kolejce i przetwarzane tylko z głównego wątku.

Jeśli chcesz zamknąć proces Windows, który ma UI, spojrzeć na using sendmessage to send wm_close to another process

+0

Dziękuję za odpowiedź, będę zaznaczyć swoją odpowiedź jako użyteczne. –

+3

W rzeczywistości może istnieć wiele kolejek komunikatów, z których każda jest powiązana z innym wątkiem, a jeśli tak, można użyć PostThreadMessage, aby wysłać wiadomość do określonego wątku. –

+0

@Harry: to interesujące wiedzieć! Jaki jest realistyczny przypadek użycia, gdy występuje wiele kolejek? – alex

4

znacznie prostszy i pewniejszego sposobu aby uzyskać identyfikator wątku głównego wątku to niech rejestruje główny wątek jego własnego ID gwint za pomocą ::GetCurrentThreadId() do zmiennej globalnej wspólnego, może w WinMain lub gdzieś na samym początku swojego głównego wątku „”:

MainThreadId_G = ::GetCurrentThreadId(); 

następnie w swoich innych wątków, można zadzwonić: ::PostThreadMessage(MainThreadId_G, WM_QUIT, returncode, 0);

+0

Zdecydowanie preferuję tę odpowiedź. Zwięzłe, zapewnia kompletną odpowiedź i dobre wyjaśnienie. Oczywiście możliwe jest przekazanie identyfikatora głównego wątku jako parametru do wątków roboczych podczas ich tworzenia, zamiast używania zmiennej globalnej. –

1

Sprawdziłem jak to jest obsługiwane w MFC, a wygląda na to, wątek UI jest określana z konstruktora:

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\src\mfc\appcore.cpp: 

CWinApp::CWinApp(LPCTSTR lpszAppName) 
{ 
... 

    m_nThreadID = ::GetCurrentThreadId(); 

i za pomocą połączenia MFC AfxGetApp()->m_nThreadID można dowiedzieć się identyfikator wątku UI.

Jednak - to podejście nie działa, jeśli plik .dll nie został załadowany z głównego wątku - wtedy nawet metoda MFC nie zadziała - AfxGetApp()->m_nThreadID zwróci coś innego niż główny wątek.

Ale zazwyczaj twój plik dll zostanie wczytany z głównego wątku, ale twój plik .dll nie jest koniecznie włączony. Mam może zalecić podejścia tak:

class GetMainThread 
{ 
public: 
    GetMainThread() 
    { 
     m_nThreadID = ::GetCurrentThreadId(); 
    } 

    DWORD m_nThreadID; 
}getMainThread; 


DWORD getUIThread() 
{ 
    DWORD id = 0; 

    if(AfxGetApp() != NULL) 
     id = AfxGetApp()->m_nThreadID; 
    else 
     id = getMainThread.m_nThreadID; 

    return id; 
} //getUIThread 

Jeśli dll jest ładowany przez głównego wątku UI, otrzymasz prawidłowy identyfikator gwintu z rozmowy konstruktora (klasa GetMainThread).

Usuń AfxGetApp() rozmowy, jeśli nie są potrzebne (W mojej aplikacji potrzebne ci)

Powiązane problemy