استدعاء Legacy VB6 COM+ DLL إلى Win32 DLL الأصلي — مشكلات الترابط مع STA؟

StackOverflow https://stackoverflow.com/questions/1057524

سؤال

تعرف على ما يبدو للوهلة الأولى وكأنه مشكلة في MT، لكنني أحاول أن أفهم بالتفصيل نموذج STA الذي يستخدمه COM+.

على نحو فعال، لدي مكون COM+ قديم، مكتوب بلغة VB6، والذي يستدعي ملف Win32 DLL الأصلي (أي غير COM) المكتوب بلغة C++.

نظرًا لوجود بعض المشكلات المتقطعة (ومن المستحيل إعادة إنتاجها في الاختبار) به، أضفت بعض تعليمات برمجية لتصحيح الأخطاء لمعرفة ما يحدث ووجدت أنه عند حدوث المشكلات، كانت لدي رسائل سجل متداخلة في الملف - لذا فهذا يعني ضمنيًا أن ملف DLL تم استدعاؤه بواسطة خيطين في وقت واحد.

ينتقل التسجيل الآن إلى ملف لكل مؤشر ترابط استنادًا إلى _getpid() وGetCurrentThreadId()، لذلك يبدو أنه عندما يتم استدعاء التعليمات البرمجية الموجودة في C++ DLL، يتم استدعاؤها مرتين على نفس مؤشر الترابط في نفس الوقت.إن فهمي لـ STA يشير إلى أن هذا قد يكون هو الحال حيث يقوم COM بتنظيم المثيلات الفردية للكائنات على مؤشر ترابط واحد ويعلق ويستأنف التنفيذ حسب الرغبة.

لسوء الحظ لست متأكدًا من أين أذهب من هنا.قرأت أنه يجب علي الاتصال بـ CoInitialiseEx() في DllMain() لإخبار COM أن هذا هو STA DLL، لكن أماكن أخرى تقول أن هذا صالح فقط لمكتبات COM DLL ولن يكون له أي تأثير في DLL أصلي.الخيار الآخر الوحيد هو تغليف أجزاء من مكتبة الارتباط الحيوي (DLL) كأقسام مهمة لإجراء تسلسل الوصول (مع أخذ أي نتيجة أداء موجودة على الذقن).

يمكنني محاولة إعادة صياغة ملف DLL، ولكن لا توجد حالة مشتركة أو vars عمومي - كل شيء موجود في متغيرات محلية، لذا من الناحية النظرية يجب أن يحصل كل استدعاء على مكدس خاص به، لكنني أتساءل عما إذا كان نموذج STA له تأثير غريب في هذا الأمر وقم فقط بإعادة الدخول إلى ملف DLL الذي تم تحميله بالفعل عند نفس نقطة الدخول مثل مكالمة أخرى.ولسوء الحظ، لا أعرف كيف أثبت أو أختبر هذه النظرية.

الأسئلة في الأساس هي:

  1. عندما يقوم مكون STA COM+ باستدعاء DLL الأصلي ، لا يوجد شيء في نموذج STA لمنع تعليق "الخيط" النشط والتحكم الذي يتم نقله إلى "موضوع" آخر في منتصف مكالمة DLL؟
  2. هل CoInitialiseEx() هي الطريقة الصحيحة لحل هذه المشكلة أم لا؟
  3. إذا لم تكن (1) أو (2) افتراضات "جيدة"، فماذا يحدث؟
هل كانت مفيدة؟

المحلول

في خادم COM المترابطة، يتم ضمان إمكانية الوصول إلى كل مثيل لفئة COM بواسطة مؤشر ترابط واحد.هذا يعنى المثال هو موضوع آمن.ومع ذلك، يمكن إنشاء العديد من المثيلات في وقت واحد، باستخدام سلاسل عمليات مختلفة.الآن، فيما يتعلق بخادم COM، لا يتعين على مكتبة الارتباط الحيوي (DLL) الأصلية الخاصة بك القيام بأي شيء خاص.فكر فقط في kernel32.dll، الذي يستخدمه كل ملف قابل للتنفيذ - هل يقوم بتهيئة COM عند استخدامه بواسطة خادم COM؟

من وجهة نظر DLL، يجب عليك التأكد من أنك آمن، حيث يمكن لمثيلات مختلفة الاتصال بك في نفس الوقت.لن تقوم STA بحمايتك في هذه الحالة.نظرًا لأنك تقول أنك لا تستخدم أي متغيرات عامة، فلا يمكنني إلا أن أفترض أن المشكلة موجودة في مكان آخر، ويحدث أنها تظهر في الظروف التي يبدو أنها تشير إلى عناصر COM.هل أنت متأكد من عدم وجود بعض المشكلات البسيطة في ذاكرة C++ القديمة؟

نصائح أخرى

أظن أن مشكلتك كانت أنه في مكان ما عميقًا داخل ملف DLL المسمى، قام بإجراء اتصال COM صادر إلى شقة أخرى (مؤشر ترابط آخر في نفس العملية، أو كائن في MTA، أو عملية أخرى تمامًا).يسمح COM لمؤشر ترابط STA بانتظار نتيجة مكالمة صادرة لتلقي مكالمة واردة أخرى، ومعالجتها بشكل متكرر.إنه مخصص فقط للمحادثات الجارية بين نفس الكائنات - أي.يتصل A بـ B، ويتصل B بـ A مرة أخرى، ويتصل A بـ B مرة أخرى - ولكن يمكن تلقي مكالمات من كائنات أخرى إذا قمت بتسليم مؤشر واجهة إلى العديد من العملاء، أو قام العميل بمشاركة مؤشر الواجهة مع عميل آخر.بشكل عام، إنها فكرة سيئة أن يتم تسليم مؤشرات الواجهة إلى كائن ذي ترابط واحد إلى عدة سلاسل عمليات عميل، حيث سيتعين عليهم فقط انتظار بعضهم البعض.قم بإنشاء كائن عامل واحد لكل مؤشر ترابط.

لا يمكن لـ COM تعليق واستئناف التنفيذ حسب الرغبة على أي مؤشر ترابط - يمكن أن تصل مكالمة واردة جديدة على مؤشر ترابط STA فقط من خلال مضخة الرسائل.عند انتظار الرد "محظور"، يقوم مؤشر ترابط STA فعليًا بضخ الرسائل، والتحقق باستخدام عامل تصفية الرسائل (انظر IMessageFilter) مما إذا كان يجب معالجة الرسالة.ومع ذلك، يجب ألا يقوم معالجو الرسائل بإجراء مكالمة صادرة جديدة - إذا فعلوا ذلك، فسوف يُرجع COM خطأ RPC_E_CANTCALLOUT_INEXTERNALCALL ("من غير القانوني الاتصال أثناء وجودك داخل عامل تصفية الرسائل.")

قد تحدث مشكلات مشابهة إذا كان لديك مضخة رسائل (GetMessage/DispatchMessage) في أي مكان داخل مكتبة الارتباط الحيوي (DLL) الأصلية.لقد واجهت مشاكل مع DoEvents الخاص بـ VB في إجراءات الواجهة.

يجب أن يتم استدعاء CoInitializeEx فقط بواسطة منشئ سلسلة المحادثات، لأنه هو الوحيد الذي يعرف سلوك ضخ الرسائل الخاص به.من المحتمل أنه إذا حاولت الاتصال به في DllMain فسوف يفشل ببساطة، حيث يتم استدعاء DLL الأصلي الخاص بك استجابة لمكالمة COM، لذلك يجب أن يكون المتصل قد اتصل بالفعل في النهاية بـ CoInitializeEx على مؤشر الترابط لإجراء المكالمة.إن القيام بذلك في إشعار DLL_THREAD_ATTACH، بالنسبة لسلاسل الرسائل التي تم إنشاؤها حديثًا، قد يعمل بشكل سطحي ولكنه يتسبب في حدوث خلل في البرنامج إذا قام COM بحظر الوقت الذي يجب أن يضخ فيه والعكس صحيح.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top