2009-03-26 10 views
19

आम तौर पर ओएस एक्स पर एक एप्लिकेशन बंडल केवल एक बार शुरू किया जा सकता है, हालांकि बस बंडल की प्रतिलिपि बनाकर उसी एप्लिकेशन को दो बार लॉन्च किया जा सकता है। इस संभावना का पता लगाने और रोकने के लिए सबसे अच्छी रणनीति क्या है?यह पता लगाने के लिए कि क्या एक ओएस एक्स एप्लिकेशन पहले से लॉन्च किया गया है

विंडोज पर इस प्रभाव को लॉन्च पर नामित संसाधन बनाने वाले एप्लिकेशन द्वारा आसानी से हासिल किया जा सकता है और फिर नामित संसाधन नहीं बनाया जा सकता है, तो यह संकेत मिलता है कि एक और प्रक्रिया चल रही है जो पहले से ही एक ही संसाधन बना चुकी है। जब एप्लिकेशन छोड़ता है तो इन संसाधनों को विंडोज पर विश्वसनीय तरीके से जारी किया जाता है।

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

ओएस एक्स पर एक ही प्रभाव प्राप्त करने के लिए मैं उपयोग कर सकते हैं एपीआई हैं: posix, कार्बन और बढ़ावा।

विचार?

+0

आप ऐसा क्यों करना चाहते हैं? विंडोज के विपरीत, ऑपरेटिंग सिस्टम सामान्य मामले में चलने से किसी एप्लिकेशन के कई उदाहरणों को रोकने का ख्याल रखता है। असामान्य मामले में, इसे क्यों रोकें? –

+0

प्रश्न में आवेदन एक खेल है। एक मशीन पर गेम की कई प्रतियां चलाकर एक खिलाड़ी को कुछ स्थितियों में अन्य खिलाड़ियों पर अनुचित लाभ होता। –

उत्तर

8

निम्न स्तर का समाधान झुंड() का उपयोग करना है।

प्रत्येक उदाहरण स्टार्टअप पर फ़ाइल लॉक करने का प्रयास करेगा, और यदि लॉक विफल हो जाता है तो दूसरा उदाहरण पहले से चल रहा है। जब आपका प्रोग्राम निकलता है तो भेड़िया स्वचालित रूप से रिलीज़ होते हैं, इसलिए बालों के ताले के बारे में कोई चिंता नहीं होती है।

ध्यान दें कि जो भी समाधान आप चुनते हैं, आपको "एकाधिक उदाहरण" होने का क्या अर्थ है इसके बारे में एक सचेत निर्णय लेने की आवश्यकता है। विशेष रूप से, यदि एकाधिक उपयोगकर्ता एक ही समय में अपना ऐप चला रहे हैं, तो क्या यह ठीक है?

+0

धन्यवाद, वह समाधान ठीक होगा। लॉक फाइल प्रति उपयोगकर्ता एक ही समय में ऐप शुरू करने के लिए एक ही मशीन पर एकाधिक उपयोगकर्ताओं को अवरुद्ध करने के लिए नहीं होगी। –

1

IPC के बारे में क्या? आप एक सॉकेट खोल सकते हैं और अन्य लॉन्च इंस्टेंस के साथ बातचीत कर सकते हैं। हालांकि आपको सावधान रहना होगा, अगर यह दोनों ऐप्स एक ही समय में शुरू होते हैं तो यह काम करता है।

मैं आपको नमूना कोड प्रदान नहीं कर सकता, जैसा कि मैंने नहीं किया है (अभी तक, लेकिन मैं जल्द ही) इसका उपयोग करता हूं।

+2

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

3

सबसे पहले, यह "मैक ओएस एक्स" या "ओएस एक्स" है। "ओएस/एक्स" जैसी कोई चीज़ नहीं है।

दूसरा, मैक ओएस एक्स बूस्ट के साथ नहीं आता है; आपको इसे अपने आवेदन के साथ बंडल करना होगा।

तीसरा, अधिकांश कार्बन 64-बिट में उपलब्ध नहीं है। यह एक स्पष्ट संकेत है कि कार्बन के उन हिस्सों को किसी दिन दूर जाना होगा (जब ऐप्पल अपने हार्डवेयर में 32-बिट छोड़ देता है)। जल्द या बाद में, आपको या तो कोको के साथ अपने ऐप को फिर से लिखना होगा या मैक को त्यागना होगा।

आम तौर पर ओएस/एक्स पर एक एप्लिकेशन बंडल केवल एक बार शुरू किया जा सकता है, हालांकि बस बंडल का नाम बदलकर उसी एप्लिकेशन को दो बार लॉन्च किया जा सकता है।

नहीं, यह नहीं कर सकता। नामित या स्थानांतरित एप्लिकेशन लॉन्च करने से पहले से चल रही प्रक्रिया को सक्रिय (सामने लाया जाएगा) सक्रिय हो जाएगा; यह पहले के साथ एक नई, दूसरी प्रक्रिया शुरू नहीं करेगा।


यह बताए जाने के कई तरीके हैं कि कोई एप्लिकेशन पहले से चल रहा है या नहीं। प्रत्येक मामले में, आप इसे लॉन्च पर करते हैं:

  1. एकल निरंतर नाम के साथ कनेक्शन पंजीकृत करने के लिए कोको के एनएससीनेक्शन का उपयोग करें। अगर नाम पहले ही पंजीकृत है तो यह असफल हो जाएगा। (आप एक कार्बन ऐप से फाउंडेशन का उपयोग कर सकते हैं; यह एप्लिकेशन किट है जिसके साथ आपको सावधान रहना होगा।)
  2. प्रोसेस मैनेजर का उपयोग उन प्रक्रियाओं के लिए प्रक्रिया सूची को स्कैन करने के लिए करें जिनके बंडल पहचानकर्ता आप जिसकी तलाश कर रहे हैं उससे मेल खाते हैं। बंडल पहचानकर्ता अपरिवर्तनीय नहीं है, लेकिन फ़ाइल नाम या स्थान से बदलना मुश्किल है।
  3. आप को देखने के लिए जब कोई अपने आप को की दूसरी प्रति चलाता देख रहे हैं, तो आप CFNotificationCenter उपयोग कर सकते हैं:

    1. "com.yourdomain.yourappname.LaunchResponse" के लिए एक पर्यवेक्षक के रूप में अपने आप को जोड़ें।
    2. "com.yourdomain.yourappname.LaunchCall" नाम के तहत एक अधिसूचना पोस्ट करें।
    3. "com.yourdomain.yourappname.LaunchCall" के लिए स्वयं को पर्यवेक्षक के रूप में जोड़ें।

    कॉल अधिसूचना के लिए आपके अवलोकन कॉलबैक में, प्रतिक्रिया अधिसूचना पोस्ट करें।
    प्रतिक्रिया अधिसूचना के लिए आपके अवलोकन कॉलबैक में, बाहर निकलें।

    इस प्रकार, जब पहली प्रक्रिया शुरू होती है, तो यह कॉल करेगी और कोई प्रतिक्रिया नहीं मिलेगी; जब दूसरी प्रक्रिया शुरू होती है, तो यह कॉल करेगी, पहली प्रक्रिया से प्रतिक्रिया प्राप्त करें, और पहले के प्रति सम्मान में बाहर निकलें।

+0

मुझे लगता है कि उसका नाम बदलने के बजाय प्रतिलिपि थी। वैसे भी, यदि आप निकोलस रिले के लॉन्च स्थापित हैं, तो आप "open -n textEdit.app" –

+0

या लॉन्च-एम का उपयोग करके दूसरा उदाहरण खोल सकते हैं। –

3

जैसा कि पहले ही उल्लेख किया गया है कोको अनुप्रयोग आमतौर पर आपको एक समय में एक से अधिक उदाहरण चलाने की अनुमति नहीं देते हैं।

सामान्य रूप से, एनएसवर्क्स स्पेस में लॉन्च एप्प्लिकेशंस पर इस रूप को हल करने के लिए कोको तरीका। यह प्रत्येक लॉन्च एप्लिकेशन के लिए एक शब्दकोश युक्त एनएसएआरआरए देता है। आप यह देखने के लिए सरणी के माध्यम से लूप कर सकते हैं कि आप जिस ऐप को ढूंढ रहे हैं वह पहले से चल रहा है या नहीं। मैं सलाह दूंगा कि आप कुंजी NSApplicationBundleIdentifier के साथ मान का उपयोग करें जिसमें नाम की तलाश करने के बजाय "com.mycompany.myapp" जैसे मान होंगे। यदि आपको किसी ऐप के लिए बंडल पहचानकर्ता ढूंढना है तो आप ऐप पैकेज में इसकी info.plist फ़ाइल देख सकते हैं।

24

यह Snow Leopard में बेहद आसान है:

- (void)deduplicateRunningInstances { 
    if ([[NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]] count] > 1) { 
     [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Another copy of %@ is already running.", [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey]] 
         defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"This copy will now quit."] runModal]; 

     [NSApp terminate:nil]; 
    } 
} 

अधिक जानकारी के लिए http://blog.jseibert.com/post/1167439217/deduplicating-running-instances-or-how-to-detect-if देखें।

+2

ओएस 10.8 में, main.m में शुरू होने पर यह चेक सही काम नहीं करता है क्योंकि उस चरण में चल रहा ऐप स्वयं सरणी में नहीं है (शायद केवल अगले रनलोप में पंजीकृत हो जाता है), इसलिए " > 1 "आपको"> 0 "के लिए जांच करनी होगी। इस सुरक्षित w.r.t. खेलने के लिए भविष्य के संस्करण, वर्तमान ऐप के लिए सरणी को स्पष्ट रूप से जांचना सबसे अच्छा है: '(NSRunning अनुप्रयोग * रनिंग अनुप्रयोगों में रनिंग ऐप) { अगर (! [runningApp isEqual: [NSRunningApplication currentAplication]]) { // अलर्ट और बाहर निकलें }} ' –

+2

एक और महत्वपूर्ण मुद्दा: runningApplicationsWithBundleIdentifier एक बंडल आईडी से मेल खाने वाले चल रहे अनुप्रयोगों को लौटाता है, लेकिन महत्वपूर्ण रूप से केवल वर्तमान उपयोगकर्ता के स्वामित्व वाले (इसलिए ये समाधान इस मशीन पर अलग-अलग उपयोगकर्ताओं को एक ही समय में आपके ऐप को चलाने से नहीं रोकेंगे। –

7

एक रहस्यमय Info.plist कुंजी है जिसे "एप्लिकेशन कई उदाहरणों को प्रतिबंधित करता है" कहा जाता है, लेकिन यह मेरे लिए काम नहीं कर रहा है। मैं एक सीएलआई आवेदन लिख रहा हूं और इसे एक बंडल के भीतर से निष्पादित कर रहा हूं। शायद यह एक जीयूआई आवेदन में काम करेगा, लेकिन मैंने कोशिश नहीं की है।

+4

यह कुंजी (LSMultipleInstancesProhibited) लॉन्चपैड या फाइंडर से ऐप लॉन्च होने पर अच्छी तरह से काम करता है। बोनस के रूप में पहले से चल रहे ऐप को सामने लाया जाता है। मेरे लिए यह एक त्रुटि संवाद प्रदर्शित करने के बाद बेहतर होता है। ऐप कमांड से शुरू होने पर कुंजी काम नहीं करता है लाइन। – Maf

0

पता लगाएं कि एक ही बंडल आईडी के साथ आवेदन चल रहा है, इसे सक्रिय करें और क्या शुरू होता है बंद करें।

- (id)init method of <NSApplicationDelegate> 

    NSArray *apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]]; 
    if ([apps count] > 1) 
    { 
     NSRunningApplication *curApp = [NSRunningApplication currentApplication]; 
     for (NSRunningApplication *app in apps) 
     { 
      if(app != curApp) 
      { 
       [app activateWithOptions:NSApplicationActivateAllWindows|NSApplicationActivateIgnoringOtherApps]; 
       break; 
      } 
     } 
     [NSApp terminate:nil]; 
     return nil; 
    } 
1

यह स्विफ्ट 2.0 के लिए रोम के लोगों 'और जेफ के जवाब का एक संयोजन है: यदि एक ही बंडल आईडी के साथ एप्लिकेशन का एक और उदाहरण पहले से ही चल रहा है, एक सूचना दिखाएंगे, अन्य उदाहरण को सक्रिय करने और डुप्लिकेट उदाहरण छोड़ दिया।

func applicationDidFinishLaunching(aNotification: NSNotification) { 
    /* Check if another instance of this app is running. */ 
    let bundleID = NSBundle.mainBundle().bundleIdentifier! 
    if NSRunningApplication.runningApplicationsWithBundleIdentifier(bundleID).count > 1 { 
     /* Show alert. */ 
     let alert = NSAlert() 
     alert.addButtonWithTitle("OK") 
     let appName = NSBundle.mainBundle().objectForInfoDictionaryKey(kCFBundleNameKey as String) as! String 
     alert.messageText = "Another copy of \(appName) is already running." 
     alert.informativeText = "This copy will now quit." 
     alert.alertStyle = NSAlertStyle.CriticalAlertStyle 
     alert.runModal() 

     /* Activate the other instance and terminate this instance. */ 
     let apps = NSRunningApplication.runningApplicationsWithBundleIdentifier(bundleID) 
     for app in apps { 
      if app != NSRunningApplication.currentApplication() { 
       app.activateWithOptions([.ActivateAllWindows, .ActivateIgnoringOtherApps]) 
       break 
      } 
     } 
     NSApp.terminate(nil) 
    } 

    /* ... */ 
} 
0

यह स्विफ्ट 3.0 के लिए SEB के का एक संस्करण है: यदि एक ही बंडल आईडी के साथ एप्लिकेशन का एक और उदाहरण पहले से ही चल रहा है, एक सूचना दिखाएंगे, अन्य उदाहरण को सक्रिय करने और डुप्लिकेट उदाहरण छोड़ दिया।

func applicationDidFinishLaunching(aNotification: NSNotification) { 
    /* Check if another instance of this app is running. */ 
    let bundleID = Bundle.main.bundleIdentifier! 
    if NSRunningApplication.runningApplications(withBundleIdentifier: bundleID).count > 1 { 
     /* Show alert. */ 
     let alert = NSAlert() 
     alert.addButton(withTitle: "OK") 
     let appName = Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) as! String 
     alert.messageText = "Another copy of \(appName) is already running." 
     alert.informativeText = "This copy will now quit." 
     alert.alertStyle = NSAlert.Style.critical 
     alert.runModal() 

     /* Activate the other instance and terminate this instance. */ 
     let apps = NSRunningApplication.runningApplications(withBundleIdentifier: bundleID) 
      for app in apps { 
        if app != NSRunningApplication.current { 
         app.activate(options: [.activateAllWindows, .activateIgnoringOtherApps]) 
         break 
        } 
      } 
       NSApp.terminate(nil) 
     } 
     /* ... */ 
} 
संबंधित मुद्दे