دراسة حالة لجسر COM يستدعي DLL بـ 64bit من تطبيق 32bit
· آخر تحديث: · 小村 豪 · COM, تطوير Windows, 32bit, 64bit
سجل التعديلات (5 تحديثات، آخر تحديث 3 Sep، 2026)
سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.
- أُضيفت روابط الاستشارة الموجودة في الأصل الياباني (consultation_services). ولم يتغيّر نصّ المقالة نفسه. قراءة النسخة السابقة لهذا التحديث (DOI: 10.5281/zenodo.22242888)
- أُصلِحت بقايا يابانية في التعليقات ونص التشخيص ورابط المقالة ذات الصلة. قراءة النسخة السابقة لهذا التحديث (DOI: 10.5281/zenodo.22240794)
- أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
- أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
- أُضيفت فقرة قبل القسم 3. إذا كان ما تريد الوصول إليه من جهة 64 بت خادم COM من نوع in-proc أصلًا (أي مكتبة DLL مسجَّلة عبر InprocServer32)، فإن إضافة AppID إلى الـ CLSID الخاص بها وكتابة DllSurrogate فارغ تحت ذلك الـ AppID تجعلها تُستضاف داخل dllhost.exe المرفق مع Windows، وقد يغنيك ذلك عن كتابة خادم EXE. وتشير الفقرة أيضًا إلى أن الـ bitness الخاص بالـ surrogate الذي يُشغَّل يتبع المكتبة لا العميل، وأن وجود LocalServer32 مسجَّل يعني أن الـ surrogate لن يُستخدم. أمّا إذا كان المطلوب استدعاء مكتبة أصلية عادية، كما في هذه المقالة، فلا يوجد أصلًا خادم COM لاستضافته، ويبقى أسلوب خادم الـ EXE الموصوف لاحقًا ضروريًا. وقد تُركت خطوات التسجيل والحدّ الفاصل بين ما يكفي فيه الـ surrogate وما يستدعي كتابة EXE خاص بك لروابط تحيل إلى المقالتين الأخريين. قراءة النسخة السابقة لهذا التحديث (DOI: 10.5281/zenodo.21621306)
- النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621305)
هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.
小村 豪 (2026). دراسة حالة لجسر COM يستدعي DLL بـ 64bit من تطبيق 32bit. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621305 https://comcomponent.com/ar/blog/2026/01/25/002-com-case-study-32bit-to-64bit/
- DOI (أحدث نسخة)
- 10.5281/zenodo.21621305
- DOI (هذه النسخة)
- 10.5281/zenodo.22279679
استدعاء DLL بـ 64bit من تطبيق 32bit متطلّب نمطي جدّاً على Windows. وخصوصاً عندما تريد الإبقاء على أصل قائم واستخدام وظائف جانب 64bit فقط، تصير تركيبة جسر COM حلاً واقعياً في الغالب.
الجمهور المستهدف: من يصون تطبيقاً قائماً لـ Windows بـ 32bit ويريد استخدام DLL أو مكتبة في جانب 64bit. النصّ مكتوب ليُقرأ بافتراض أنّك «سمعت بـ COM لكنّك لم تركّبه بنفسك».
بيئة الافتراض: Windows بإصدار 64bit (x64)، وبيئة تطوير تكتب فيها C# (مثل Visual Studio). تسجيل خادم COM على الجهاز كلّه (تحت HKEY_LOCAL_MACHINE) يتطلّب امتيازات المدير. الفكرة الأساسية لـ COM مرتّبة في «ما هو COM - لماذا لا يزال تصميم Windows COM يبدو جميلاً».
المحتويات
- الوضع المفترض
- طريقة الحلّ
- تدفّق المعالجة (مخطّط تسلسل)
- عيّنة شيفرة (تصوّر)
- عيّنة الشيفرة الكاملة
- خلاصة
- مراجع
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 19، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
1. الوضع المفترض
الحالة: الإبقاء على تطبيق قائم بـ 32bit، مع الرغبة في استخدام معالجة DLL بـ 64bit. غير أنّ عملية 32bit لا تحمّل DLL بـ 64bit. هذا قيد على مستوى نظام التشغيل، لا أمر يُحلّ بالتحايل.
الشائع هو وضع كهذا.
- التطبيق القائم بـ 32bit أصل كبير، ولا يمكن ترحيله فوراً
- في جانب DLL بـ 64bit وظيفة جديدة، أو مكتبة تابعة متاحة بـ 64bit فقط
- تريد الاستدعاء من جانب 32bit «بأنواع»
بهذا الجمع، طريق الاستدعاء داخل العملية نفسها مغلق من البداية.
flowchart TB
accTitle: بنية الوضع المفترض
accDescr: مخطّط يبيّن أنّ تطبيقاً قائماً بـ 32bit يريد استخدام معالجة DLL بـ 64bit، لكنّ عملية 32bit لا تحمّل DLL بـ 64bit بسبب قيد على مستوى نظام التشغيل، فطريق الاستدعاء داخل العملية نفسها مغلق.
app["تطبيق قائم بـ 32bit"] --> want["يريد استخدام معالجة DLL بـ 64bit"]
want --> deny["لا يمكن التحميل داخل العملية نفسها"]
deny -.-> os["قيد على مستوى نظام التشغيل لا يُتحايل عليه"]
الشكل 1: عملية 32bit لا تحمّل DLL بـ 64bit، لذلك لا يوجد من البداية طريق للاستدعاء داخل العملية نفسها.
2. طريقة الحلّ
من هنا تتوالى مصطلحات متخصّصة، فنضع الحدّ الأدنى من الكلمات أوّلاً.
| المصطلح | المعنى |
|---|---|
| In-proc COM (خادم DLL) | شكل يحمّل مكوّن COM في العملية نفسها مع المستدعي. سريع، لكنّه لا يُحمَّل إن لم يتطابق عدد البتّات |
| Out-of-proc COM (خادم EXE) | شكل يشغّل مكوّن COM كعملية أخرى. يمكن الربط حتى إن اختلف عدد البتّات |
| LocalServer | خادم COM يعمل كعملية أخرى على الجهاز نفسه. يُسجَّل مسار EXE في مفتاح السجلّ LocalServer32 |
| IDL / TypeLib | تعريف يكتب شكل الواجهة (أسماء الدوال وأنواع الوسائط) (IDL)، ونسخته الثنائية (TypeLib). يُستخدم حتى يرى الطرفان «العقد» نفسه |
| marshalling | إعادة تعبئة الوسائط والقيم المعادة إلى شكل يمكن إرساله لعبور حدود العملية. العكس هو unmarshalling |
| Proxy / Stub | شيفرة وكيلة تقوم بالـ marshalling فعلاً. يقوم Proxy في جهة المستدعي، وStub في جهة الخادم |
| WOW6432Node | المكان الذي تُوضع فيه فعلياً محتويات السجلّ الموجّهة لتطبيقات 32bit على Windows بـ 64bit. حتى مع اسم المفتاح نفسه يختلف المحتوى بين جانب 32bit وجانب 64bit |
أساس الحلّ هو الفصل عبر Out-of-proc COM (خادم EXE). تُستدعى DLL بـ 64bit من خادم COM (EXE) بـ 64bit، ويستخدمها تطبيق 32bit عبر COM.
flowchart TB
accTitle: التركيبة الأساسية لجسر COM
accDescr: مخطّط يبيّن تركيبة الفصل إلى عملية أخرى: تطبيق 32bit يستدعي EXE خادم COM بـ 64bit عبر COM، وذلك الخادم يستدعي داخلياً DLL بـ 64bit.
a32["تطبيق 32bit"] -->|"يستدعي عبر COM"| srv["خادم COM بـ 64bit (EXE)"]
srv -->|"يستدعي داخلياً"| dll["DLL بـ 64bit"]
الشكل 2: تُحمَل DLL بـ 64bit في خادم EXE بـ 64bit، ويستخدم تطبيق 32bit ذلك الخادم عبر COM.
التدفّق كالتالي.
- تجهيز COM LocalServer (EXE) بـ 64bit، واستدعاء DLL بـ 64bit من داخله
- مشاركة واجهة COM (IDL/TypeLib) ونشر الأنواع
- يستدعي تطبيق 32bit COM «بأنواع» (التبادل عبر Proxy/Marshal)
غير أنّ هناك نقاط انتباه.
- تسجيل 32bit/64bit منفصل (بما فيه WOW6432Node)
- البنى المخصّصة تحتاج تصميم marshalling
- توجد تكلفة IPC، فالحذر من الاستدعاء عالي التكرار
أي أنّ المسار المعتمد هو «تهريب معالجة 64bit إلى عملية أخرى، والجسر إليها عبر COM».
flowchart TB
accTitle: ثلاث خطوات لتركيب الجسر
accDescr: مخطّط يبيّن تدفّق ثلاث خطوات: تجهيز COM LocalServer بـ 64bit واستدعاء DLL بـ 64bit من داخله، ونشر أنواع الواجهة بـ IDL وTypeLib، ثم استدعاء تطبيق 32bit بأنواع.
s1["تجهيز COM LocalServer بـ 64bit"] --> s2["نشر الأنواع بـ IDL / TypeLib"]
s2 --> s3["تطبيق 32bit يستدعي بأنواع"]
s3 -.-> ps["التبادل عبر Proxy / Marshal"]
الشكل 3: يكتمل الجسر بثلاث مراحل: تجهيز LocalServer، ونشر الأنواع، والاستدعاء ذي الأنواع.
علماً بأنّه إذا كان ما تريد استخدامه في جانب 64bit خادم COM من نوع in-proc أصلاً (DLL تُسجَّل عبر InprocServer32)، فقد تستغني عن كتابة خادم EXE. إذا أضفتَ AppID إلى CLSID وكتبتَ DllSurrogate بسلسلة فارغة تحت مفتاح AppID ذاك، تُحمَّل تلك الـ DLL داخل عملية الـ surrogate المرفقة مع Windows (System32\dllhost.exe إن كانت الـ DLL بـ 64bit)، وتبدو لعميل 32bit كخادم COM من نوع out-of-proc يعمل في عملية أخرى (هذا ليس تسجيل مسار EXE في LocalServer32. بل على العكس، وجود LocalServer32 يمنع استخدام الـ surrogate). الآلية نفسها تنطبق في الاتجاه المعاكس (استخدام COM DLL بـ 32bit من تطبيق بـ 64bit)، ويتحدّد bitness الـ surrogate الذي يُشغَّل من جانب الـ DLL لا من جانب العميل. غير أنّه إن كان ما تريد استدعاءه مجرّد DLL أصلية كما في هذا المقال، فلا يوجد أصلاً خادم COM يُحمَّل في الـ surrogate، لذلك يلزم أسلوب خادم EXE المشروح فيما يلي. خطوات التسجيل مجموعة في القسم 3.5 من مطبات شائعة في تطوير مكوّنات COM وOCX / ActiveX - فخاخ Visual Studio بين 32-bit و64-bit والتسجيل وامتيازات المدير (مكتوب بافتراض DLL بـ 32bit، فيكفي أن تعكس الـ view الذي تُكتب فيه قيمة AppID)، والخطّ الفاصل بين ما يكفي فيه الـ surrogate وما يستوجب كتابة EXE خاص بك مجموعة في كيف تتعامل مع ActiveX / OCX اليوم - جدول قرار للإبقاء والتغليف والاستبدال في القسم 5.2.
3. تدفّق المعالجة (مخطّط تسلسل)
فيما يلي تدفّق الاستدعاء عندما يستدعي تطبيق 32bit معالجة داخل DLL بـ 64bit.
sequenceDiagram
participant App as تطبيق عميل 32bit
box rgba(100,100,255,0.1) تعالجها بنية marshalling في COM بعد التسجيل
participant Proxy as COM Proxy<br/>(جانب 32bit)
participant RPC as RPC/IPC<br/>(تواصل بين العمليات)
participant Stub as COM Stub<br/>(جانب 64bit)
end
participant Server as 64bit COM Server<br/>(EXE)
participant DLL as 64bit DLL
App->>Proxy: ICalcService.Add(1, 2)
rect rgba(100,100,255,0.1)
Note over Proxy: marshalling للوسائط
Proxy->>RPC: بيانات مُسَلسَلة
RPC->>Stub: نقل عبر حدود العملية
Note over Stub: unmarshalling للوسائط
end
Stub->>Server: Add(1, 2)
Server->>DLL: استدعاء دالة أصلية
DLL-->>Server: النتيجة: 3
Server-->>Stub: النتيجة: 3
rect rgba(100,100,255,0.1)
Note over Stub: marshalling للقيمة المعادة
Stub-->>RPC: نتيجة مُسَلسَلة
RPC-->>Proxy: نقل عبر حدود العملية
Note over Proxy: unmarshalling للقيمة المعادة
end
Proxy-->>App: النتيجة: 3
الشكل 4: استدعاء تطبيق 32bit يصل إلى خادم 64bit وDLL بـ 64bit عبر Proxy والتواصل بين العمليات وStub، وتعود النتيجة بالمسار نفسه.
النقاط الجوهرية:
- يستطيع تطبيق 32bit الاستدعاء بأمان الأنواع عبر واجهة
ICalcService - يعبر زمن تشغيل COM حدود العملية باستخدام Proxy/Stub DLL المسجّلة، وmarshaller لـ TypeLib، والـ marshaller القياسي، وغيرها
- توجد تكلفة تواصل بين العمليات، لذلك يُفضَّل التجميع على الاستدعاءات الدقيقة
flowchart TB
accTitle: فكرة حبيبات الاستدعاء
accDescr: مخطّط يبيّن الفكرة: للتواصل بين العمليات تكلفة، فتتراكم إن تكرّرت الاستدعاءات الدقيقة بتكرار عالٍ، ويُفضَّل التجميع لتقليل الأثر.
ipc["تكلفة التواصل بين العمليات"] --> fine["تتراكم بتكرار الاستدعاءات الدقيقة"]
ipc --> batch["يقلّ الأثر إذا مِلت إلى التجميع"]
batch -.-> rec["هذا هو المرغوب"]
الشكل 5: كلّ عبور لحدود العملية له تكلفة، لذلك يُفضَّل تجميع الاستدعاءات بحبيبات أخشن.
4. عيّنة شيفرة (تصوّر)
4.1. الواجهة المشتركة والخادم والعميل
فيما يلي تصوّر للفكرة. للتشغيل تحتاج تسجيل القسم 4.2 بعد هذا.
// الواجهة المشتركة (تعادل IDL)
[ComVisible(true)]
[Guid("7A4B5B23-0A2F-4D2B-9D4D-8A2A92B8B001")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICalcService
{
int Add(int a, int b);
}
// خادم COM LocalServer بـ 64bit (جانب EXE)
[ComVisible(true)]
[Guid("1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("KomuraSoft.CalcService")]
public class CalcService : ICalcService
{
public int Add(int a, int b)
{
// استدعِ DLL بـ 64bit هنا
return a + b;
}
}
// جانب تطبيق 32bit (العميل)
Type t = Type.GetTypeFromProgID("KomuraSoft.CalcService");
var calc = (ICalcService)Activator.CreateInstance(t);
int result = calc.Add(1, 2);
بهذا الشكل يتعامل جانب 32bit «بأنواع». يستخدم COM داخلياً الوكيل/الـ stub ويستدعي عبر IPC.
سبب إضافة [ProgId("KomuraSoft.CalcService")] هو تمكين العميل من البحث بـ Type.GetTypeFromProgID("KomuraSoft.CalcService"). ProgID مجرّد «اسم بديل يقرأه الإنسان»، وما يجد الخادم فعلاً هو تسجيل CLSID المشروح تالياً.
flowchart LR
accTitle: من ProgID حتى الوصول إلى الخادم
accDescr: مخطّط يبيّن ترتيب الحلّ: ProgID الذي يحدّده العميل مجرّد اسم بديل يقرأه الإنسان، ومنه يُستخرج CLSID، وبتسجيل CLSID يُعثر على الخادم الفعلي.
progid["ProgID (اسم بديل يقرأه الإنسان)"] --> clsid["تسجيل CLSID"]
clsid --> srv["يُعثر على الخادم"]
الشكل 6: ProgID اسم بديل عند المدخل، وما يشير إلى الخادم فعلاً هو تسجيل CLSID.
4.2. الحدّ الأدنى من خطوات التسجيل
COM آلية «يسحب فيها زمن تشغيل COM CLSID المسجّل في السجلّ ويشغّله»، لذلك شيفرة غير مسجّلة لا تعمل أبداً (Type.GetTypeFromProgID يعيد null، أو CreateInstance يعطي REGDB_E_CLASSNOTREG). تسجيل خادم EXE (LocalServer) يُختصر إلى المفاتيح الثلاثة التالية.
| ما يُسجَّل | المفتاح | القيمة |
|---|---|---|
| تقابل ProgID ← CLSID | HKEY_CLASSES_ROOT\KomuraSoft.CalcService\CLSID |
{1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11} |
| CLSID ← مسار EXE | HKEY_CLASSES_ROOT\CLSID\{1C9B6F4D-…}\LocalServer32 |
المسار الكامل لـ EXE خادم COM بـ 64bit |
| البحث العكسي CLSID ← ProgID | HKEY_CLASSES_ROOT\CLSID\{1C9B6F4D-…}\ProgID |
KomuraSoft.CalcService |
وهنا الفخّ الذي هو موضوع المقال نفسه. وثائق Microsoft تنصّ على أنّ HKEY_LOCAL_MACHINE\SOFTWARE\Classes مشترك بين تطبيقات 32bit و64bit، بينما مفتاح CLSID الفرعي تحته (وInterface وغيرها) منفصل بين جانب 32bit وجانب 64bit (كيان جانب 32bit هو WOW6432Node). أي أنّ مفتاح ProgID إن كُتب مرّة يُرى من الاثنين، أمّا تسجيل CLSID إن لم يُكتب في الـ view بـ 32bit والـ view بـ 64bit معاً، فلن يجد عميل 32bit الخادم.
flowchart TB
accTitle: الجزء المشترك من السجلّ والجزء المنفصل
accDescr: مخطّط يبيّن أنّ مفتاح ProgID تحت Classes مباشرة إن كُتب مرّة يُرى من 32bit و64bit، بينما التسجيل تحت CLSID منفصل بين view بـ 64bit وview بـ 32bit فيلزم الكتابة في الاثنين، وإن نقص جانب 32bit لا يرى عميل 32bit الخادم.
progk["مفتاح ProgID (تحت Classes مباشرة)"] --> shared["يُرى من الاثنين"]
clsk["التسجيل تحت CLSID"] --> v64["يُكتب في الـ view بـ 64bit"]
clsk --> v32["يُكتب في الـ view بـ 32bit"]
v32 -.-> wow["الكيان هو WOW6432Node"]
v32 -.-> warn["إن نقص لا يُرى من 32bit"]
الشكل 7: مفتاح ProgID مشترك، أمّا ما تحت CLSID فمنفصل لكلّ view بـ 32bit/64bit، لذلك يُسجَّل في الاثنين.
الأضمن في موجه أوامر بامتيازات المدير استخدام /reg:32 و/reg:64 لأمر reg (كتابة Wow6432Node بنفسك في المسار غير مستحسن عند Microsoft).
:: نفِّذ في موجه أوامر بامتيازات المدير
set CLSID={1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11}
set PROGID=KomuraSoft.CalcService
set SERVER=C:\Program Files\KomuraSoft\CalcServer.exe
:: 1) ProgID → CLSID (ما تحت HKLM\SOFTWARE\Classes مباشرة مشترك بين 32/64)
reg add "HKLM\SOFTWARE\Classes\%PROGID%\CLSID" /ve /d "%CLSID%" /f
:: 2) CLSID → LocalServer32 و ProgID (ما تحت CLSID منفصل لـ 32/64 فاكتبه في الاثنين)
:: قيمة LocalServer32 تُدخل مسار الملف التنفيذي مع علامات الاقتباس.
:: إن كتبت /d "%SERVER%" تختفي علامات الاقتباس في تحليل وسائط reg.exe، وتُخزَّن القيمة
:: كما هي C:\Program Files\... . يفسّرها COM كسطر أوامر
:: فيبحث أولاً عن C:\Program.exe المقطوع قبل الفراغ
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\LocalServer32" /ve /d "\"%SERVER%\"" /f /reg:64
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\ProgID" /ve /d "%PROGID%" /f /reg:64
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\LocalServer32" /ve /d "\"%SERVER%\"" /f /reg:32
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\ProgID" /ve /d "%PROGID%" /f /reg:32
:: تحقّق من القيمة المخزَّنة. إن ظهرت "C:\Program Files\..." مع علامات الاقتباس فهي صحيحة
reg query "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\LocalServer32" /ve /reg:64
علامات الاقتباس في LocalServer32 ليست مجرّد مسألة أدب. بدونها يمكن تفسير C:\Program Files\... على أنّه «تمرير Files\... كوسيط إلى C:\Program». والنتيجة أنّه في بيئة يملك فيها طرف صلاحية إنشاء C:\Program.exe قد يُشغَّل ذلك أوّلاً. إذا وضعتَ مساراً فيه فراغ فأرفق علامات الاقتباس حتماً.
flowchart TB
accTitle: كيف يتغيّر التفسير بوجود علامات اقتباس LocalServer32 أو غيابها
accDescr: مخطّط يبيّن أنّ تخزين المسار في قيمة LocalServer32 بلا علامات اقتباس يتيح تفسيراً يقطع قبل الفراغ، وقد يُشغَّل C:\Program.exe أوّلاً في بيئة يمكن وضع ذلك الملفّ فيها، بينما التخزين مع علامات الاقتباس يشغّل EXE المقصود.
noq["تخزين بلا علامات اقتباس"] --> cut["يصحّ تفسير يقطع قبل الفراغ"]
cut --> evil["قد يُشغَّل C:\Program.exe أوّلاً"]
q["تخزين مع علامات الاقتباس"] --> safe["يُشغَّل EXE المقصود"]
الشكل 8: تسجيل مسار فيه فراغ بلا علامات اقتباس يفتح مجالاً لتشغيل ملفّ تنفيذي آخر.
الإلغاء مجرّد حذف المفاتيح نفسها.
set CLSID={1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11}
set PROGID=KomuraSoft.CalcService
reg delete "HKLM\SOFTWARE\Classes\CLSID\%CLSID%" /f /reg:64
reg delete "HKLM\SOFTWARE\Classes\CLSID\%CLSID%" /f /reg:32
reg delete "HKLM\SOFTWARE\Classes\%PROGID%" /f
إضافة إلى ذلك، على جانب EXE عند التشغيل أن يعلن لـ COM «أنا المسؤول عن هذا CLSID». في C/C++ هذا CoRegisterClassObject، وفي .NET Framework RegistrationServices.RegisterTypeForComClients. تسجيل السجلّ لا يرعى إلا حتى تشغيل COM للـ EXE، فإن سقط هذا الإعلان تحصل على فشل مربك: الـ EXE يعمل لكن لا يُنشأ الكائن.
flowchart TB
accTitle: تسجيل السجلّ وإعلان جانب EXE
accDescr: مخطّط يبيّن أنّ تسجيل السجلّ يرعى حتى تشغيل COM للـ EXE، ولا يُنشأ الكائن إلا بعد أن يعلن EXE المشغَّل أنّه المسؤول عن هذا CLSID، وإن سقط الإعلان يفشل الإنشاء رغم تشغيل EXE.
regd["تسجيل السجلّ"] --> boot["COM يشغّل EXE"]
boot --> ann["EXE يعلن أنّه مسؤول CLSID"]
ann --> ok["يمكن إنشاء الكائن"]
boot -.->|"إن سقط الإعلان"| ng["فشل: يعمل لكن لا يُنشأ"]
الشكل 9: تسجيل السجلّ يصل إلى تشغيل EXE، وما بعده لا يُنشئ الكائن دون إعلان EXE نفسه.
علماً بأنّه للتجربة أثناء التطوير يمكنك الكتابة بالبنية نفسها في HKCU\SOFTWARE\Classes بدل HKLM دون امتيازات مدير (HKEY_CLASSES_ROOT هو view مركّب من HKLM وHKCU). غير أنّ HKCU\SOFTWARE\Classes\CLSID يُعامل أيضاً معاملة منفصلة بين 32bit/64bit، لذلك لا يتغيّر لزوم الكتابة في الـ viewين.
4.3. طريقة البناء تختلف بين .NET Framework و.NET (5 وما بعده)
الشيفرة أعلاه C#، لكن الخطوات تتغيّر كثيراً بحسب أيّ .NET تستخدم. الخلط هنا يوقعك في مأزق.
| .NET Framework | .NET (Core 3.0 / 5 وما بعده) | |
|---|---|---|
| أداة التسجيل | يوجد RegAsm.exe (لكنّ ما يُنشأ هو تسجيل InprocServer32 لـ in-proc، لذلك تكتب LocalServer32 بنفسك في النهاية) |
لا توجد أداة تعادل RegAsm |
| النشر القياسي لـ COM | صفات على التجميعة ثمّ RegAsm |
توليد *.comhost.dll بـ <EnableComHosting>true</EnableComHosting> والتسجيل بـ regsvr32 (in-proc فقط) |
| توليد TypeLib (.tlb) | يمكن التوليد بـ TlbExp / RegAsm /tlb |
غير مدعوم. تكتب IDL يدوياً وتترجمه بـ MIDL (من .NET 6 يمكن تضمين .tlb الجاهز في comhost) |
| تحديد CLSID | يمكن حذفه | التصريح بـ CLSID إلزامي للأصناف التي يولّدها COM |
| معاملة AnyCPU | يمكن استخدامه من عملاء 32bit و64bit | *.comhost.dll المرفق 64bit افتراضياً، لذلك لا يُستخدم إلا من عميل 64bit |
تركيبة هذا المقال (خادم EXE) خارج نطاق EnableComHosting القياسي في .NET (5 وما بعده)، لذلك تكتب معالجة التسجيل بنفسك. عيّنة Microsoft الرسمية OutOfProcCOM نقطة انطلاق إن ركّبت في جانب .NET.
flowchart TB
accTitle: المسار عند تركيب خادم EXE في .NET (5 وما بعده)
accDescr: مخطّط يبيّن المسار: تركيبة خادم EXE في هذا المقال خارج نطاق EnableComHosting القياسي في .NET 5 وما بعده، لذلك تكتب معالجة التسجيل بنفسك، وعيّنة OutOfProcCOM الرسمية نقطة الانطلاق.
exe["خادم EXE في .NET (5 وما بعده)"] --> range["خارج نطاق EnableComHosting القياسي"]
range --> self["تكتب معالجة التسجيل بنفسك"]
self -.-> smp["عيّنة OutOfProcCOM الرسمية نقطة الانطلاق"]
الشكل 10: خادم EXE في .NET (5 وما بعده) خارج نطاق الوظيفة القياسية، لذلك تفترض كتابة معالجة التسجيل بنفسك.
5. عيّنة الشيفرة الكاملة
عيّنة تنفّذ الفكرة أعلاه بشكل يعمل فعلاً منشورة على GitHub.
Call64bitDLLFrom32bitProc - GitHub
يتضمّن المستودع ما يلي:
- Call64bitDLLFrom32bitProc/ - COM LocalServer بـ 64bit (EXE)
- X64DLL/ - DLL بـ 64bit (المعالجة الفعلية)
- X86App/ - عميل 32bit (WinForms)
- scripts/ - سكربتات تسجيل خادم COM وإلغائه
إذا بنيتَ وسجّلت وفق خطوات README يمكنك التحقّق فعلاً من استدعاء DLL بـ 64bit من عملية 32bit.
6. خلاصة
جسر COM ليس علاجاً لكلّ شيء، بل تركيبة ينفصل فيها بوضوح ما يناسبه وما لا يناسبه. قبل اعتمادها طابق حالتك على الجدول التالي.
| حالات مناسبة | حالات غير مناسبة |
|---|---|
| لا يمكن إعادة بناء تطبيق 32bit نفسه (تكلفة التعديل لا تتناسب) | يمكن أصلاً إعادة بناء جانب 32bit بـ 64bit (هذا الأقصر) |
| الاستدعاء بحبيبات خشنة (صورة واحدة لكلّ استدعاء، أو ملفّ واحد لكلّ استدعاء، إلخ) | تدوير استدعاءات دقيقة بتكرار عالٍ، مثل عشرات الآلاف عنصراً عنصراً (تكلفة IPC تصير مهيمنة) |
| ما يُتبادل أرقام أو سلاسل أو مصفوفات وأنواع يسهل عمل marshalling لها | ذهاب وإياب كثير لمؤشّرات خام أو بنى مخصّصة معقّدة |
| تريد إبقاء التطبيق نفسه حيّاً حتى إن سقطت معالجة جانب 64bit (فصل العملية مزيّة) | لا تريد كتابة معالجة استعادة لتعطّل الخادم أو إعادة تشغيله |
| تريد الإبقاء على استدعاء ذي أنواع (IntelliSense وفحص وقت الترجمة) | يكفي معالجة دفعية لمرّة واحدة، والتمرير عبر الإدخال/الإخراج القياسي أو ملفّ كافٍ |
كقلب الصفّ الأخير، يستحقّ دائماً النظر في البديل الساذج: «جعل معالجة جانب 64bit مجرّد EXE لوحدة تحكّم، والتبادل بالوسائط والملفّات». جسر COM ينفع عندما تريد الإبقاء على استدعاء ذي أنواع، وعندما تريد استدعاء خادم يحمل حالة مرّات كثيرة.
flowchart TB
accTitle: نقطة الانفصال بين جسر COM والبديل الساذج
accDescr: مخطّط يبيّن نقطة الانفصال: إن لزم الإبقاء على استدعاء ذي أنواع أو استدعاء خادم يحمل حالة مرّات كثيرة ينفع جسر COM، وإن كفت معالجة دفعية لمرّة واحدة يستحقّ النظر في بديل ساذج يجعل الأمر EXE لوحدة تحكّم ويتبادل بالوسائط والملفّات.
q{"هل يلزم استدعاء ذو أنواع أو الاحتفاظ بالحالة؟"} -->|"نعم"| br["جسر COM"]
q -->|"لا"| alt["EXE لوحدة تحكّم وتبادل بالوسائط والملفّات"]
الشكل 11: لزوم الاستدعاء ذي الأنواع والاحتفاظ بالحالة هو نقطة الانفصال بين الجسر والبديل الساذج.
كخطوة تالية نوصي بهذا الترتيب.
- أوّلاً استنسخ مستودع العيّنة في الباب 5، وابنِ وسجّل وفق README، واصنع لديك حالة واحدة تعمل.
- اختر دالة واحدة من DLL بـ 64bit لديك، وأضف إلى العيّنة دالة واحدة في واجهة تعادل
ICalcService، وأمرّرها. - إذا مرّت، قِس عدد الاستدعاءات وحجم البيانات لكلّ مرّة. إن أنهيت هنا حكم التصميم «الميل إلى حبيبات أخشن» (جمع استدعاءات متعدّدة في واحد) أوّلاً، قلّ الرجوع لاحقاً.
7. مراجع
- نظرة عامّة على Component Object Model (COM) https://learn.microsoft.com/en-us/windows/win32/com/component-object-model–com–portal
- تسجيل COM LocalServer32 https://learn.microsoft.com/en-us/windows/win32/com/localserver32
- أساسيات واجهة COM https://learn.microsoft.com/en-us/windows/win32/com/the-component-object-model
- COM Interop (الاستخدام من .NET) https://learn.microsoft.com/en-us/dotnet/standard/native-interop/cominterop
- موجّه سجلّ WOW64 (
HKLM\SOFTWARE\Classesمشترك، وما تحتCLSIDمنفصل بين 32/64) https://learn.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys - نشر مكوّنات .NET (Core / 5 وما بعده) إلى COM https://learn.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
فخاخ التسجيل وbitness في تطوير COM و OCX / ActiveX
نرتّب من منظور عمليّ ما يسهل التعثّر فيه في تطوير COM و OCX و ActiveX: 32-bit/64-bit، Visual Studio 2022، regsvr32/Regasm، صلاحيات المسؤو...
أسباب توقّف ActiveX على Office 2024 / Microsoft 365 وخطوات التحقّق
عندما يتوقّف ActiveX على Office 2024 / Microsoft 365، نرتّب ترتيب الفصل بين التعطيل الافتراضيّ و 32bit/64bit وتسجيل COM وDLL التابعة و IE...
WinRT هو COM ── IInspectable و.winmd وإسقاط اللغات، ولماذا لا يزال WinUI يركب عقداً ثنائياً
WinRT ليس وقت تشغيل مُداراً؛ بل ABI أُضيفت إليه بيانات وصفية (.winmd) وإسقاطات لغات فوق COM. من علاقة IUnknown وIInspectable إلى تهيئة HW...
ما كائن OLE؟ ── آلية التضمين والربط ومطبّات مستندات الأعمال
حقيقة ميزة تضمين جدول Excel في Word هي كائن OLE. نشرح الفرق بين التضمين والربط، والملفّ المركَّب والتخزين المنظَّم، وآلية In-Place Activa...
كيف تعمل الحافظة والسحب والإفلات ── معالجة نقل بيانات OLE بشكل صحيح في تطبيقات الأعمال
لماذا ينهار لصق Excel ويفشل اللصق بعد إغلاق المصدر: تنسيقات الحافظة، والعرض المؤجَّل، وسحب وإفلات OLE، وسياسات السجلّ ومزامنة السحابة.
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
دعم إعادة استخدام الأصول القديمة وترحيلها
لأنه حديث عن مدّ جسر نحو جانب 64bit مع الإبقاء على أصول 32bit، فهو موضوع يتّصل مباشرة بدعم إعادة استخدام الأصول القديمة وترحيلها.
الاستشارات التقنية ومراجعة التصميم
إن كنت في مرحلة تريد فيها أولاً ترتيب جسر COM وكيفية رسم حدود العمليات، فيمكن الموازنة بين الخيارات ضمن الاستشارة التقنية ومراجعة التصميم.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- هل يمكن استدعاء DLL بـ 64bit مباشرة من تطبيق 32bit؟
- لا. عملية 32bit لا تحمّل DLL بـ 64bit، وهذا قيد على مستوى نظام التشغيل، لا أمر يمكن التحايل عليه. طريق الاستدعاء داخل العملية نفسها مغلق من البداية، لذلك يلزم فصل معالجة جانب 64bit إلى عملية أخرى.
- كيف تستخدم وظائف DLL بـ 64bit من تطبيق 32bit؟
- الفصل عبر Out-of-proc COM (خادم EXE) هو المسار المعتمد. تُستدعى DLL بـ 64bit من COM LocalServer (EXE) بـ 64bit، ويستخدمها تطبيق 32bit عبر واجهة COM بأنواع. تُشارَك واجهة COM (IDL/TypeLib) لنشر الأنواع، ويتولّى زمن تشغيل COM عبور الحدود بالوكيل/الـ stub والتواصل بين العمليات.
- ما نقاط الانتباه في تركيبة جسر COM؟
- ثلاث نقاط رئيسة. تسجيل 32bit/64bit منفصل (بما فيه WOW6432Node)، والبنى المخصّصة تحتاج تصميم marshalling، وهناك تكلفة تواصل بين العمليات تستدعي الحذر من الاستدعاءات الدقيقة عالية التكرار. الأفضل تجميع العمل دفعة واحدة بدل تمرير استدعاءات صغيرة كثيرة.
- هل توجد عيّنة شيفرة تعمل فعلاً؟
- نعم. مستودع Call64bitDLLFrom32bitProc على GitHub ينشر عيّنة كاملة تضمّ COM LocalServer (EXE) بـ 64bit، وDLL بـ 64bit، وعميلاً 32bit (WinForms)، وسكربتات تسجيل خادم COM وإلغائه. إذا بنيتَ وسجّلت وفق خطوات README يمكنك التحقّق من استدعاء DLL بـ 64bit من عملية 32bit.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.