نقل COM إلى Linux

أنا أحب تقنية COM. لكننا لن نتحدث عن التكنولوجيا أو الثناء أو أوجه القصور في COM ، ولكن تجربة النقل والتنفيذ على Linux. دراجة؟ النفعية؟ دعونا لا نركز على هذا.


كائن COM (1)

بشكل عام ، كائن من فئة تطبق واجهة COM واحدة على الأقل. يتم إخفاء تنفيذ الكائن بشكل أساسي في مكتبة متصلة ديناميكيًا تسمى خادم COM (2) ، ويتم نشر الواجهات وتوزيعها للاستخدام.


واجهة COM ، فئة مجردة تحتوي فقط على وظائف افتراضية خالصة. تم تمييز واجهة IUnknown خاصة ، يجب على أي كائن COM تنفيذ هذه الواجهة.


يجب أن تحتوي كل واجهة COM على معرفها الخاص. في COM ، يتم تحديده من خلال بنية GUID وهنا سنواجه العيب الأول لـ COM. المعرّف الفريد العمومي (GUID) غير مفهوم ولا يقرأ جيدًا ، وكل شيء آخر موصوف في ويكي. نحن بحاجة إلى نفس الشيء ، ولكن بطريقة أكثر قابلية للقراءة ومفهومة (دعونا نسميها uiid).


IUnknown و uiid

#define define_uiid(name) \ inline static const std::string& guid() { const static std::string idn(dom_guid_pre_name #name); return idn; } namespace Dom { using uiid = std::string; using clsuid= std::string; struct IUnknown { virtual long AddRef() = 0; virtual long Release() = 0; virtual bool QueryInterface(const uiid&, void **ppv) = 0; define_uiid(Unknown) }; } 

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


الملخص
كائن COM يحتوي على معرف فئة واحد. يقوم بتنفيذ واجهة COM واحدة على الأقل - IUnknown (أي واجهة COM لها معرف واجهة فريد). يمكن أن يكون للتطبيقات المختلفة لكائن COM نفس معرف الفئة (مثال: إصدار الإصدار وتصحيحه).



خادم COM (2)

مكتبة مرتبطة ديناميكيًا (بالنسبة إلى Linux فهي كائن مشترك - لذا) تنفذ كائن COM واحدًا على الأقل. يجب على الخادم تصدير مجموعة محددة من الوظائف:


 extern "C" bool DllCreateInstance(const uiid& iid, void** ppv) 
ينشئ كائن فئة بواسطة clsuid ، يزيد من عدد المراجع لذلك ، في كل مرة يتم إنشاء الكائن بنجاح. يجب أن يؤدي استدعاء IUnknown :: AddRef أيضًا إلى زيادة العدد المرجعي لذلك ، ويجب أن ينخفض ​​IUnknown :: Release.

 extern "C" bool DllCanUnloadNow() 

إذا كان عدد المراجع لـ SO هو 0 ، فيمكنك إلغاء تحميل المكتبة.

 extern "C" bool DllRegisterServer(IUnknown* unknown) 

يسجل جميع خوادم clsuid في "التسجيل". يتم استدعاؤه مرة واحدة أثناء تثبيت خادم COM.

 extern "C" bool DllUnRegisterServer(IUnknown* unknown) 

حذف من إدخالات "التسجيل" حول خادم clsuid المسجل. يتم استدعاؤه مرة واحدة عند إلغاء تثبيت خادم COM.

مثال SimpleHello ، أعلن عن واجهة IHello:

 struct IHello : public virtual Dom::IUnknown { virtual void Print() = 0; define_uiid(Hello) }; 

تنفيذ الواجهة:

 /* COM- */ class SimpleHello : public Dom::Implement<SimpleHello, IHello> { public: SimpleHello() { printf("%s\n", __PRETTY_FUNCTION__); } ~SimpleHello() { printf("%s\n", __PRETTY_FUNCTION__); } virtual void Print() { printf("Hello from %s\n",__PRETTY_FUNCTION__); } define_clsuid(SimpleHello) }; /* COM- */ namespace Dom { DOM_SERVER_EXPORT_BEGIN EXPORT_CLASS(SimpleHello) DOM_SERVER_EXPORT_END DOM_SERVER_INSTALL(IUnknown* unknown) { Interface<IRegistryServer> registry; if (unknown->QueryInterface(IRegistryServer::guid(), registry)) { //      } return true; } DOM_SERVER_UNINSTALL(IUnknown* unknown) { Interface<IRegistryServer> registry; if (unknown->QueryInterface(IRegistryServer::guid(), registry)) { //      } return true; } } 

تقوم مجموعة من وحدات الماكرو بإخفاء عمليات تنفيذ الوظائف من خلال توفير تعريف ومنطق أكثر تنظيمًا.


Dom :: تنفيذ <SimpleHello، IHello> - يخفي تنفيذ أساليب واجهة IUnknown ، ويضيف "السكر" عند الإعلان عن واجهات يتم تنفيذها بواسطة كائن (C ++ 11 وقوالب متنوعة):



 template <typename T, typename ... IFACES> struct Implement : virtual public IUnknown, virtual public IFACES… { ... }; 

واجهة IRegistryServer - تحدد مجموعة من الأساليب للعمل مع "تسجيل" خوادم COM.


"تسجيل" خوادم COM (3)

يمكن التقليل من أهمية التسجيل ، ولكن من المحتمل أنه الركيزة الرئيسية لـ COM. تكتب Microsoft إلى التسجيل ، وتنشئ بنية معقدة لوصف الواجهات وسماتها (idl) ، لقد ذهبت بطريقة مختلفة قليلاً.


في التطبيق ، يستند التسجيل إلى نظام الملفات.
أي نوع من الكعك؟ إمكانية الفهم والبساطة وإمكانية الاسترداد وكعكة خاصة عند تسجيل الخادم ، يمكنك تعيين نوع من مساحة الاسم (دليل متعلق بالتسجيل الأساسي حيث سيتم تسجيل كائنات الخادم) ، وبالتالي يمكنك تنفيذ تكامل التطبيقات وإصدارها باستخدام التكنولوجيا.


من أوجه القصور ، المشاكل الأمنية المحتملة ، استبدال تطبيقات الكائن.


كيفية الاستخدام ، تطبيق عينة (4)

من أجل جعل كل شيء يعمل ، ستحتاج إلى "مكتبة" صغيرة و "برنامج" صغير.


إن "المكتبة" ليست سوى غلاف يقوم بتنفيذ كل شيء وتجميعه في وحدة واحدة كاملة ، والعمل مع السجل ، وتحميل / تفريغ SO ، وإنشاء كائنات.
هو الوحيد الذي يجب تحديده عند إنشاء التطبيق. كل شيء آخر ، "أريد أن أصدق ،" ستفعل نفسها.


" Programka " - regsrv هو في الواقع تناظري من برنامج Microsoft RegSrv32 الذي يقوم بتنفيذ نفس الإجراءات (+ القدرة على تحديد مساحة الاسم ، + القدرة على الحصول على قائمة خوادم مسجلة وخوادم COM).



عينة

 #include "../include/dom.h" #include "../../skel/ihello.h" int main() { Dom::Interface<Dom::IUnknown> unkwn; Dom::Interface<IHello> hello; if (Dom::CreateInstance(Dom::clsid("SimpleHello"), unkwn)) { unkwn->QueryInterface(IHello::guid(), hello); hello->Print(); } else { printf("[WARNING] Class `SimpleHello` not register.\nFirst execute command\n\tregsrv <fullpath>/libskel.so\n... and try again."); } return 0; } 

دوم (5)

Dom (نموذج كائن ديناميكي) ، عملي على Linux.

استنساخ بوابة


شكرا لك

Source: https://habr.com/ru/post/ar427919/


All Articles