2011-09-12 17 views
5

के लिए प्रतीक्षा कर रहा है मुझे वैश्विक हॉटकी पंजीकृत करने के लिए एक सी # एपीआई लिखनी है। WM_HOTKEY संदेश प्राप्त करने के लिए, मैं System.Windows.Forms.NativeWindow का उपयोग करता हूं और System.Windows.Forms.Application.Run(ApplicationContext) के साथ अपना संदेश लूप चलाता हूं। जब उपयोगकर्ता हॉटकी पंजीकृत करना चाहता है, तो उसे RegisterHotkey() नामक एक विधि चलाने की आवश्यकता है जो System.Windows.Forms.ApplicationContext.ExitThread() के साथ संदेश लूप को रोकता है, RegisterHotKey() (पी/Invoke) फ़ंक्शन के साथ हॉटकी को पंजीकृत करता है और संदेश लूप को फिर से शुरू करता है। यह आवश्यक है, क्योंकि RegisterHotKey() को उसी थ्रेड के भीतर बुलाया जाना चाहिए जिसने विंडो बनाई है, जिसे फिर से उसी थ्रेड के भीतर तुरंत चालू किया जाना चाहिए जो संदेश लूप चलाता है।सी # - WinForms संदेश लूप

समस्या यह है, उपयोगकर्ता कॉल RegisterHotkey() विधि शीघ्र ही धागा जो संदेश पाश चल रहा है शुरू करने के बाद, ApplicationContext.ExitThread()Application.Run(ApplicationContext) से पहले कहा जाता हो जाता है और इसलिए आवेदन ब्लॉक अनिश्चित काल के लिए कि। क्या किसी को संदेश लूप शुरू करने की प्रतीक्षा करने के लिए कोई दृष्टिकोण पता है?

अग्रिम धन्यवाद!

+0

यह बहुत समझ में नहीं आता है। * जब आप संदेश लूप शुरू करते हैं तो पहले RegisterHotkey() * को कॉल करें। साथ ही, एक वास्तविक विंडो की आवश्यकता है, आपके पास वैध संभाल होना चाहिए। –

+0

चूंकि मैं प्रति हॉटकी पर एक संदेश लूप शुरू नहीं करना चाहता हूं, इसलिए मुझे उसी थ्रेड के भीतर RegisterHotKey() को कॉल करने के लिए मौजूदा को रोकना और पुनरारंभ करना होगा। और चूंकि मेरा कोड काम कर रहा है, मुझे नहीं लगता कि मुझे एक वास्तविक विंडो की आवश्यकता है, वैध हैंडल 'नेटिव विन्डो.क्रेटहैंडल (CreateParams)' द्वारा बनाया गया है। –

उत्तर

5

तो RegisterHotKey को उसी थ्रेड से कॉल करने की आवश्यकता है जिसने विंडो बनाई और संदेश लूप शुरू किया। अपने कस्टम संदेश लूप थ्रेड में RegisterHotKey के निष्पादन को इंजेक्ट क्यों न करें? इस तरह आपको संदेश लूप को रोकने और पुनरारंभ करने की आवश्यकता नहीं है। आप बस शुरू किए गए पहले व्यक्ति का पुन: उपयोग कर सकते हैं और एक ही समय में अजीब दौड़ की स्थिति से बच सकते हैं।

आप ISynchronizeInvoke.Invoke का उपयोग करके किसी अन्य धागे पर एक प्रतिनिधि को इंजेक्ट कर सकते हैं जो कि ISynchronizeInvoke उदाहरण होस्ट करने वाले धागे पर प्रतिनिधि होगा। यहां बताया गया है कि यह कैसे किया जा सकता है।

void Main() 
{ 
    var f = new Form(); 

    // Start your custom message loop here. 
    new Thread(
    () => 
    { 
     var nw = NativeWindow.FromHandle(f.Handle); 
     Application.Run(new ApplicationContext(f)); 
    } 

    // This can be called from any thread. 
    f.Invoke(
    (Action)(() => 
    { 
     RegisterHotKey(/*...*/); 
    }), null); 
} 

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

आपको लगता है कि मनमाने ढंग से Form उदाहरण बनाया तो आप शायद SendMessage और तरह के माध्यम से धागा करने के लिए एक कस्टम संदेश भेजने और NativeWindow.WndProc में यह प्रसंस्करण एक ही प्रभाव है कि ISynchronizeInvoke तरीकों स्वचालित रूप से प्रदान करते हैं पाने के लिए के साथ भाग मिल सकता है नहीं करना चाहते हैं।

+0

मैं हेवीवेट फॉर्म क्लास का उपयोग करने से बचाना चाहता था और इसके बजाय हल्के नेटिवविंडो क्लास का उपयोग करना चाहता था। लेकिन यह Invoke दृष्टिकोण का उपयोग करने का मौका पाने का सबसे साफ समाधान प्रतीत होता है ... धन्यवाद! –

+0

@ जोनास: मुझे पहले से ही संदेह है कि आपकी टिप्पणियों में से एक और आधारित है। मैंने अपना जवाब अपडेट किया: मूल रूप से, आपको मैन्युअल संदेश पास करने वाली तकनीकों का उपयोग करके 'Invoke' की नकल करने में सक्षम होना चाहिए। –

+0

यह बात है, धन्यवाद! –

1

मैं वहाँ एक बेहतर तरीका है पता नहीं है, लेकिन आप एक Mutex का उपयोग करें और इसे रीसेट कर सकता है जब आप Application.Run फोन और Mutex.Wait() का उपयोग करते हैं Applicationcontext.ExitThread() बुला।

+1

आपके त्वरित उत्तर के लिए धन्यवाद! मैंने पहले से ही 'ऑटोरेटीवेंट' कक्षा के साथ यह कोशिश की है, लेकिन इस मामले में मुझे 'AutoResetEvent.Set()' को 'Application.Run()' से पहले कॉल करना होगा, जिसका अर्थ है कि 'ApplicationContext.ExitThread() ' 'AutoResetEvent.WaitOne()' के साथ प्रतीक्षा करने के बाद भी संभव है ... –

1

आप एप्लिकेशन तक प्रतीक्षा करने का प्रयास कर सकते हैं। उपयोगकर्ता को रजिस्टरहोटकी को कॉल करने की अनुमति देने के लिए इडल इवेंट निकाल दिया जाता है।

+0

आपके उत्तर के लिए धन्यवाद! यदि पुस्तकालय किसी अन्य Winforms एप्लिकेशन में अपने संदेश लूप के साथ उपयोग किया जाता है तो यह कोई समस्या नहीं होगी? –

+0

माध्यमिक धागे पर एक संदेश पंप बनाना और एक फॉर्म प्रदर्शित करना निश्चित रूप से संभव है। वह धागा तब पहले थ्रेड के स्वतंत्र रूप से घटनाओं को संसाधित कर सकता है। यह इस बात पर निर्भर हो सकता है कि आपके एपीआई का उपयोग कैसे किया जाता है। – CommonSense

+0

मैंने आपके दृष्टिकोण की कोशिश की है और यह एक काम को छोड़कर महान काम करता है: एप्लिकेशन.इडल इवेंट केवल एक बार आग लगती है और संदेश पंप के पुनरारंभ को पहचान नहीं पाती है: एस –

0

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

मैंने यह सब तय किया है और कामकाजी कोड नीचे है। मैंने इसे उत्तर पर एक टिप्पणी में रखा होगा लेकिन मेरे पास पर्याप्त प्रतिनिधि नहीं है। ऐसा करने के बाद, मैं एप्लिकेशन को कॉल करने से पहले फॉर्म को बनाने के लिए मजबूर करने की वैधता के बारे में कुछ अनिश्चित हूं। इस तरह से चलाएं, या एक थ्रेड पर 'नया फॉर्म' करने और फिर इसे पूरी तरह से बनाने के लिए दूसरे को पास करने के लिए (खासतौर से चूंकि इसे आसानी से बचाया जाता है):

static void Main(string[] args) 
{ 
    AutoResetEvent are = new AutoResetEvent(false); 
    Form f = new Form(); 
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId); 

    new Thread(
     () => 
     { 
      Console.WriteLine(Thread.CurrentThread.ManagedThreadId); 
      var nw = NativeWindow.FromHandle(f.Handle); 
      are.Set(); 
      Application.Run(f); 
     }).Start(); 

    are.WaitOne(new TimeSpan(0, 0, 2)); 

    f.Invoke(
     (Action)(() => 
     { 
      Console.WriteLine(Thread.CurrentThread.ManagedThreadId); 
     }), null); 

    Console.ReadLine(); 
}