هل سيكون com Marchalling (من أي وقت مضى) ضروريًا لكائن مع ThreadingModel على حد سواء؟

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

سؤال

هذا هو الناجم عن ذلك سؤال آخر.

على وجه التحديد ، لدي فئة كوم في عملية ، يتم تعريفها في سجل CLSID كما وجود ThreadingModel من Both.

عمليتنا تنشط هذا الكائن من خلال CoCreateInstance (ليس CoCreateInstanceEx, ، إذا كان هذا يهم حتى بالنسبة لخادم DLL داخل)

إعطاء نموذج خيوط من Bothوبالنظر إلى القواعد المدرجة في مستندات:

Threading model of server | Apartment server is run in
------------------------------------------------------
Both                      | Same apartment as client

وبالنظر إلى ما يكتبه هانز في الإجابة الأخرى:

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

استنتاجي المبدئي هو أن مثل هذا الشيء سوف أبداً تحتاج إلى تنظيم ضمني للمكالمات إلى واجهاته ، لأن الكائن سيعيش دائمًا في نفس الشقة مثل عميلها.

هل هذا صحيح ، حتى لو كانت عملية العميل تعمل كما ستا?

هل كانت مفيدة؟

المحلول

نعم ، قد يكون هناك تنظيم.

إذا كان عميل فئة COM يعمل في STA وحاولت استدعاء فصلك من شقة أخرى ، فسيتعين عليه تنظيم الشقة التي تم إنشاؤها فيها.

يمكن أن تكون مصطلحات COM مربكة حقًا. عندما تشير إلى "عميل" في هذه الحالة ، فأنت تشير حقًا إلى مؤشر ترابط ، وليس التطبيق بأكمله (كما يعني).

Both يعني فقط أن نموذج خيوط الخادم يتوافق مع العميل الذي يقوم بتثبيته. وهذا هو ، عندما تقوم بتثبيت صفك ، فإنه يأخذ نموذج الخيط الخاص بالخيط الذي تم إنشاؤه عليه. نظرًا لأنك تقوم بتثبيت الخادم في STA ، فسيستخدم الخادم الخاص بك STA ، مما يعني أنه لا يمكن استدعاؤه إلا في الخيط الذي أنشأه ؛ إذا حاول مؤشر ترابط آخر استدعاءه ، فسيتم حشد الخيط الذي تم إنشاؤه عليه.

نصائح أخرى

لا يمكنني مساعدة نفسي في نشر هذا ، على الرغم من أنه ليس إجابة مباشرة على السؤال.

هناك مقال رائع من MSKB من العصور الذهبية في كوم: معلومات: أوصاف وأعمال نماذج خيوط OLE. لا يزال هناك ، ولديه كل المعلومات ذات الصلة. النقطة المهمة هي أنه لا ينبغي أن تقلق بشأن ما إذا كان هناك حشد أم لا ، إذا اتبعت القواعد. فقط قم بتسجيل كائنك كـ ThreadingModel=Both, ، تجميع المارشالر الحراري مع CoCreateFreeThreadedMarshaler, وينجز. سيفعل com الحشد إذا لزم الأمر ، بأفضل طريقة ممكنة. اعتمادًا على نموذج شقة العميل ، قد يتلقى رمز العميل المؤشر المباشر إلى الواجهة الخاصة بك ، إذا كان يتبع القواعد أيضًا.

أي واجهة "أجنبية" قد تتلقاها عندما يتم استدعاء طريقة الواجهة الخاصة بك ، ستكون صالحة في نطاق المكالمة ، لأنك تبقى على نفس الموضوع. إذا كنت لا تحتاج إلى تخزينها ، فهذا كل ما يهم.

ومع ذلك ، إذا كنت بحاجة إلى تخزين الواجهة "الغريبة" ، فإن الطريقة الصحيحة للقيام بذلك هي تخزينها باستخدام CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream:

لتخزينه:

  • أدخل القسم الحرج ؛
  • مكالمة CoMarshalInterThreadInterfaceInStream وتخزين IStream مؤشر في حقل عضو ؛
  • اترك القسم الحرج ؛

لاسترداده

  • أدخل القسم الحرج ؛
  • مكالمة CoGetInterfaceAndReleaseStream لاسترداد الواجهة
  • مكالمة CoMarshalInterThreadInterfaceInStream وتخزينه مرة أخرى IStream لأي استخدام في المستقبل
  • اترك القسم الحرج ؛
  • استخدم الواجهة في نطاق المكالمة الحالية

لإطلاق سراحه:

  • عندما لم تعد بحاجة إلى الاحتفاظ بها ، ما عليك سوى إطلاق المخزنة IStream (داخل القسم الحرج).

إذا كان الكائن "الأجنبي" قد تم تربيته حرًا أيضًا ، وكانت الأشياء تحدث داخل نفس العملية ، فمن المحتمل أن تتعامل مع مؤشر الواجهة المباشر بعد CoGetInterfaceAndReleaseStream. ومع ذلك ، يجب ألا تضع أي افتراضات ، ولا تحتاج حقًا إلى معرفة ما إذا كان الكائن الذي تتعامل معه هو الكائن الأصلي أو وكيل Com Marshaller.

يمكن تحسين ذلك قليلاً باستخدام CoMarshalInterface ث/ MSHLFLAGS_TABLESTRONG / CoUnmarshalInterface / IStream::Seek(0, 0) / CoReleaseMarshalData بدلاً من CoGetInterfaceAndReleaseStream/CoGetInterfaceAndReleaseStream, ، لإلغاء تشغيل الواجهة نفسها عدة مرات كما هو مطلوب دون إطلاق الدفق.

سيناريوهات التخزين المؤقت الأكثر تعقيدًا (وربما أكثر كفاءة) ممكنة ، والتي تتضمن تخزين الخيوط المحلية. ومع ذلك ، أعتقد أن هذا سيكون مبالغة. لم أفعل أي توقيت ، لكنني أعتقد أن النفقات العامة CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStreamهو حقا منخفضة.

ومع ذلك ، إذا كنت بحاجة إلى الحفاظ على حالة ذلك يخزن أي موارد أو كائنات قد تتطلب تقارب الخيط, ، بخلاف واجهات كوم المذكورة أعلاه ، أنت لا يجب ضع علامة على هدفك ThreadingModel=Both أو تجميع FTM.

نعم ، لا يزال الماردة ممكنًا. بعض الأمثلة:

  1. يتم إنشاء كائن من مؤشر ترابط MTA ويتم وضعه في شقة MTA ثم يتم تمرير مؤشره في أي مؤشر ترابط STA وأن أساليب مؤشر ترابط STA للكائن. في هذه الحالة ، لا يمكن لخيط STA الوصول إلى الكائن إلا عن طريق التنميقة.

  2. يتم إنشاء كائن من خيط STA ويتم وضعه في شقة STA التي تنتمي إلى هذا الخيط ثم يتم تمرير مؤشره في مؤشر ترابط STA آخر أو مؤشر ترابط MTA. في كلتا الحالتين ، يمكن لتلك المواضيع الوصول إلى الكائن فقط عن طريق التنقل.

في الواقع ، لا يمكنك توقع أي حشد فقط في الحالتين التاليتين:

  1. يتم إنشاء مثيل للكائن من مؤشر ترابط MTA ومن ثم الوصول إليه فقط بواسطة مؤشرات ترابط MTA - كلاهما الذي قام بتثبيت الكائن وجميع مؤشرات ترابط MTA الأخرى من نفس العملية.
  2. يتم إنشاء مثيل للكائن من مؤشر ترابط STA ثم الوصول إليه فقط من خلال هذا الموضوع بالذات

وفي جميع الحالات الأخرى ، سيبدأ التنقل.

ThreadingModel = كلاهما يعني ببساطة أن مؤلف Com Server يمكنه إعطاء ضمان بأن رمزه آمن مؤشر ترابط ولكن لا يمكنه إعطاء نفس الضمان آخر الكود الذي لم يكتبه سيتم استدعاؤه بطريقة آمنة مؤشرات الترابط. الحالة الأكثر شيوعًا للحصول على مثل هذه التعليمات البرمجية الخارجية لتنفيذها هي من خلال عمليات الاسترجاعات ، وهي نقاط الاتصال هي المثال الأكثر شيوعًا (تسمى عادة "الأحداث" في أوقات تشغيل العميل).

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

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