2009-11-05 21 views
7

क्या विंडोज़ में बाल प्रक्रिया द्वारा लॉन्च की गई सभी प्रक्रियाओं का इंतजार करना संभव है? मैं बच्चे या पोते की प्रक्रियाओं को संशोधित नहीं कर सकता।विंडोज़ में पोते की प्रक्रियाओं की प्रतीक्षा

विशेष रूप से, मैं यही करना चाहता हूं। मेरी प्रक्रिया uninstallA.exe लॉन्च करता है। प्रक्रिया uninistallA.exe uninstallB.exe लॉन्च करता है और तुरंत बाहर निकलता है, और uninstallB.exe थोड़ी देर के लिए चलाता है। मैं uninstallB.exe से बाहर निकलने के लिए प्रतीक्षा करना चाहता हूं ताकि अनइंस्टॉल समाप्त होने पर मुझे पता चल सके।

उत्तर

9

CreateJobObject के साथ एक नौकरी ऑब्जेक्ट बनाएं। एक निलंबित राज्य में UninstallA.exe शुरू करने के लिए CreateProcess का उपयोग करें। AssignProcessToJobObject के साथ अपनी नौकरी ऑब्जेक्ट में उस नई प्रक्रिया को असाइन करें। CreateProcess से वापस मिले धागे के हैंडल पर ResumeThread पर कॉल करके UninstallA.exe प्रारंभ करें।

फिर कठिन हिस्सा: नौकरी ऑब्जेक्ट को इसके निष्पादन को पूरा करने के लिए प्रतीक्षा करें। दुर्भाग्यवश, यह किसी के मुकाबले काफी अपेक्षाकृत जटिल है। मूल विचार यह है कि आप एक I/O समापन पोर्ट बनाते हैं, फिर आप ऑब्जेक्ट ऑब्जेक्ट बनाते हैं, इसे I/O पूरा करने वाले पोर्ट से संबद्ध करते हैं, और अंत में I/O समापन पोर्ट पर प्रतीक्षा करें (GetQueuedCompletionStatus के साथ इसकी स्थिति प्राप्त करें)। रेमंड चेन ने अपने blog पर एक प्रदर्शन (और यह कैसे बताया) के बारे में स्पष्टीकरण दिया है।

+4

धन्यवाद। ऐसा लगता है कि एक सरल WaitForSingleObject मुझे तब नहीं बताएगा जब सभी प्रक्रियाएं निकलती हैं, लेकिन ऐसा लगता है कि मैं एक आईओ पूरा करने वाला पोर्ट बना सकता हूं और JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO के लिए प्रतीक्षा कर सकता हूं। – thudbang

+0

रेमंड चेन ने अपने ब्लॉग में शामिल किया: [मैं नौकरी में सभी प्रक्रियाओं से बाहर निकलने तक कैसे प्रतीक्षा करूं?] (Https://blogs.msdn.microsoft.com/oldnewthing/20130405-00/?p=4743) –

0

named mutex का उपयोग करें।

+0

मुझे नहीं लगता कि मैं ऐसा कर सकता हूं। मैं बाल प्रक्रियाओं को संशोधित नहीं कर सकता। – thudbang

+0

हाँ, यह तब काम नहीं करेगा। – Cheeso

0

एक संभावना यह Cygwin स्थापित करने के लिए और फिर ps आदेश का उपयोग पोता के लिए देखने के लिए बाहर निकलने के लिए

1

सभी पोते के लिए प्रतीक्षा करने के लिए एक सामान्य तरीका नहीं है लेकिन अपने विशिष्ट मामले के लिए आप कुछ हैक करने में सक्षम हो सकता है साथ में। आप जानते हैं कि आप एक विशिष्ट प्रक्रिया उदाहरण की तलाश में हैं। मैं पहले uninstallA.exe से बाहर निकलने के लिए प्रतीक्षा करूंगा (WaitForSingleObject का उपयोग करके) क्योंकि उस बिंदु पर आप जानते हैं कि uninstallB.exe प्रारंभ हो गया है। फिर चल रहे uninstallB.exe उदाहरण को खोजने के लिए PSAPI से EnumProcesses और GetProcessImageFileName का उपयोग करें। यदि आपको यह नहीं मिला है तो आप जानते हैं कि यह पहले ही समाप्त हो चुका है, अन्यथा आप इसके लिए प्रतीक्षा कर सकते हैं।

एक अतिरिक्त जटिलता यह है कि यदि आपको XP से पुराने विंडोज़ के संस्करणों का समर्थन करने की आवश्यकता है तो आप GetProcessImageFileName का उपयोग नहीं कर सकते हैं, और Windows NT के लिए आप PSAPI का उपयोग नहीं कर सकते हैं। विंडोज 2000 के लिए आप GetModuleFileNameEx का उपयोग कर सकते हैं लेकिन इसमें कुछ चेतावनी हैं जिसका अर्थ है कि यह कभी-कभी विफल हो सकता है (दस्तावेज़ों की जांच करें)। अगर आपको एनटी का समर्थन करना है तो टूलहेल 2 देखें।

हाँ यह बहुत बदसूरत है।

+1

आप टूलहेल 2 को किसी भी तरह से उपयोग करने पर विचार कर सकते हैं, क्योंकि यह प्रत्येक प्रक्रिया की अभिभावक (स्पॉन्गिंग) आईडी प्रदान करता है, और CreateProcess() एक प्रक्रिया आईडी भी देता है। आपको uninstallA.exe लॉन्च करने में सक्षम होना चाहिए, इसकी प्रक्रिया आईडी प्राप्त करें, और उसके बाद उसी प्रक्रिया आईडी द्वारा शुरू की गई सभी प्रक्रियाओं को देखने के लिए ToolHelp32 का उपयोग करें। जब तक आप CreateProcess() द्वारा लौटाए गए प्रक्रिया संभाल को बंद नहीं करते हैं, तब तक ओएस अनइंस्टॉलए की प्रक्रिया आईडी का पुन: उपयोग नहीं कर सकता है। –

1

यहां एक तकनीक है कि, अचूक नहीं होने पर, उपयोगी हो सकता है अगर किसी कारण से आप नौकरी ऑब्जेक्ट का उपयोग नहीं कर सकते हैं। विचार एक अज्ञात पाइप बनाना है और बच्चे को प्रक्रिया को पाइप के लिखने के अंत में हैंडल का उत्तराधिकारी होना है।

आम तौर पर, पोते की प्रक्रियाओं को पाइप के लिखने के अंत का भी वारिस होगा। विशेष रूप से, cmd.exe (उदा।, बैच फ़ाइल से) द्वारा लॉन्च की गई प्रक्रियाएं हैंडल का उत्तराधिकारी होंगी।

एक बार बाल प्रक्रिया समाप्त हो जाने के बाद, मूल प्रक्रिया पाइप के लिखने के अंत में अपने हैंडल को बंद कर देती है, और फिर पाइप से पढ़ने का प्रयास करती है। चूंकि कोई भी पाइप को नहीं लिख रहा है, इसलिए पढ़ना ऑपरेशन अनिश्चित काल तक अवरुद्ध होगा। (निश्चित रूप से आप धागे या एसिंक्रोनस I/O का उपयोग कर सकते हैं यदि आप पोते के लिए इंतज़ार करते समय सामान करना जारी रखना चाहते हैं।)

जब (और केवल जब) पाइप के लिखने के अंत में अंतिम संभाल बंद हो जाता है, तो पाइप का अंत लिखना स्वचालित रूप से नष्ट हो जाता है। यह पाइप को तोड़ता है और पढ़ा गया ऑपरेशन पूरा करता है और एक ERROR_BROKEN_PIPE विफलता की रिपोर्ट करता है।

मैं कई वर्षों से उत्पादन में इस कोड (और उसी कोड के पुराने संस्करण) का उपयोग कर रहा हूं।

// pwatch.c 
// 
// Written in 2011 by Harry Johnston, University of Waikato, New Zealand. 
// This code has been placed in the public domain. It may be freely 
// used, modified, and distributed. However it is provided with no 
// warranty, either express or implied. 
// 
// Launches a process with an inherited pipe handle, 
// and doesn't exit until (a) the process has exited 
// and (b) all instances of the pipe handle have been closed. 
// 
// This effectively waits for any child processes to exit, 
// PROVIDED the child processes were created with handle 
// inheritance enabled. This is usually but not always 
// true. 
// 
// In particular if you launch a command shell (cmd.exe) 
// any commands launched from that command shell will be 
// waited on. 

#include <windows.h> 

#include <stdio.h> 

void error(const wchar_t * message, DWORD err) { 

    wchar_t msg[512]; 

    swprintf_s(msg, sizeof(msg)/sizeof(*msg), message, err); 

    printf("pwatch: %ws\n", msg); 

    MessageBox(NULL, msg, L"Error in pwatch utility", MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL); 

    ExitProcess(err); 

} 

int main(int argc, char ** argv) { 

    LPWSTR lpCmdLine = GetCommandLine(); 

    wchar_t ch; 

    DWORD dw, returncode; 

    HANDLE piperead, pipewrite; 

    STARTUPINFO si; 

    PROCESS_INFORMATION pi; 

    SECURITY_ATTRIBUTES sa; 

    char buffer[1]; 

    while (ch = *(lpCmdLine++)) { 

    if (ch == '"') while (ch = *(lpCmdLine++)) if (ch == '"') break; 

    if (ch == ' ') break; 

    } 

    while (*lpCmdLine == ' ') lpCmdLine++; 

    sa.nLength = sizeof(sa); 
    sa.bInheritHandle = TRUE; 
    sa.lpSecurityDescriptor = NULL; 

    if (!CreatePipe(&piperead, &pipewrite, &sa, 1)) error(L"Unable to create pipes: %u", GetLastError()); 

    GetStartupInfo(&si); 

    if (!CreateProcess(NULL, lpCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) 
    error(L"Error %u creating process.", GetLastError()); 

    if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) error(L"Error %u waiting for process.", GetLastError()); 

    if (!GetExitCodeProcess(pi.hProcess, &returncode)) error(L"Error %u getting exit code.", GetLastError()); 

    CloseHandle(pipewrite); 

    if (ReadFile(piperead, buffer, 1, &dw, NULL)) { 

    error(L"Unexpected data received from pipe; bug in application being watched?", ERROR_INVALID_HANDLE); 

    } 

    dw = GetLastError(); 

    if (dw != ERROR_BROKEN_PIPE) error(L"Unexpected error %u reading from pipe.", dw); 

    return returncode; 

} 
संबंधित मुद्दे