2013-04-22 6 views
19

यह एक असंभव कार्य की तरह लग रहा है। बिल्कुल कुछ भी मुझे काम नहीं मिला है। सवाल यह है कि प्रोसेस के साथ शुरू किए गए कंसोल एप्लिकेशन को कैसे बंद करना है। स्टार्ट जो कि कंसोल विंडो के साथ शुरू किया गया है और खोल निष्पादन के बिना: (ProcessStartInfo.CreateNoWindow = true; ProcessStartInfo.UseShellExecute = false;)।प्रक्रिया के साथ शुरू किए गए कंसोल ऐप को कैसे बंद करें। स्टार्ट?

यह दिया गया है कि यदि एप्लिकेशन को ctrl-c या ctrl-break सिग्नल प्राप्त होता है तो इसे "साफ-सुथरा" बंद कर दिया जाएगा, लेकिन ऐसा लगता है कि यह काम करने वाला कोई तरीका नहीं है (विशेष रूप से GenerateConsoleCtrlEvent)।

  • प्रक्रिया। किल काम नहीं करता है। यह प्रक्रिया की हत्या के कारण भ्रष्ट फ़ाइलों को पीछे छोड़ देता है।
  • प्रक्रिया। CloseMainWindow काम नहीं करता है। इस मामले में कोई मुख्य विंडो नहीं है, इसलिए फ़ंक्शन झूठा लौटाता है और कुछ भी नहीं करता है।
  • प्रक्रिया के लिए सभी थ्रेड पर EnumThreadWindows को कॉल करना और प्रत्येक विंडो में WM_CLOSE भेजना कुछ भी नहीं करता है, और वैसे भी कोई थ्रेड विंडो नहीं है।
  • जेनरेट कंसोल CtrlEvent काम नहीं करता है। यह वही समूह में प्रक्रियाओं के लिए केवल उपयोगी है (जो .NET आपको कोई नियंत्रण नहीं देता है), वैसे भी कॉलिंग प्रक्रिया को बंद करने के अनचाहे दुष्प्रभाव के साथ। फ़ंक्शन आपको प्रक्रिया आईडी निर्दिष्ट करने की अनुमति नहीं देता है।

जो कोई भी "प्रक्रिया" ऑब्जेक्ट स्वीकार करता है जो उपरोक्त पैरामीटर के साथ शुरू किया गया है, जिसके परिणामस्वरूप कॉलिंग प्रक्रिया को प्रभावित किए बिना प्रारंभिक प्रक्रिया का एक साफ शटडाउन परिणाम के रूप में चिह्नित किया जाएगा। एक उदाहरण कंसोल ऐप के रूप में 7z.exe (7-ज़िप संग्रहकर्ता) का उपयोग करें, जो एक बड़ी फ़ाइल को संपीड़ित करना शुरू कर देता है, और स्पष्ट रूप से समाप्त नहीं होने पर भ्रष्ट, अधूरा फ़ाइल छोड़ देगा।

जब तक कोई कोई कार्यात्मक उदाहरण या कोड प्रदान नहीं करता है जो एक कार्यात्मक उदाहरण की ओर जाता है, तो यह प्रश्न अनुत्तरित नहीं है। मैंने इस सवाल और दर्जनों उत्तरों ऑनलाइन पूछने वाले दर्जनों लोगों को देखा है, और उनमें से कोई भी काम नहीं करता है। .NET अपनी प्रक्रिया आईडी को दिए गए कंसोल एप्लिकेशन को साफ़ करने के लिए कोई समर्थन प्रदान नहीं करता है, जो कि .NET प्रक्रिया ऑब्जेक्ट के साथ शुरू होने पर विचार कर रहा है। समस्या का एक हिस्सा एक नए प्रक्रिया समूह में एक प्रक्रिया बनाने में असमर्थता है, जो जेनरेट कंसोल CtrlEvent बेकार का उपयोग करता है। इसका समाधान होना चाहिए।

+0

मेरे लिए ध्वनि सही समाधान की तरह 'है GenerateConsoleCtrlEvent'। इसलिए आपको यह समझने की जरूरत है कि नए समूह में प्रक्रिया कैसे बनाएं। यदि .NET प्रक्रिया रैपर का उपयोग करना संभव नहीं है, तो मूल Win32 फ़ंक्शन पर जाएं। पी/Invoke का उपयोग करने में कोई हानि नहीं है, आप इसे पहले से ही 'GenerateConsoleCtrlEvent' के लिए कर रहे हैं। –

+0

मैं पहले से ही GenerateConsoleCtrlEvent का उपयोग नहीं कर रहा हूं (यह काम नहीं करता है), और मैं पी/Invoke का उपयोग नहीं करना चाहूंगा। CreateProcess द्वारा उपयोग की जाने वाली SECURITY_ATTRIBUTES संरचना में एक पॉइंटर होता है जिसके लिए मुझे 'असुरक्षित' के साथ संकलन करने की आवश्यकता होती है, जो मैं नहीं कर रहा हूं। CreateProcess के साथ एक और समस्या यह है कि मैं Process.EnableRaisingEvents और Exited Event विश्वसनीय रूप से उपयोग नहीं कर सकता, क्योंकि प्रक्रिया को कॉल करने का मौका मिलने से पहले प्रक्रिया समाप्त हो सकती है। GetProcessById हैंडलर असाइन करने के लिए .NET प्रक्रिया उदाहरण बनाने के लिए, जो आमतौर पर होगा प्रक्रिया कॉल करने से पहले किया गया। स्टार्ट। मुझे Win32 में अधिकांश तर्क फिर से लिखने के लिए मजबूर किया जा रहा है। – Triynko

+0

और जैसा कि मैंने कहा, यह वैसे भी काम नहीं करता है। CREATE_NEW_PROCESS_GROUP के साथ प्रक्रिया बनाने के बावजूद, GenerateConsoleCtrlEvent का समूह पर कोई प्रभाव नहीं पड़ता है, जैसा कि प्रक्रिया आईडी द्वारा पहचाना गया है। मैंने CTRL + C के लिए निरंतर पारित करने का प्रयास किया, और फिर CTRL + BREAK, और न ही काम किया। साथ ही, CREATE_NEW_PROCESS_GROUP के साथ, मैंने CREATE_NO_WINDOW या DETACHED_PROCESS (अलग से, क्योंकि वे परस्पर अनन्य हैं) के लिए दोनों झंडे की कोशिश की, और दोनों एक कंसोल-कम प्रक्रिया प्राप्त करते हैं जो मेरे मुख्य एप्लिकेशन की कंसोल विंडो में हस्तक्षेप नहीं करता है, जेनरेट कंसोल कर्टलवेन्ट विधि काम नहीं करती है। – Triynko

उत्तर

2

यह थोड़ी देर हो चुकी है, इसलिए आप इसे अब और का उपयोग नहीं हो सकता है, लेकिन शायद यह दूसरों की मदद करेगा ...

आप इस overthinking कर रहे हैं। समस्या यह है कि आप केवल एक प्रक्रिया से ब्रेक सिग्नल कर सकते हैं जो एक ही कंसोल साझा करता है - समाधान अपेक्षाकृत स्पष्ट होना चाहिए।

एक कंसोल प्रोजेक्ट बनाएं। इस परियोजना के लक्ष्य आवेदन हमेशा की तरह का शुभारंभ करेंगे:

var psi = new ProcessStartInfo(); 
psi.FileName = @"D:\Test\7z\7z.exe"; 
psi.WorkingDirectory = @"D:\Test\7z\"; 
psi.Arguments = "a output.7z input.bin"; 
psi.UseShellExecute = false; 

var process = Process.Start(psi); 

UseShellExecute महत्वपूर्ण हिस्सा है - यह सुनिश्चित करता है कि दो आवेदन एक ही सांत्वना साझा करने के लिए जा रहे हैं।

Console.CancelKeyPress += (s, e) => e.Cancel = true; 
Thread.Sleep(1000); 
GenerateConsoleCtrlEvent(ConsoleCtrlEvent.CTRL_C, 0); 

होस्ट किए गए इस आवेदन एक दूसरे के बाद इसे शुरू किया गया है टूट जाएगा:

यह आपको अपनी सहायक अनुप्रयोग, जो रूप में अच्छी तरह का आयोजन किया आवेदन करने के लिए पारित हो जाएगा करने के लिए ब्रेक भेजने के लिए अनुमति देता है। आसानी से, सुरक्षित रूप से। CancelKeyPress की आवश्यकता नहीं है - मैंने इसे केवल यह स्पष्ट करने के लिए रखा है कि आप होस्ट की गई प्रक्रिया को तोड़ सकते हैं, और फिर भी चलते रहेंगे। असली सहायक एप्लिकेशन में, इसका उपयोग कुछ अधिसूचनाओं या उस तरह के कुछ के लिए किया जा सकता है, लेकिन यह वास्तव में आवश्यक नहीं है।

अब आपको ब्रेक कमांड जारी करने के लिए सहायक एप्लिकेशन को सिग्नल करने का एक तरीका चाहिए - सरल कंसोल इनपुट का उपयोग करने का सबसे आसान तरीका होगा, लेकिन यह होस्ट किए गए एप्लिकेशन में हस्तक्षेप कर सकता है। अगर है कि आप के लिए एक विकल्प नहीं है, एक साधारण म्युटेक्स ठीक काम करेगा:

using (var mutex = Mutex.OpenExisting(args[0])) 
using (var processWaitHandle = new SafeWaitHandle(process.Handle, false)) 
using (var processMre = new ManualResetEvent(false) { SafeWaitHandle = processWaitHandle }) 
{ 
    var which = WaitHandle.WaitAny(new WaitHandle[] { mutex, processMre }); 

    if (which == 0) 
    { 
     Console.WriteLine("Got signalled."); 
     GenerateConsoleCtrlEvent(ConsoleCtrlEvent.CTRL_C, 0); 
    } 
    else if (which == 1) 
    { 
     Console.WriteLine("Exitted normally."); 
    } 
} 

यह या तो म्युटेक्स पर एक संकेत के लिए, या का आयोजन किया अनुप्रयोग से बाहर करने के लिए इंतजार करेगा। सहायक आवेदन शुरू करने के लिए, तुम सब करने की जरूरत है यह है:

var mutexName = Guid.NewGuid().ToString(); 
mutex = new Mutex(true, mutexName); 

var process = Process.Start(@"TestBreak.exe", mutexName); 

और तोड़ जारी करने के लिए, बस म्युटेक्स जारी:

mutex.ReleaseMutex(); 

यह है कि। यदि आपको कड़े नियंत्रण की आवश्यकता है, तो नामित पाइप की तरह कुछ उपयोग करना बेहतर विकल्प हो सकता है, लेकिन यदि आपको केवल ब्रेक सिग्नल की आवश्यकता है, तो एक म्यूटेक्स ठीक काम करेगा। आपको पास करने के लिए आवश्यक किसी भी तर्क को सहायक एप्लिकेशन के लिए तर्क के रूप में पारित किया जा सकता है, और आप यह काम शैल स्क्रिप्ट के साथ भी कर सकते हैं (केवल चलाने के लिए आवश्यक कुछ भी चलाने के लिए सहायक एप्लिकेशन का उपयोग करें)।

0

मैंने इसे अपने आप को समझने की कोशिश करने में कई घंटे बिताए। जैसा कि आपने बताया है, वेब उन उत्तरों से भरा हुआ है जो बस काम नहीं करते हैं। बहुत से लोग GenerateConsoleCtrlEvent का उपयोग करने का सुझाव देते हैं, लेकिन वे कोई संदर्भ प्रदान नहीं करते हैं, वे केवल बेकार कोड स्निपेट प्रदान करते हैं। नीचे दिया गया समाधान GenerateConsoleCtrlEvent का उपयोग करता है, लेकिन यह काम करता है। मैंने इसका परीक्षण किया है।

ध्यान दें कि यह एक WinForms ऐप है और जिस प्रक्रिया को मैं शुरू कर रहा हूं और रोक रहा हूं वह FFmpeg है। मैंने किसी और चीज के साथ समाधान का परीक्षण नहीं किया है। मैं एक वीडियो रिकॉर्ड करने के लिए एफएफएमपीईजी का उपयोग कर रहा हूं और आउटपुट को "video.mp4" नामक फ़ाइल में सहेज रहा हूं।

नीचे दिया गया कोड मेरी फॉर्म 1.cs फ़ाइल की सामग्री है। यह वह फ़ाइल है जो विजुअल स्टूडियो आपके लिए बनाता है जब आप WinForms समाधान बनाते हैं।

using System; 
using System.ComponentModel; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 
using System.Threading; 
using System.Windows.Forms; 

namespace ConsoleProcessShutdownDemo { 
    public partial class Form1 : Form { 

    BackgroundWorker worker; 
    Process currentProcess; 

    public Form1() { 
     InitializeComponent(); 
    } 

    private void Worker_DoWork(object sender, DoWorkEventArgs e) { 
     const string outFile = "video.mp4"; 

     var info = new ProcessStartInfo(); 
     info.UseShellExecute = false; 
     info.CreateNoWindow = true; 
     info.FileName = "ffmpeg.exe"; 
     info.Arguments = string.Format("-f gdigrab -framerate 60 -i desktop -crf 0 -pix_fmt yuv444p -preset ultrafast {0}", outFile); 
     info.RedirectStandardInput = true; 

     Process p = Process.Start(info); 

     worker.ReportProgress(-1, p); 
    } 

    private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { 
     currentProcess = (Process)e.UserState; 
    } 

    private void btnStart_Click(object sender, EventArgs e) { 
     btnStart.Enabled = false; 
     btnStop.Enabled = true; 

     worker = new BackgroundWorker(); 

     worker.WorkerSupportsCancellation = true; 
     worker.WorkerReportsProgress = true; 
     worker.DoWork += Worker_DoWork; 
     worker.ProgressChanged += Worker_ProgressChanged; 

     worker.RunWorkerAsync(); 

    } 

    private void btnStop_Click(object sender, EventArgs e) { 
     btnStop.Enabled = false; 
     btnStart.Enabled = true; 

     if (currentProcess != null) 
      StopProgram(currentProcess); 
    } 





    //MAGIC BEGINS 


    [DllImport("kernel32.dll", SetLastError = true)] 
    static extern bool AttachConsole(uint dwProcessId); 

    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] 
    static extern bool FreeConsole(); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId); 

    [DllImport("Kernel32", SetLastError = true)] 
    private static extern bool SetConsoleCtrlHandler(HandlerRoutine handler, bool add); 

    enum CtrlTypes { 
     CTRL_C_EVENT = 0, 
     CTRL_BREAK_EVENT, 
     CTRL_CLOSE_EVENT, 
     CTRL_LOGOFF_EVENT = 5, 
     CTRL_SHUTDOWN_EVENT 
    } 

    private delegate bool HandlerRoutine(CtrlTypes CtrlType); 

    public void StopProgram(Process proc) { 

     int pid = proc.Id; 

     FreeConsole(); 

     if (AttachConsole((uint)pid)) { 

      SetConsoleCtrlHandler(null, true); 
      GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0); 

      Thread.Sleep(2000); 

      FreeConsole(); 

      SetConsoleCtrlHandler(null, false); 
     } 

     proc.WaitForExit(); 

     proc.Close(); 
    } 


    //MAGIC ENDS 
} 

}

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