2009-06-29 13 views
7

एमटी-इश्यू की तरह पहली नजर में क्या दिखता है, लेकिन मैं COM + द्वारा उपयोग किए गए एसटीए मॉडल को विस्तार से समझने की कोशिश कर रहा हूं।लीगेसी वीबी 6 कॉम + डीएलएल देशी विन 32 डीएलएल में कॉलिंग - एसटीए के साथ थ्रेडिंग मुद्दे?

प्रभावी रूप से, मेरे पास एक विरासत COM + घटक है, जो वीबी 6 में लिखा गया है, जो सी ++ में लिखित विन 32 डीएलएल देशी (यानी, नहीं-COM) में कॉल करता है।

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

अब लॉगिंग _getpid() और GetCurrentThreadId() पर आधारित प्रति-थ्रेड फ़ाइल पर जाती है, इसलिए ऐसा लगता है कि जब C++ DLL में कोड कहा जाता है, तो उसे एक ही समय में एक ही थ्रेड पर दो बार बुलाया जा रहा है । एसटीए की मेरी समझ का कहना है कि यह मामला हो सकता है क्योंकि COM एक ही थ्रेड पर ऑब्जेक्ट्स के अलग-अलग उदाहरणों को मार्शल करता है और इच्छानुसार निष्पादन को फिर से शुरू करता है।

असुविधाजनक रूप से मुझे यकीन नहीं है कि यहां से कहां जाना है। मैं पढ़ रहा हूं कि मुझे COMInitialiseEx() को DllMain() में COM को बताने के लिए कॉल करना चाहिए कि यह एक एसटीए डीएलएल है, लेकिन अन्य स्थानों का कहना है कि यह केवल COM DLL के लिए मान्य है और इसका मूल डीएलएल में कोई प्रभाव नहीं पड़ेगा। एकमात्र अन्य विकल्प डीएलएल के हिस्सों को एक्सेस को क्रमबद्ध करने के लिए महत्वपूर्ण वर्गों के रूप में लपेटना है (ठोड़ी पर जो भी प्रदर्शन हिट लेना है)।

मैं डीएलएल को आजमाकर पुनः प्रयास कर सकता हूं, लेकिन कोई साझा राज्य या वैश्विक युद्ध नहीं है - सबकुछ स्थानीय चर में है, इसलिए सिद्धांत में प्रत्येक कॉल को अपना खुद का ढेर मिलना चाहिए, लेकिन मुझे आश्चर्य है कि क्या एसटीए मॉडल मूल रूप से कुछ है इस पर अजीब प्रभाव और एक ही कॉल के रूप में एक ही प्रविष्टि बिंदु पर पहले से लोड डीएलएल में फिर से प्रवेश कर रहा है। असुविधाजनक रूप से, मुझे नहीं पता कि इस सिद्धांत को साबित या परीक्षण कैसे करें।

सवालों मूल रूप से कर रहे हैं:

  1. एक जाँच COM + घटक एक देशी DLL कॉल है, वहाँ को एसटीए मॉडल में कुछ भी नहीं है को रोकने के सक्रिय "सूत्र" निलंबित किया जा रहा है और नियंत्रण एक और "धागा करने के लिए पारित किया जा रहा "एक डीएलएल कॉल के बीच में?
  2. क्या CoInitialiseEx() इसे हल करने का सही तरीका है, या नहीं?
  3. यदि न तो (1) या (2) "अच्छी" धारणाएं हैं, तो क्या हो रहा है?
+0

>> इसे एक ही समय में एक ही थ्रेड पर दो बार बुलाया जा रहा है। << अब यह मजाकिया है क्योंकि यह "एक ही समय में" नहीं हो सकता है। शायद यह कोड अपने आप को सबसे अच्छी तरह से बुला रहा है। – wqw

उत्तर

1

एक अपार्टमेंट थ्रेडेड COM सर्वर में, COM क्लास के प्रत्येक उदाहरण को एक थ्रेड द्वारा एक्सेस करने की गारंटी है। इसका मतलब है उदाहरण थ्रेड सुरक्षित है। हालांकि, विभिन्न धागे का उपयोग करके, कई उदाहरण एक साथ बनाया जा सकता है। अब, जहां तक ​​COM सर्वर का संबंध है, आपके मूल डीएलएल को कुछ विशेष करने की ज़रूरत नहीं है। बस kernel32.dll के बारे में सोचें, जिसका उपयोग प्रत्येक निष्पादन योग्य द्वारा किया जाता है - क्या यह COM सर्वर द्वारा उपयोग किए जाने पर COM प्रारंभ करता है?

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

+0

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

+0

1. आप वास्तव में किस तरह की समस्याएं अनुभव कर रहे हैं? दुर्घटनाओं, hangup, दुर्व्यवहार? 2. क्या आप सीधे या अप्रत्यक्ष रूप से धागे बना रहे हैं या संदेश भेज रहे हैं? 3. जैसे ही आप किसी समस्या की पहचान करते हैं, एक मिनीडम्प (या पूर्ण डंप) बनाने का प्रयास करें, स्टैक ट्रेस आपके देव env के आराम में समस्या की पहचान करने में मदद कर सकता है। – eran

+0

पुन: "सादा पुराना सी ++ मेमोरी समस्याएं": हाँ यह अंत में था, लेकिन malloc() प्रकार क्षेत्र में नहीं, लेकिन एक चार सरणी स्थिर के रूप में परिभाषित किया गया है। रेमंड चेन ने इसके बारे में यहां http://blogs.msdn.com/oldnewthing/archive/2004/03/08/85901.aspx ब्लॉग किया है और मुझे कुछ अन्य स्थानों और साथ ही साइटों पर पुष्टि मिली है। स्थिर को हटाने के लिए कोड को दोबारा प्रतिक्रिया देना इसे हल किया जाता है। –

0

मुझे संदेह है कि आपकी समस्या यह थी कि डीएलएल नामक कहीं गहराई से, यह एक अन्य अपार्टमेंट (एक ही प्रक्रिया में एक और थ्रेड, या एमटीए में एक वस्तु, या पूरी तरह से एक प्रक्रिया) के लिए एक आउटबाउंड COM कॉल किया गया।COM एक एसटीए थ्रेड को एक अन्य इनबाउंड कॉल प्राप्त करने के लिए आउटबाउंड कॉल के परिणाम के लिए प्रतीक्षा कर रहा है, इसे पुन: संसाधित करता है। यह केवल वही ऑब्जेक्ट्स के बीच चल रहे वार्तालापों के लिए है - यानी ए कॉल बी, बी कॉल ए बैक, ए कॉल बी दोबारा वापस - लेकिन यदि आप कई क्लाइंट्स या क्लाइंट को इंटरफ़ेस पॉइंटर सौंप चुके हैं तो अन्य ऑब्जेक्ट्स से कॉल प्राप्त कर सकते हैं इंटरफ़ेस पॉइंटर को किसी अन्य क्लाइंट को साझा किया है। आम तौर पर एक सिंगल थ्रेडेड ऑब्जेक्ट को इंटरफ़ेस पॉइंटर्स को एकाधिक क्लाइंट थ्रेड में सौंपना एक बुरा विचार है, क्योंकि उन्हें केवल एक दूसरे के लिए इंतजार करना होगा। प्रति थ्रेड एक कार्यकर्ता वस्तु बनाएँ।

COM किसी भी थ्रेड पर इच्छानुसार निष्पादन को निलंबित और फिर से शुरू नहीं कर सकता - एसटीए थ्रेड पर एक नई आने वाली कॉल केवल संदेश पंप के माध्यम से पहुंच सकती है। प्रतिक्रिया के लिए 'अवरुद्ध' होने पर, एसटीए थ्रेड वास्तव में संदेशों को पंप कर रहा है, संदेश फ़िल्टर के साथ जांच कर रहा है (IMessageFilter देखें) संदेश को संभाला जाना चाहिए या नहीं। हालांकि, संदेश संचालकों एक नया आउटगोइंग कॉल करना चाहिए नहीं - अगर वे करते हैं कॉम एक RPC_E_CANTCALLOUT_INEXTERNALCALL त्रुटि लौटाएगा ("यह जबकि अंदर संदेश फिल्टर बाहर कॉल करने के लिए गैर कानूनी है।") यदि आप संदेश पंप है

इसी प्रकार की समस्याएं उत्पन्न हो सकते हैं (GetMessage/DispatchMessage) देशी डीएलएल के भीतर कहीं भी। मुझे इंटरफ़ेस प्रक्रियाओं में वीबी के DoEvents के साथ समस्याएं आई हैं।

CoInitializeEx केवल थ्रेड के निर्माता द्वारा ही बुलाया जाना चाहिए, क्योंकि केवल वे जानते हैं कि उनका संदेश पंपिंग व्यवहार क्या होगा। ऐसा लगता है कि यदि आप इसे DllMain में कॉल करने का प्रयास करते हैं तो यह असफल हो जाएगा, क्योंकि आपके मूल डीएलएल को COM कॉल के जवाब में बुलाया जा रहा है, इसलिए कॉल करने के लिए कॉलर को आखिरकार थ्रेड पर CoInitializeEx कहा जाना चाहिए था। इसे नए बनाए गए धागे के लिए DLL_THREAD_ATTACH अधिसूचना में करना, सतही रूप से काम कर सकता है लेकिन यदि प्रोग्राम को पंप करना चाहिए और इसके विपरीत, यदि ब्लॉक ब्लॉक हो तो प्रोग्राम खराब हो सकता है।

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