سجل التعديلات (3 تحديثات، آخر تحديث 2 Sep، 2026)
سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.
- أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
- أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
- أضيف شرح لـ DllSurrogate: لماذا لا تستطيع عملية 64-bit تحميل InprocServer32 بـ 32-bit حتى بعد نجاح regsvr32، وكيف أن إضافة AppID إلى الـ CLSID وقيمة DllSurrogate فارغة تحت ذلك الـ AppID تستضيف الـ DLL داخل dllhost.exe دون كتابة EXE جديد، ولماذا تتبع بنية dllhost.exe الذي يعمل بنية الـ DLL لا بنية العميل، ولماذا يفوز LocalServer32 المسجَّل على الوسيط، وكيف يتم التحقق من النتيجة. كما يوضّح النص أن الوسيط لا يلغي فرق البنية: ما يزول هو قيد العملية الواحدة فقط، وتصبح الاستدعاءات خارج العملية بتكلفة marshaling و IPC. قراءة النسخة السابقة لهذا التحديث (DOI: 10.5281/zenodo.21621504)
- النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621503)
هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.
小村 豪 (2026). فخاخ التسجيل وbitness في تطوير COM و OCX / ActiveX. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621503 https://comcomponent.com/ar/blog/2026/04/15/001-com-ocx-activex-pitfalls-visual-studio-bitness-admin-rights/
- DOI (أحدث نسخة)
- 10.5281/zenodo.21621503
- DOI (هذه النسخة)
- 10.5281/zenodo.22240918
في العمل على مكوّنات COM و OCX / ActiveX، تتعثّر الفِرَق في الغالب لا في الشيفرة نفسها بل في الحدود المحيطة بـ بيئة التشغيل والتسجيل والاستضافة والصلاحيّات.
تبدو الأعراض النموذجيّة هكذا:
- البناء ينجح، لكنّ التشغيل يفشل بـ
0x80040154 - يعمل على جهاز المطوّر، لكن لا يعمل على جهاز PC آخر
- يعمل في وقت التشغيل، لكنّ Visual Studio Designer وحده يموت
- يعمل عند التشغيل بصلاحيّات المسؤول، لكنّه ينكسر بصلاحيّات عاديّة
- تواصل تشغيل
regsvr32، ومع ذلك لا يُحَلّ شيء
في كثير من الحالات، ليست هذه أخطاءً مستقلّة. إنّها علامات على أنّ شرطًا مسبقًا من شروط COM لا يصطفّ بشكل صحيح.
إن أردت ترتيب المصطلحات أوّلًا، فمن المفيد البدء بـ ما هي COM / ActiveX / OCX: الفروق والعلاقات.
يكمل هذا المقال من تلك النقطة ويركّز على الأماكن التي يتعثّر الناس فيها فعلًا في الممارسة، بما في ذلك مشاكل bitness الخاصّة بـ Visual Studio وصلاحيّات المسؤول.
1. الإجابة الموجزة
إن قُلْنَاها بطريقة مفيدة في العمل الفعليّ، فإنّ النقاط الجوهريّة هي التالية.
- معظم إخفاقات COM / OCX / ActiveX سببها أقلّه منطق العمل، وأكثره عدم تطابق في bitness (32-bit / 64-bit) وموقع التسجيل وعمليّة المضيف والصلاحيّات.
- Visual Studio 2022 هو عمليّة 64-bit، لذا فإنّ مسارات التكامل في وقت التصميم التي كانت تعمل سابقًا تحت افتراضات 32-bit القديمة قد تفشل الآن كما هي.12
regsvr32ليس أمرًا سحريًّا يُسجّل كلّ شيء. إنّه لخوادم COM الأصليّة من نوع in-process مثل DLL و OCX. إن كنت تكشف تجميعات .NET Framework لـ COM، فالأداة هيRegasm.exe؛ وفي .NET 5+ / .NET 6+ / .NET 8+، يصبح المسار تسجيل الـ.comhost.dllالمُولَّد.3456- “يعمل عند التشغيل كمسؤول، فلا بدّ أنّه على ما يرام” خطير. كثيرًا ما يعني ذلك فقط أنّ المكوّن مرئيّ بالصدفة من خلال تسجيل per-user، أو أنّ شيئًا كان ينبغي تثبيته بشكل صحيح قد سُجِّل يدويًّا فقط على جهاز المطوّر.378
لذا، عند التعامل مع COM / OCX / ActiveX، الأكثر أمانًا أن تبدأ من هذه المحاور الأربعة:
- أيّ عمليّة تستضيفه
- هل تلك العمليّة 32-bit أم 64-bit
- أين سُجِّل (HKCU / HKLM، عرض 32-bit / عرض 64-bit)
- هل العمليّة أو مسار التنفيذ يتطلّب فعلًا صلاحيّات المسؤول
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 34، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
2. بالعمل عكسيًّا من الأعراض، يبدو الأمر عادةً هكذا
| العَرَض | أوّل ما يُشتبه به | السبب الحقيقيّ في الغالب |
|---|---|---|
0x80040154 Class not registered |
غير مُسجَّل | في الواقع كثيرًا ما يكون “مُسجَّلًا فقط في عرض الـ bitness الآخر” أو “مُسجَّلًا فقط لذلك المستخدم” |
DllRegisterServer failed: 0x80070005 |
صلاحيّات غير كافية | محاولة التسجيل كمستخدم قياسيّ، أو تشغيل تسجيل post-build دون رفع |
| ينكسر VS2022 Designer وحده | قيود Designer | لا يستطيع Visual Studio 64-bit تحميل COM / ActiveX 32-bit مباشرةً |
| يعمل فقط عند التشغيل كمسؤول | مشكلة صلاحيّات | كثيرًا ما تكون المشكلة الحقيقيّة عدم تطابق نطاق التسجيل أو تصميم التثبيت، لا الصلاحيّات بحدّ ذاتها |
| تطبيق 64-bit لا يستطيع استدعاء OCX 32-bit | قيود معماريّة في COM | يجب أن تتطابق خوادم in-process مع bitness المضيف |
| يعمل على خيط الـ UI لكنّه يتجمّد على خيط خلفيّ | نموذج خيوط | تُنتَهَك افتراضات STA / MTA أو CoInitializeEx أو حلقة الرسائل |
رسميًّا، 0x80040154 هو REGDB_E_CLASSNOTREG، أي حرفيًّا Class not registered.9
و 0x80070005 من regsvr32 موثّق أيضًا كحالة تنقص فيها صلاحيّات المسؤول ولا تستطيع العمليّة الكتابة إلى registry أو System32.10
النقطة المهمّة هي عدم الثقة باسم الخطأ حرفيًّا أكثر من اللازم.
على سبيل المثال، Class not registered لا يعني دائمًا “غير مُسجَّل تمامًا”. قد يحدث أيضًا حين يكون الفصل مُسجَّلًا فقط في عرض registry آخر أو فقط في موقع per-user لا تراه العمليّة الحاليّة.117
3. bitness الخاصّ بـ Visual Studio فخّ كبير
3.1. صار Visual Studio 2022 بـ 64-bit
هذا أحد أكبر الفخاخ في تطوير COM / ActiveX الحاليّ.
Visual Studio 2022 هو 64-bit فقط لـ devenv.exe.1
هذا يعني أنّ جانب Visual Studio من تجربة وقت التصميم لـ WinForms لا يستطيع تحميل المكوّنات 32-bit مباشرةً. تذكر Microsoft صراحةً أنّ Visual Studio 2022 هو عمليّة 64-bit ولذلك لا يمكنه تحميل مكوّنات .NET / COM / ActiveX 32-bit مباشرةً.2
بمعنى آخر، الإعداد الذي كان يعمل تحت افتراضات مثل:
- المشروع x86
- ضابط ActiveX المرجعيّ أيضًا x86
- Visual Studio نفسه 32-bit
يتحوّل إلى شيء مختلف في VS2022:
- لا يزال تطبيق وقت التشغيل يستطيع العمل كـ x86
- لكنّ Designer يعمل الآن داخل Visual Studio 64-bit
ينشأ عن ذلك عدم تطابق.
والنتيجة هي الحالة المزعجة بشكل خاصّ حيث لا يزال وقت التشغيل يعمل، لكنّ Designer وحده يموت.2
3.2. التحوّل إلى AnyCPU لا يصلح الأمر تلقائيًّا
هذا سوء فهم شائع آخر.
AnyCPU ليس سحرًا يجعل كلّ تبعيّة محايدة أيضًا.
توضّح Microsoft أيضًا أنّه حتى إن بدا المكوّن نفسه AnyCPU، فإنّ وقت التصميم في Visual Studio 2022 لا يزال ينكسر إن كان شيء أسفله يعتمد في النهاية على COM / ActiveX 32-bit.2
لذا، حين لا يجعل التحوّل إلى AnyCPU الخطأ يختفي، يكون من الأسرع غالبًا أن تشتبه بـ:
- ما إن كانت تلك التجميعة تعتمد على شيفرة أصليّة 32-bit في الأسفل
- ما إن كان ضابط ActiveX / OCX مثبّتًا على x86
- ما إن كانت هناك شيفرة تُحمَّل فقط في وقت Designer
3.3. فخّ System32 و SysWOW64
في Windows x64، تتباعد الأسماء عن الواقع بما يكفي لإرباك الناس.
تشرح Microsoft Learn أنّ %windir%\\System32 على Windows x64 مخصّص لـ التطبيقات 64-bit. أمّا الجانب 32-bit فيُعاد توجيهه إلى مكان آخر بواسطة WOW64 file-system redirector.12
يعمل registry بشكل مشابه: WOW64 registry redirector يقدّم عروضًا منطقيّة مختلفة للشيفرة 32-bit و 64-bit.11
لهذا السبب من الأكثر أمانًا، عند تشخيص المشاكل محلّيًّا، أن تجعل أيّ bitness من regsvr32 تستخدم صريحًا تمامًا.
# When you want to register a 64-bit DLL / OCX
C:\Windows\System32\regsvr32.exe vendor.ocx
# When you want to register a 32-bit DLL / OCX on x64 Windows
C:\Windows\SysWOW64\regsvr32.exe vendor.ocx
الجانب المزعج من هذا الفخّ هو أنّك قد تُسجِّل في الجانب الخطأ وتشعر مع ذلك أنّك سجّلته بنجاح.
تصبح النتيجة النهائيّة:
- نجح
regsvr32 - لكنّ التطبيق لا يزال يحصل على
0x80040154 - يبدو registry وكأنّ المُدخل موجود
- لكنّك تنظر إلى العرض الخطأ
3.4. لا يستطيع regsvr32 تسجيل كلّ شيء
هذا أيضًا يُساء فهمه على نطاق واسع.
عادةً تُصدِّر خوادم COM الأصليّة من نوع in-process DllRegisterServer / DllUnregisterServer وتدعم التسجيل الذاتيّ.3
regsvr32 هو الأداة لحالات DLL / OCX تلك.4
في المقابل، إن كنت تريد كشف تجميعة .NET Framework لـ COM، فإنّ الأداة العاديّة هي Regasm.exe. توضّح Microsoft Learn صراحةً أنّ Regasm.exe يُستخدم لتسجيل التجميعات لاستخدام COM.514
ثمّ تتغيّر القصّة مرّة أخرى في .NET 5+ / .NET 6+ / .NET 8+: مع <EnableComHosting>true</EnableComHosting>، يُولّد البناء *.comhost.dll، وذلك الـ DLL المُولَّد هو ما تُسجّله بـ regsvr32.6
بشكل تقريبيّ، الانقسام هو:
| ما تريد كشفه | طريقة التسجيل النموذجيّة |
|---|---|
| native C++ DLL / OCX | regsvr32 |
| تجميعة .NET Framework مكشوفة لـ COM | Regasm.exe |
| كشف COM في .NET 5+ / 6+ / 8+ | تسجيل الـ .comhost.dll المُولَّد بـ regsvr32 |
يُشغَّل Regasm.exe من Developer Command Prompt / Developer PowerShell. أكثر ما يُستخدَم هو الأشكال الثلاثة التالية.14
:: 1) تسجيل الأصناف العامّة داخل التجميعة
regasm myTest.dll
:: 2) توليد type library وتسجيله أيضاً (للمستهلكين الذين يحتاجون TLB، مثل VBA / VB6)
regasm myTest.dll /tlb:myTest.tlb
:: 3) كتابة المحتوى إلى ملفّ .reg للفحص دون تغيير الـ registry مباشرة
regasm myTest.dll /regfile:myTest.reg
الإلغاء يتمّ بـ /unregister. type library المسجَّل بـ /tlb يُلغى بتحديد /tlb و /unregister معاً.
regasm myTest.dll /tlb:myTest.tlb /unregister
هنا قيدان يسهل الوقوع فيهما.14
- التجميعة التي لا تُدخَل في GAC تحتاج
/codebase. لأنّ/codebaseيسجّل مسار الملفّ وقت التسجيل في الـ registry، فإنّ نقل التجميعة لاحقاً يجعل التنشيط يفشل. كما توصي Microsoft بقوّة أن تكون أيّ تجميعة تُسجَّل بـ/codebaseذات اسم صارم (strong name). - لا يمكن الجمع بين
/regfileو/unregisterأو/tlb. وما يكتبه/regfileهو إدخالات الأصناف المُدارة فقط؛ لا يُخرَجTypeLibIDوInterfaceID. إن افترضت أنّ «توزيع ملفّ.regيكفي لإتمام التسجيل»، فلن يكفي.
عندما تختلط هذه، يكون نمط الإخفاق الشائع جدًّا:
- تشغيل
regsvr32على DLL مُدارة - بوضوح،
DllRegisterServerمفقود - الاستنتاج بأنّ الـ DLL لا بدّ أنّها معطوبة
ذلك الاستنتاج عادةً خاطئ.
في العوالم التي تحتاج type library أيضًا، مثل VBA و VB6 وبعض مستهلكي early-binding، فإنّ توليد TLB وتسجيله هو مسألة منفصلة أخرى. في .NET Framework، يستطيع Regasm.exe /tlb توليد type library وتسجيلها، وتشرح Microsoft أيضًا أنّ “تسجيل النوع” و “تسجيل type library” نشاطان منفصلان.15
3.5. حين تريد إخراج in-proc بـ 32-bit من عمليّة 64-bit - DllSurrogate
كان 3.3 و 3.4 عن سؤال “هل موقع التسجيل ووسيلته متطابقان”.
هنا نضيف مخرجًا واحدًا لحالة التسجيل صحيح لكنّ الـ bitness لا يتطابق.
الخلاصة أوّلًا.
- حتى إن نجح
regsvr32، فإنّ العمليّة 64-bit لا تستطيع تحميلInprocServer32الخاصّ بـ 32-bit. هذا ليس خللًا في التسجيل، بل هو شرط in-proc نفسه. - إن أردت استخدام تلك الـ DLL من الجانب 64-bit دون كتابة EXE جديد، فأصغر علاج هو إضافة قيمة
AppIDإلى الـ CLSID، وكتابةDllSurrogateبسلسلة فارغة تحت مفتاحAppIDذاك.16 - والذي يُشغَّل حينها ليس bitness جانب العميل، بل
dllhost.exeبالـ bitness الخاصّ بالـ DLL الذي يشير إليهInprocServer32.
لماذا يستحيل الأمر ما دام in-proc
إنّ “تطبيق 64-bit لا يستطيع استدعاء OCX 32-bit” المذكور في جدول الأعراض في الفصل 2 يرتدي وجه 0x80040154 نفسه، لكنّ مضمونه مختلف.
InprocServer32 هو حرفيًّا تحديد لـ “خادم يعمل داخل عمليّة المستدعي”. ولن يحدث أبدًا أن تحمّل عمليّة 64-bit الـ DLL بـ 32-bit المكتوبة هناك، مهما أصلحت في الـ registry. وحتّى انقسام عروض الـ registry الذي رأيناه في 3.3 يعود في أصله إلى هذا القيد.
لذا فما يلي ليس عن “إصلاح التسجيل”، بل عن “إخراجه إلى عمليّة أخرى”.
ما الذي يفعله الـ surrogate
DllSurrogate هو تحديدٌ يجعل خادم الـ DLL من نوع in-proc يُحمَّل داخل عمليّة surrogate ويُقدَّم بوصفه Local Server.17
وإن جعلت القيمة سلسلة فارغة، فسيُستخدم الـ surrogate الافتراضيّ المرفق مع Windows (dllhost.exe).18
إن كانت الـ DLL بـ 32-bit، فسيُشغَّل surrogate بـ 32-bit، وتُحمَّل الـ DLL داخله in-proc كما كانت دائمًا. أمّا العميل 64-bit فيتعامل مع الكائن من خارج تلك العمليّة عبر proxy.
هذه النقطة يُساء فهمها بسهولة، فلنكتبها بوضوح.
الـ surrogate لا يمحو الفرق في الـ bitness. الذي يختفي هو قيد “وجوب التحميل في العمليّة نفسها” فقط. تصبح الاستدعاءات out-of-proc، وتُضاف إليها تكلفة الـ marshaling والاتّصال بين العمليّات. وكشرط مسبق، يلزم أيضًا أن تكون الأنواع المتبادَلة قابلة للـ marshaling (إمّا IDispatch، أو proxy / stub مُسجَّل، أو نوع يستطيع الـ marshaler القياسيّ التعامل معه).
ونقطة أخرى. ما يسهل التعامل معه عبر الـ surrogate هو كائنات automation التي تكتمل باستدعاءات الدوالّ والأحداث. أمّا الـ OCX المرئيّ الذي تضعه على نموذج ليرسم نفسه فيفترض أن تكون نافذته داخل عمليّة المضيف، ولذلك لا يحلّه هذا العلاج وحده.
flowchart TB
accTitle: هل ينتهي التفعيل إلى in-proc أم إلى الـ surrogate
accDescr: رسم يوضّح أنّه عند الطلب بـ CLSCTX يتضمّن in-proc، إن وُجد InprocServer32 في CLSID ضمن العرض نفسه الخاصّ بالعميل فيُحمَّل أوّلًا in-proc، وإن لم يوجد وكان هناك تسجيل لخادم EXE فيُشغَّل ذلك قبل الـ surrogate، وإن لم يوجد تسجيل EXE ووُجد DllSurrogate فارغ فيُشغَّل dllhost بالـ bitness الخاصّ بالـ DLL، وإن لم يوجد أيٌّ منها فلا يمكن التفعيل.
req["العميل يطلب التفعيل"] --> ctx["الطلب بـ CLSCTX يتضمّن in-proc"]
ctx --> q1{"هل يوجد InprocServer32 في CLSID ضمن العرض نفسه"}
q1 -->|"يوجد"| inproc["يُحمَّل in-proc كما كان دائمًا"]
q1 -->|"لا يوجد"| q2{"هل يوجد تسجيل لخادم EXE"}
q2 -->|"يوجد"| exe["يُشغَّل EXE قبل الـ surrogate"]
q2 -->|"لا يوجد"| q3{"هل يوجد DllSurrogate فارغ"}
q3 -->|"يوجد"| host["يُشغَّل dllhost بالـ bitness الخاصّ بالـ DLL"]
q3 -->|"لا يوجد"| err["يتعذّر التفعيل 0x80040154"]
الشكل 1: تتفرّع عملية التنشيط أولاً بحسب ظهور InprocServer32 بالبنية نفسها، ولا تنتقل إلى خادم EXE ثم إلى DllSurrogate الفارغ إلا عند غيابه.
الحدّ الأدنى من التسجيل
تُعدِّد Microsoft Learn شروط تحميل خادم الـ DLL داخل surrogate على النحو التالي.16
- في مفتاح CLSID توجد قيمة
AppID، ويوجد مفتاحAppIDالمقابل لها - استدعاء التفعيل يرفع
CLSCTX_LOCAL_SERVER، و لا يوجد في مفتاح CLSID أيٌّ منLocalServer32/LocalServer/LocalService - يوجد
InprocServer32في مفتاح CLSID - الـ DLL التي يشير إليها
InprocServer32موجودة فعلًا - توجد قيمة
DllSurrogateتحت مفتاحAppID
وإن أردت تشغيل تلك الـ DLL وحدها في surrogate واحد، فإنّ الطريقة التي توصي بها Microsoft هي جعل الـ AppID بالـ GUID نفسه الخاصّ بالـ CLSID.16
ثلاثة أسطر تكفي للتسجيل. المثال أدناه لحالة استخدام COM DLL بـ 32-bit من مضيف 64-bit، والـ GUID فيه مجرّد عنصر نائب. والشرط المسبق هو أن يكون InprocServer32 قد أُدخل مسبقًا بـ regsvr32 من جانب SysWOW64 (انظر 3.3).
:: نفّذ هذا في موجّه أوامر بصلاحيّات المسؤول
set CLSID={11111111-2222-3333-4444-555555555555}
set APPID={11111111-2222-3333-4444-555555555555}
:: 1) أضف AppID إلى الـ CLSID. ما تحت CLSID منفصل بين 32 و 64، لذا حدّد العرض صراحةً
:: وللاتّجاه المعاكس، أي استخدام COM DLL بـ 64-bit من مضيف 32-bit، اقرأ هذا على أنّه /reg:64
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%" /v AppID /t REG_SZ /d "%APPID%" /f /reg:32
:: 2) أنشئ مفتاح AppID المقابل. Classes\AppID مشترك بين 32 و 64، فلا حاجة إلى تحديد العرض
reg add "HKLM\SOFTWARE\Classes\AppID\%APPID%" /f
:: 3) اجعل DllSurrogate من نوع REG_SZ بقيمة فارغة. إن حذفت /d فسيُنشأ بسلسلة فارغة
reg add "HKLM\SOFTWARE\Classes\AppID\%APPID%" /v DllSurrogate /t REG_SZ /f
لماذا تكون سلسلة فارغة
DllSurrogate من نوع REG_SZ، و القيمة نفسها هي مسار الـ surrogate. فإن جعلتها سلسلة فارغة (أو NULL) استُخدم الـ surrogate الافتراضيّ للنظام، وإن كتبت مسارًا فسيحاول تشغيل الـ surrogate المخصّص الموجود في ذلك المسار.1718
أي أنّ كتابة مسار dllhost.exe بنيّة المساعدة تأتي بنتيجة عكسيّة، لأنّ الإفراغ نفسه هو التحديد الذي يعني “استخدم الافتراضيّ”.
وللعلم، فإنّ HKLM\SOFTWARE\Classes\AppID مفتاح مشترك بين 32-bit و 64-bit منذ Windows 7 / Windows Server 2008 R2، ولا تمييز فيه بين العروض.19
في المقابل، ما تحت CLSID تنقسم فيه العروض، لذا إن كان الخادم بـ 32-bit فيجب وضع قيمة AppID على جانب CLSID في العرض 32-bit. وحين لا ينجح الأمر، تحقّق من هذين الموضعين معًا دائمًا.
قد يبدو هذا متناقضًا مع ما في 3.3 و 6.3 من أنّ “التسجيل على الجانب 32-bit فقط يعني 0x80040154 من عمليّة 64-bit”، لكنّ ذاك كلام عن in-proc. أمّا في التفعيل out-of-proc، فحين لا يبدي العميل ولا الخادم رغبةً في bitness معيّن، يبحث COM عن خادم بالـ bitness المناسب للعميل، وإن لم يجده شغّل الخادم ذا الـ bitness الآخر.20
ولهذا تصحّ هذه الخطوات مع إبقاء جانب CLSID في العرض 32-bit. لا حاجة إلى نسخ InprocServer32 إلى العرض 64-bit.
الـ bitness الخاصّ بـ dllhost.exe الذي يُشغَّل
هنا يقع أكثر سوء الفهم شيوعًا.
يتحدّد الـ bitness الخاصّ بالـ surrogate لا من جانب العميل، بل من جانب الـ DLL التي يشير إليها InprocServer32. وهذا طبيعيّ، لأنّ الـ DLL تُحمَّل in-proc داخل الـ surrogate.
| COM DLL التي تُحمَّل بالداخل | dllhost.exe الذي يُشغَّل |
|---|---|
| 32-bit | %SystemRoot%\SysWOW64\dllhost.exe |
| 64-bit | %SystemRoot%\System32\dllhost.exe |
وإن نظرت إلى سطر الأوامر في Task Manager أو Process Explorer، فستجده قائمًا بالشكل dllhost.exe /Processid:{...}. والـ GUID هنا هو AppID وليس CLSID. فإن بحثت عنه على أنّه CLSID فلن تجده، فانتبه لذلك.
حين يوجد LocalServer32 يفوز الـ EXE
تنصّ Microsoft Learn صراحةً على أنّه حين يوجد LocalServer / LocalServer32 / LocalService، فإنّ تشغيل خادم EXE أو خدمة له الأولويّة دائمًا على تحميل الـ DLL في surrogate.16
أي أنّ الـ surrogate هو “المخرج حين لا يوجد خادم EXE”. وإضافة DllSurrogate إلى CLSID مُسجَّل له خادم EXE بالفعل لن تجعل الـ surrogate يُستخدم. والأولويّة هنا هي في مواجهة الـ surrogate فقط، لا في مواجهة in-proc.
ومن المطمئن أيضًا أن تحيط بأثر ذلك على المستدعين الحاليّين.
فحتى مع إضافة DllSurrogate، تبقى العملاء الذين ينشئون الكائن بـ CLSCTX يتضمّن in-proc على حالهم in-proc كما كانوا، ما دام الـ bitness متطابقًا. ذلك أنّ تمرير عدّة قيم CLSCTX مجموعةً بـ OR يُجرَّب بترتيب التعداد (in-proc → local → remote)، وفي مرحلة in-proc يُستخدم مفتاح InprocServer32 إن كان موجودًا.20
وينطبق هذا على الاستدعاءات التي تمرّر in-proc و local معًا مثل CLSCTX_ALL أو CLSCTX_SERVER. وعلى العكس، المواضع التي تستدعي بـ CLSCTX_INPROC_SERVER وحده لن تتحوّل إلى الـ surrogate حتّى بعد إضافة DllSurrogate.
ومن منظور العميل 64-bit، فإنّ InprocServer32 الذي وُضع في العرض 32-bit غير موجود في العرض 64-bit. فتمرّ مرحلة in-proc بلا توقّف بوصفها “لا يوجد مفتاح مطابق”، ويمضي التفعيل مباشرةً إلى جانب local (الـ surrogate). فالأمر ليس محاولةً فاشلةً لتحميل DLL بـ bitness مختلف.2019
طريقة التحقّق
للتأكّد ممّا إن كان قد أُدخل فعلًا، الأضمن أن تقرأه مرّة أخرى مع تحديد العرض صراحةً.
reg query "HKLM\SOFTWARE\Classes\CLSID\{11111111-2222-3333-4444-555555555555}\InprocServer32" /ve /reg:32
reg query "HKLM\SOFTWARE\Classes\CLSID\{11111111-2222-3333-4444-555555555555}" /v AppID /reg:32
reg query "HKLM\SOFTWARE\Classes\AppID\{11111111-2222-3333-4444-555555555555}" /v DllSurrogate
لا تحذف السطر الأوّل. فـ reg add يُنشئ المفتاح المحدَّد إن لم يكن موجودًا، ولذلك إن أخطأت في كتابة الـ GUID أو سجّلت في عرض آخر، فسينشأ مفتاح CLSID فارغ لا يحمل سوى قيمة AppID، وسيمرّ السطر الثاني بنجاح. ولا يكون تحقّقك تحقّقًا فعليًّا إلّا حين ترى أنّ InprocServer32 موجود في العرض نفسه.
والنتيجة الصحيحة لـ DllSurrogate هي أن يظهر وقيمته فارغة. بعد ذلك أنشئ الكائن من عميل 64-bit وانظر هل يُشغَّل dllhost.exe من جانب SysWOW64.
- لا يزال
0x80040154→ راجع ما إن كانت قيمةAppIDموضوعة على جانب CLSID في العرض 32-bit، وما إن كانت الـ DLL التي يشير إليهاInprocServer32موجودة فعلًا - يُشغَّل
dllhost.exeلكنّ الاستدعاء ينهار → تحقّق من شروط الـ marshaling (IDispatch/ proxy-stub / الـ marshaler القياسيّ) - يُشغَّل EXE آخر → لا يزال
LocalServer32أو ما شابهه موجودًا على ذلك الـ CLSID
إلى هنا ينتهي الكلام عن “التسجيل”
أمّا هل يكفي الـ surrogate، أم ينبغي كتابة helper EXE خاصّ بك على الجانب 32-bit، فذلك ليس مسألة تسجيل بل مسألة اختيار البنية.
وقد رتّبنا ذلك في القسم 5.2 «الرغبة في إدخال OCX بـ 32-bit إلى الجانب 64-bit» من كيف تتعامل مع ActiveX / OCX اليوم - جدول قرار للإبقاء أو التغليف أو الاستبدال.
4. مشاكل صلاحيّات المسؤول فخّ كبير آخر
4.1. التسجيل موصول بـ post-build وينجح فقط تحت المسؤول
في بناءات C++ مع Visual Studio، من الطبيعيّ تمامًا بمعنى آليّ استدعاء regsvr32.exe من build events أو custom build steps. تُظهر Microsoft Learn حتى أمثلة post-build تستخدم regsvr32.exe.21
لكنّ الممكن و الآمن ليسا الشيء نفسه.
عندما يشغّل مستخدم قياسيّ regsvr32، قد يفشل بـ 0x80070005 لأنّه لا يستطيع الكتابة إلى registry أو System32. تصف KB من Microsoft أيضًا السبب بأنّه نقص في صلاحيّات المسؤول.10
ما يحدث غالبًا في الممارسة:
- Visual Studio المُشغَّل بشكل عاديّ يبني الثنائيّات بنجاح
- لكنّ تسجيل post-build وحده يفشل
- يُغفل الإخفاق
- لا يزال تسجيل قديم موجودًا من قبل، فيعمل التطبيق مصادفةً محلّيًّا
- في بيئة نظيفة، يفشل بطبيعة الحال
في مشروع من هذا النوع، الافتراضيّ الأكثر أمانًا هو فصل البناء عن التسجيل.
- يجب أن يُنتِج البناء ثنائيّات فقط
- يجب أن يكون التسجيل خطوة تثبيت / سكريبتًا / خطوة installer واضحة
- في CI، ينبغي أن تكون خطوة “تتطلّب التسجيل” مهمّة منفصلة عن البناء
هذا الفصل وحده يزيل عددًا مدهشًا من الحوادث.
4.2. خلط التسجيل per-user و per-machine يُعطّب الأمور
إن كانت ذاكرتك الوحيدة هي “COM ينظر في HKCR”، فقد تتعثّر هنا.
تشرح Microsoft Learn أنّ COM ينظر فعلًا في HKEY_CURRENT_USER\\Software\\Classes أوّلًا، ثمّ يتعامل مع المعلومات الخاصّة بالجهاز كلّه بعد ذلك.3
كما تشرح أنّ HKEY_CLASSES_ROOT هو عرض مدمج لـ HKLM\\Software\\Classes و HKCU\\Software\\Classes.722
هذا يعني أنّ مواقف كهذه طبيعيّة تمامًا:
- يسجّل المطوّر A يدويًّا تحت مستخدمه
- يعمل لدى المطوّر A
- لا يعمل لدى المطوّر B
- يفشل أيضًا لحساب خدمة
- يتغيّر السلوك مرّة أخرى عند التشغيل كمسؤول
كما تذكر Microsoft أنّ التطبيقات التي تتطلّب صلاحيّات المسؤول ينبغي أن تسجّل كائنات COM التابعة في مخزن تكوين COM per-machine أثناء التثبيت.87
لذا في فريق تطوير حقيقيّ، يجب أن يكون التمييز التالي صريحًا:
- هل هذا تسجيل per-user للمطوّر فقط
- أم تسجيل إنتاجيّ per-machine لكلّ مستخدم على الـ PC
- أم تسجيل يجب أن يكون مرئيًّا للخدمات أو التطبيقات المرفوعة
إن بقي ذلك ضبابيًّا، فستحصل على ارتباك كلاسيكيّ مثل “يعمل فقط كمسؤول” أو “يعمل من استضافة Explorer extension، لكن ليس من الخدمة”.
4.3. تشغيل Visual Studio دائمًا كمسؤول ليس إصلاحًا حقيقيًّا
ثمّة حالات يكون فيها الرفع ضروريًّا.
لكن إن كان Visual Studio يعمل دائمًا كمسؤول، فقد يخفي مشاكل ينبغي حلّها فعلًا بواسطة installer أو سكريبت تسجيل صريح.
كما يغيّر Visual Studio نفسه سلوكه في الوضع المرفوع. توثّق Microsoft Learn إعدادات حيث تُعطَّل الامتدادات per-user عند تشغيل Visual Studio مرفوعًا.23
لذا، كنموذج تشغيل، هذا عادةً أكثر أمانًا:
- قُمْ بالتطوير العاديّ تحت صلاحيّات المستخدم العاديّ
- شغّل فقط الخطوات التي تتطلّب فعلًا تسجيلًا بـ Developer Command Prompt / PowerShell / installer مرفوع صراحةً
- إن تكرّر شيء كمسؤول فقط، فاعتبر ذلك الافتراض نفسه حقيقة تصميميّة ينبغي توثيقها
هذا يُسبّب عادةً مشاكل أقلّ لاحقًا.
5. لـ ActiveX / OCX فخاخها الإضافيّة الخاصّة
5.1. قد تختلف رخص وقت التصميم عن رخص وقت التشغيل
جزء مزعج بهدوء من العمل على OCX / ActiveX هو الترخيص.
خصوصًا لضوابط ActiveX الأقدم، قد تكون رخصة وقت التصميم و رخصة وقت التشغيل منفصلتين. توثيق MFC ActiveX يشرح أيضًا آليّات حيث تفصل ملفّات الرخصة أو مفاتيح الرخصة استخدام وقت التصميم عن وقت التشغيل.2425
ينشأ عن ذلك مواقف مثل:
- استخدام وقت التشغيل يعمل
- لكنّ وضع الضابط على نموذج يقول إنّه لا توجد رخصة
- جهاز المطوّر A يستطيع وضعه
- جهاز المطوّر B لا يستطيع
أخطاء مثل “license information for this component not found” أو “you don’t have an appropriate license” ليست غير معتادة في بيئات ActiveX الأقدم.26
5.2. عند وصوله إلى داخل WinForms، يكون هناك بالفعل طبقة مغلِّفة
عندما يستخدم WinForms ActiveX، فإنّ Windows Forms لا يستضيف ضابط ActiveX خامًا تمامًا.
كما تشرح Microsoft Learn في توثيق Aximp.exe، يُولّد ActiveX Control Importer مغلِّفًا لـ WinForms من type library الخاصّة بـ COM، ثمّ يعمل WinForms مع ذلك من خلال ضابط مبنيّ على AxHost.2728
لذا فالمشكلة ليست طبقة واحدة. إنّها على الأقلّ هذه الطبقات:
- الـ OCX / ActiveX الأصليّ نفسه
- type library
- الـ interop / wrapper المُولَّد
- WinForms Designer / runtime
لهذا تحدث أمور كهذه:
- استبدال OCX المورّد يغيّر تواقيع الأحداث
- إعادة إضافة المرجع يُعيد توليد المغلِّفات ويُنشئ diff كبيرًا
- تُولّد آلات مختلفة نتائج interop مختلفة قليلًا
رؤيته مدرجًا في Choose Toolbox Items لا تعني أنّك بأمان.
من الأفضل التحقّق مما إن كان Designer يستطيع وضعه، وما إن كانت أحداث وقت التشغيل تصل بشكل صحيح، وما إن كانت البيئة المنشورة تعمل بما في ذلك المغلِّف كفحوص منفصلة.
5.3. إن استهنت بـ STA / MTA وحلقة الرسائل، تتجمّد الأمور
أوّلًا نعرّف ثلاثة مصطلحات فقط.
| المصطلح | المعنى |
|---|---|
| الـ apartment | مجموعة منطقيّة تجمع كائنات COM والخيوط تحت قاعدة التزامن نفسها. لا ينتمي الكائن الواحد إلّا إلى apartment واحد |
| STA (single-threaded apartment) | apartment ينتمي إليه خيط واحد فقط. استدعاء ذلك الكائن يُنفَّذ حتمًا على ذلك الخيط الواحد |
| marshaling | آليّة يمرّر بها COM الاستدعاء حين تُمرَّر مؤشّر الواجهة عبر حدود الـ apartment. يمرّ عبر proxy و stub |
يلزم تهيئة COM لكلّ خيط يستخدمه بـ CoInitializeEx. تنصّ Microsoft Learn صراحةً على أنّ كلّ خيط يستخدم COM يجب أن يستدعي CoInitializeEx لنفسه.29
وفي STA (single-threaded apartment) تلزم حلقة رسائل.2930
لماذا يتجمّد الأمر إن لم توجد حلقة رسائل
حفظ الخلاصة وحدها لا يكفي للتطبيق، لذلك نفتح الآليّة درجة واحدة.
ينشئ COM لكلّ STA نافذة مخفيّة واحدة من صنف النوافذ OleMainThreadWndClass. حين يستدعي apartment آخر ذلك الكائن، لا يصير الاستدعاء استدعاء دالّة مباشرًا، بل يصل كرسالة نافذة موجَّهة إلى هذه النافذة المخفيّة. حين يأخذ خيط STA الرسالة ويوزّعها، يستدعي إجراء النافذة دالّة الواجهة المقابلة.30
أي أنّ حلقة الرسائل ليست «أسلوبًا يُستحسن وجوده»، بل آليّة توصيل الاستدعاء نفسها.
sequenceDiagram
participant Caller as المستدعي من خيط آخر
participant Queue as طابور رسائل النافذة المخفيّة
participant STA as خيط STA
participant Obj as OCX / ActiveX
Caller->>Queue: يُكدَّس استدعاء الدالّة كرسالة
STA->>Queue: حلقة الرسائل تأخذ الرسالة
Queue->>Obj: إجراء النافذة يستدعي الدالّة المقابلة
Obj-->>Caller: تُعاد القيمة
Note over STA,Obj: إن توقّفت حلقة الرسائل<br/>لا يحدث هذا الأخذ ويبقى المستدعي منتظرًا
الشكل 2: الاستدعاء من خيط آخر يصل كرسالة إلى النافذة المخفيّة، ولا يُمرَّر إلى الكائن إلّا حين تأخذه حلقة الرسائل.
لذلك يتجمّد الأمر إن حُظر خيط STA. كتابات مثل Task.Wait() أو Task.Result على خيط الواجهة، أو الانتظار بـ WaitOne، توقف أخذ الرسائل، فتتوقّف توصيل استدعاءات COM بين الـ apartment والـ callback، ويحدث جمود.30
للسبب نفسه، لا يجوز نسخ مؤشّر واجهة كائن STA إلى خيط آخر كما هو. النسخ يجعل استدعاءً كان ينبغي توصيله إلى خيط STA يُنفَّذ مباشرة على خيط آخر، فيحدث تزامن لم يفترضه جانب الكائن. عند عبور الـ apartment يُجرى marshaling بـ CoMarshalInterThreadInterfaceInStream و CoGetInterfaceAndReleaseStream.30
وخصوصًا مكوّنات OCX / ActiveX الموجَّهة للواجهة كثيرًا ما تفترض STA، فيصير العطل المزعج الآتي.
- يعمل على خيط الواجهة
- يتجمّد إن أُلقي إلى
Task.Runأو تجمّع الخيوط - لا تعود الأحداث
- يتكرّر أحيانًا فقط
وفوق ذلك في STA يوجد أيضًا افتراض أنّك لا تنسخ مؤشّر الواجهة إلى خيط آخر كما هو، وإن لزم فاجعل marshaling.2930
هذا النوع من الأعطال ليس لطيفًا مثل 0x80040154، ولا يُرى إلّا كـ تجمّد / لا يعود / يسقط أحيانًا، فيأكل وقتًا بقدر أعطال السجلّ.
6. ترتيب عمليّ لتشخيص المشاكل يعمل في الميدان
في الممارسة، يكون من الأسرع غالبًا عدم الغوص العميق فورًا، بل تقطيع المشكلة بهذا الترتيب.
6.1. أوّلًا، ثبّت أيّ عمليّة بأيّ bitness
هذا أوّل شيء تتحقّق منه.
- ما هي عمليّة المضيف (Visual Studio Designer / تطبيقك الخاصّ / Office / Access / Explorer / مضيف شبيه بالمتصفّح)
- هل هذا المضيف 32-bit أم 64-bit
- هل الـ DLL / OCX المستهدف 32-bit أم 64-bit
- هل هو in-process أم out-of-process
إن بقي ذلك ضبابيًّا وبدأت من registry، فستضيع عادةً.
6.2. ثمّ تحقّق من نوع التسجيل الصحيح فعلًا
السؤال التالي هو ما الذي يجب تسجيله بأيّ شيء.
- DLL / OCX أصليّ →
regsvr32 - كشف COM لـ .NET Framework →
Regasm.exe - كشف COM لـ .NET 5+ / 6+ / 8+ →
.comhost.dll - DLL بدون دعم تسجيل ذاتيّ → ليس
regsvr32
هذا التصنيف وحده يمنع الكثير من الأخطاء غير المضطرّة.
6.3. بعد ذلك، تحقّق من المكان الذي سُجِّل فيه فعلًا
لمحة بسيطة على HKCR لا تكفي.
تحتاج غالبًا إلى النظر إلى:
HKCU\\Software\\ClassesHKLM\\Software\\Classes- عرض registry 32-bit / 64-bit إن كان ذا صلة
- ProgID / CLSID / TypeLib المستهدف
InprocServer32/LocalServer32ThreadingModel- المسار الفعليّ لـ DLL المرجعيّ
“موجود في HKCR” لا يكفي بعد. لا يصبح ذا معنى إلّا حين تصطفّ مَن ينظر، بأيّ bitness، عبر أيّ عرض.711
مسارات المفاتيح التي ينبغي النظر إليها
فتح HKEY_CLASSES_ROOT\CLSID\{...} يعرض فقط ما يراه bitness الأداة التي تشغّلها والمستخدم الحاليّ. في الفصل ننظر إلى الكيان قبل الدمج في أربعة مواضع. ما تحت CLSID هدف إعادة توجيه WOW64، والموضع الفيزيائيّ للجانب 32-bit هو Wow6432Node تحت Classes.19
| ما تريد رؤيته | مسار السجلّ |
|---|---|
| الجهاز كلّه / عرض 64-bit | HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{CLSID} |
| الجهاز كلّه / عرض 32-bit | HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node\CLSID\{CLSID} |
| ذلك المستخدم فقط / عرض 64-bit | HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{CLSID} |
| ذلك المستخدم فقط / عرض 32-bit | HKEY_CURRENT_USER\SOFTWARE\Classes\Wow6432Node\CLSID\{CLSID} |
| الاستدلال على CLSID من ProgID | القيمة الافتراضيّة لـ HKEY_CLASSES_ROOT\{ProgID}\CLSID |
regedit.exe في Windows 10 / 11 عمليّة 64-bit، لذلك إدخال المسارات أعلاه كما هي يفتح العرضين مباشرة. لصق المسار في شريط العنوان ينقلك إلى ذلك الموضع.
ومسار يشمل Wow6432Node موضع فيزيائيّ، وترشد Microsoft إلى عدم لمسه مباشرة من شيفرة التطبيق. لا بأس بالنظر إليه في تحقيق يدويّ، أمّا من سكربت أو تطبيق فأأمن استخدام تعيين العرض المذكور لاحقًا.11
التحقّق بالأمر
في reg.exe يمكن التصريح بالعرض بـ /reg:32 و /reg:64.
reg query "HKLM\SOFTWARE\Classes\CLSID\{00000000-0000-0000-0000-000000000000}\InprocServer32" /reg:32
reg query "HKLM\SOFTWARE\Classes\CLSID\{00000000-0000-0000-0000-000000000000}\InprocServer32" /reg:64
إن أردت رؤية المواضع الأربعة دفعة في PowerShell، مرّر العرض إلى RegistryKey.OpenBaseKey. لا تنجرّ النتيجة وراء bitness PowerShell نفسه، لذلك هذا أضمن في الفصل.
# عيّن ProgID المراد فحصه في موضع واحد
$progId = 'Vendor.Control.1'
# 1) اجلب CLSID من ProgID (HKCR هو view مدمج لـ HKLM و HKCU)
$clsid = (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\$progId\CLSID" -Name '(default)' -ErrorAction Stop).'(default)'
"ProgID $progId -> CLSID $clsid"
# 2) افحص بالمرور على 4 مواضع: hiveان × viewان
foreach ($hive in [Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryHive]::CurrentUser) {
foreach ($view in [Microsoft.Win32.RegistryView]::Registry64, [Microsoft.Win32.RegistryView]::Registry32) {
$base = [Microsoft.Win32.RegistryKey]::OpenBaseKey($hive, $view)
$key = $base.OpenSubKey("SOFTWARE\Classes\CLSID\$clsid\InprocServer32")
if ($null -eq $key) {
'{0,-12} {1,-11} : بلا تسجيل' -f $hive, $view
}
else {
'{0,-12} {1,-11} : {2} (ThreadingModel={3})' -f $hive, $view, $key.GetValue(''), $key.GetValue('ThreadingModel')
$key.Dispose()
}
$base.Dispose()
}
}
نتائج هذه الأسطر الأربعة هي جواب الفصل كما هي.
- المواضع الأربعة كلّها «غير مسجَّل» → غير مسجَّل فعلًا. راجع إن كانت وسيلة التسجيل تطابق تصنيف 6.2.
- يظهر في
Registry32فقط → مسجَّل في الجانب 32-bit فقط. من عمليّة 64-bit يصير0x80040154. - يظهر في
CurrentUserفقط → لا يراه إلّا ذلك المستخدم. لا يراه مستخدم آخر ولا حساب خدمة ولا عمليّة مرفوعة. - المسار يظهر ولا يعمل → أكّد أنّ الملفّ الذي تشير إليه القيمة الافتراضيّة لـ
InprocServer32موجود، وأنّ bitness ذلك الملفّ يطابق المضيف.
6.4. أخيرًا، تحقّق ممّا إن كانت الصلاحيّات تخفي الشكل الحقيقيّ فقط
في الخطوة الأخيرة، تحقّق ممّا إن كانت المشكلة فعلًا صلاحيّات، أو ما إن كانت الصلاحيّات تغيّر فقط أيّ تسجيل مرئيّ.
- هل يتغيّر السلوك بين المستخدم القياسيّ والمسؤول
- ماذا يتغيّر إن رُفع Visual Studio
- هل يتكرّر تحت حساب خدمة أو مستخدم آخر
- هل يعمل أيضًا في بيئة نظيفة مدفوعة بـ installer
إن كنت تنظر دائمًا إلى جهاز مطوّر واحد فقط، فهنا يُسيء الناس قراءة الموقف بسهولة كبيرة.
7. قرارات تشغيليّة تقلّل الحوادث
في تطوير وصيانة COM / ActiveX / OCX، قد تكون طريقة اتّخاذ قرارات نموذج التشغيل أهمّ من حِيَل التنفيذ منخفضة المستوى.
7.1. قرّر سياسة الـ bitness أوّلًا
في البداية، قرّر هذا صراحةً:
- هل ستذهب x86 فقط
- هل x64 هو الهدف الرئيسيّ
- هل تدعم الاثنين
- هل يجب أن يبقى هذا المكوّن in-process
إن كان OCX المورّد مثبّتًا على x86، فإنّ تجاهل ذلك وجعل التطبيق فقط x64 سيفشل عادةً لاحقًا.
للهيكليّة الأوسع حول هذه المشكلة، فإنّ دراسة حالة لجسر COM يستدعي DLL بـ 64bit من تطبيق 32bit وثيقة الصلة.
7.2. قرّر استراتيجيّة التسجيل
من الأفضل أيضًا التعامل مع التسجيل بشكل متعمَّد.
- يستخدمه الجهاز كلّه → سجّل per-machine عبر installer
- يستخدمه فقط ذلك المستخدم → استخدم تسجيل per-user عمدًا
- يبقى داخل تطبيقك بالكامل → فكّر في COM بدون تسجيل
- مطلوب فقط للتطوير → احصره في سكريبت إعداد تطوير صريح
COM بلا تسجيل مفيد لأنّه يستطيع الاحتفاظ بمعلومات التفعيل في manifest بدلًا من السجلّ، وهذا يساعد على تقليل جحيم التسجيل. كلّ من registration-free COM على جانب Win32 و RegFree COM على جانب .NET موثَّقان رسميًّا.31632
من حيث الآليّة، حين يعالج COM CoCreateInstance وما شابه، يبحث أوّلًا عن سياق التفعيل، فإن لم توجد فيه معلومات نظر إلى السجلّ. الـ manifest ملفّ يعلن محتوى سياق التفعيل ذلك.31
الـ manifest اللازم اثنان، ويُوضعان في المجلّد نفسه مع exe.
الأوّل manifest التجميعة على جانب المكوّن (VendorCtl.manifest). يكتب أيّ ملفّ يوفّر أيّ CLSID.33
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32"
name="MyCompany.VendorCtl"
version="1.0.0.0"
processorArchitecture="x86" />
<file name="vendor.ocx">
<comClass description="Vendor Control"
clsid="{00000000-0000-0000-0000-000000000000}"
threadingModel="Apartment"
progid="Vendor.Control.1" />
</file>
</assembly>
الثاني manifest التطبيق على جانب التطبيق (MyApp.exe.manifest). يكتب فقط الاعتماد على التجميعة أعلاه.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32"
name="MyCompany.MyApp"
version="1.0.0.0"
processorArchitecture="x86" />
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"
name="MyCompany.VendorCtl"
version="1.0.0.0"
processorArchitecture="x86" />
</dependentAssembly>
</dependency>
</assembly>
ما يسهل الخطأ فيه عند الكتابة الآتي.33
assemblyIdentityعلى جانب الاعتماد يجب أن يطابق تمامًاassemblyIdentityعلى جانب المكوّن. انحراف واحد فيnameأوversionأوprocessorArchitectureيمنع الحلّ.- اسم التجميعة واسم DLL منفصلان. عند وضع الـ manifest في ملفّ منفصل، يلزم أن يختلف اسم التجميعة واسم الـ manifest عن اسم DLL.
- أسماء العناصر والسمات حسّاسة لحالة الأحرف. كتابة
comClassكـComClassتمنع القراءة. processorArchitectureهو bitness نفسه. لا يُحلّ تطبيقamd64أمام OCX بـ x86. اجعله مطابقًا حتمًا لسياسة bitness في 7.1.- إن لصقت OCX فقد تلزم سمات عائلة
miscStatus. يلزم نسخ معلومات تعادل مفتاحMiscStatusفي السجلّ إلى الجانب الـ manifest كـmiscStatus/miscStatusContentوغيره.
7.3. لا تترك الكثير من الأصول المُولَّدة خارج التحكّم بالمصدر
من أنماط الإخفاق الشائعة في عمل OCX / ActiveX أنّ التبعيّات تتشتّت:
- الـ OCX نفسه
- DLLs التابعة
- الـ TLB
- الـ
.lic - DLLs الخاصّة بـ interop
- مغلِّف AxHost
- سكريبتات التسجيل
- المضيف النموذجيّ
إن وُجدت تلك فقط على آلة شخص ما المحلّيّة، فسيصبح ذلك تقريبًا حادثًا حتمًا بعد بضعة أشهر.
كحدّ أدنى، ينبغي الاحتفاظ بما يلي بجوار الشيفرة:
- أيّ إصدار مفترض
- ما الذي يجب تثبيته، وبأيّ ترتيب
- أيّ أوامر تُسجّله
- هل هو لـ x86 أم x64
8. هذا النوع من الاستشارات مناسب جيّدًا
غالبًا ما يقدّم هذا الموضوع قيمة حتى قبل بدء إعادة كتابة كاملة، لأنّ التشخيص و قرارات السياسة وحدها يمكنها دفع المشروع إلى الأمام.
على سبيل المثال، هذه أنواع عمل استشاريّ متوافقة جدًّا:
- فصل أسباب
0x80040154أو0x80070005إلى مشاكل bitness وتسجيل وصلاحيّات - التحقّق من مقدار ما يمكن إنقاذه واقعيًّا من إعداد Designer مكسور بعد الانتقال إلى Visual Studio 2022
- الإبقاء على OCX المورّد في مكانه مع نقل الشيفرة المحيطة نحو .NET أو C#
- تقرير إلى أيّ مدى نُمدّ عمر الأصول المثبّتة على x86، وأين نَجسر أو نُغلّف أو نستبدل
- إعادة تصميم التثبيت / النشر بحيث لا يعتمد النظام على
regsvr32يدويّ
قرار keep / wrap / replace الأوسع نفسه أسهل بكثير في تنظيمه أيضًا مع كيف تتعامل مع ActiveX / OCX اليوم - جدول قرار للإبقاء أو التغليف أو الاستبدال.
9. الخلاصة
عندما يفشل العمل على مكوّنات COM و OCX / ActiveX، تقع الأسباب عادةً في هذه المجموعات الأربع:
- الـ bitness لا يصطفّ
- طريقة التسجيل خاطئة
- نطاق التسجيل غير متطابق (HKCU / HKLM، عرض 32-bit / 64-bit)
- النظام يبدو أنّه يعمل فقط لأنّ الصلاحيّات تكشف شيئًا مصادفةً
مع تحوّل Visual Studio 2022 إلى 64-bit، صار الإعداد القديم بأسلوب “كان يعمل بطريقة ما” أسهل بكثير في الانكسار بطرق مرئيّة.12
لهذا بالضبط، عند لمس COM / OCX / ActiveX اليوم، فإنّ المسار الأسرع غالبًا هو محاذاة افتراضات البيئة قبل كتابة المزيد من الشيفرة.
بدلًا من السؤال عن عدد مرّات تشغيل regsvr32، من الأسرع عادةً ترتيب:
- أيّ عمليّة تستضيفه
- أيّ bitness لتلك العمليّة
- أين يجب تسجيله
- هل ينبغي فعلًا أن يتطلّب ذلك التسجيل صلاحيّات مسؤول
- هل يُفحَص Designer ووقت التشغيل بشكل منفصل
المراجع
-
Microsoft Learn, Visual Studio 2022 version 17.0 Release Notes —
devenv.exe is now 64-bit only. https://learn.microsoft.com/en-us/visualstudio/releases/2022/release-notes-v17.0 ↩ ↩2 ↩3 -
Microsoft Learn, Troubleshoot 32-bit problems - Windows Forms — Visual Studio 2022 is a 64-bit process, cannot directly load 32-bit .NET / COM / ActiveX, and has out-of-process designer constraints. https://learn.microsoft.com/en-us/dotnet/desktop/winforms/visualstudio/troubleshoot-32bit ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Classes and Servers — COM registration, HKCU / HKCR, self-registration, and
DllRegisterServer. https://learn.microsoft.com/en-us/windows/win32/com/classes-and-servers ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, regsvr32 — syntax and role of
regsvr32. https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/regsvr32 ↩ ↩2 -
Microsoft Learn, Registering Assemblies with COM — .NET Framework COM registration uses
Regasm.exe. https://learn.microsoft.com/en-us/dotnet/framework/interop/registering-assemblies-with-com ↩ ↩2 -
Microsoft Learn, Expose .NET Core components to COM —
EnableComHosting, generated.comhost.dll,regsvr32, andEnableRegFreeCom. https://learn.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com ↩ ↩2 ↩3 -
Microsoft Learn, Merged View of HKEY_CLASSES_ROOT — HKCR is a merged view of HKLM and HKCU. https://learn.microsoft.com/en-us/windows/win32/sysinfo/merged-view-of-hkey-classes-root ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, HKEY_CLASSES_ROOT Key — recommends per-machine COM registration for applications that require administrator rights. https://learn.microsoft.com/en-us/windows/win32/sysinfo/hkey-classes-root-key ↩ ↩2
-
Microsoft Learn, COM Error Codes (Generic) (Winerror.h) — including
REGDB_E_CLASSNOTREG (0x80040154). https://learn.microsoft.com/en-us/windows/win32/com/com-error-codes-1 ↩ -
Microsoft Learn, You receive 0x80070005 error when you try to register a DLL by using Regsvr32.exe — the typical permissions-related DLL-registration failure. https://learn.microsoft.com/en-us/troubleshoot/windows-client/shell-experience/dllregisterserver-error ↩ ↩2
-
Microsoft Learn, Registry Redirector — 32-bit / 64-bit registry views under WOW64. https://learn.microsoft.com/en-us/windows/win32/winprog64/registry-redirector ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, File System Redirector —
%windir%\\System32on x64 Windows and WOW64 file-system redirection. https://learn.microsoft.com/en-us/windows/win32/winprog64/file-system-redirector ↩ -
Microsoft Learn, Overview of compatibility considerations for 32-bit programs on 64-bit versions of Windows — file and registry redirection under WOW64. https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/compatibility-limitations-32-bit-programs-64-bit-system ↩
-
Microsoft Learn, Regasm.exe (Assembly Registration Tool) — the role of
Regasm.exeand options such as/tlb. https://learn.microsoft.com/en-us/dotnet/framework/tools/regasm-exe-assembly-registration-tool ↩ ↩2 ↩3 -
Microsoft Learn, Packaging a .NET Framework Assembly for COM — type libraries and
Regasm.exe /tlb. https://learn.microsoft.com/en-us/dotnet/framework/interop/packaging-an-assembly-for-com ↩ -
Microsoft Learn, Registering the DLL Server for Surrogate Activation — شروط تحميل خادم الـ DLL داخل surrogate، وأنّ تشغيل خادم EXE أو خدمة له الأولويّة عند وجود
LocalServer/LocalServer32/LocalService، والتكوين الذي يجعل الـAppIDبالـ GUID نفسه الخاصّ بالـ CLSID. https://learn.microsoft.com/en-us/windows/win32/com/registering-the-dll-server-for-surrogate-activation ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, DllSurrogate — أنّ
DllSurrogateتحتAppIDمن نوعREG_SZ، وأنّ السلسلة الفارغة تعني الـ surrogate الافتراضيّ للنظام، بينما كتابة مسار تعني استخدام الـ surrogate المخصّص الموجود في ذلك المسار. https://learn.microsoft.com/en-us/windows/win32/com/dllsurrogate ↩ ↩2 -
Microsoft Learn, Using the system-supplied surrogate — أنّ تحديد سلسلة فارغة أو
NULLيُشغّل الـ surrogate الافتراضيّ للنظام، والتعامل مع نموذج الخيوط وعمر العمليّة داخل الـ surrogate. https://learn.microsoft.com/en-us/windows/win32/com/using-the-system-supplied-surrogate ↩ ↩2 -
Microsoft Learn, CLSCTX enumeration — أنّ تمرير عدّة قيم
CLSCTXمجموعةً بـ OR يُجرَّب بترتيب التعداد، وأنّ مفتاحInprocServer32يُستخدم في مرحلة in-proc إن كان موجودًا، وأنّه حين لا يبدي العميل ولا الخادم رغبةً في bitness معيّن يُختار خادم بالـ bitness المناسب للعميل، وإن لم يوجد شُغِّل ذو الـ bitness الآخر. https://learn.microsoft.com/en-us/windows/win32/api/wtypesbase/ne-wtypesbase-clsctx ↩ ↩2 ↩3 -
Microsoft Learn, Understanding Custom Build Steps and Build Events — includes a post-build example that uses
regsvr32.exe. https://learn.microsoft.com/en-us/cpp/build/understanding-custom-build-steps-and-build-events ↩ -
Microsoft Learn, The Windows registry for advanced users — behavior of
HKCU\\Software\\Classes,HKLM\\Software\\Classes, and HKCR. https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/windows-registry-advanced-users ↩ -
Microsoft Learn, Find, install, and manage extensions for Visual Studio — behavior of per-user extensions under elevated execution. https://learn.microsoft.com/en-us/visualstudio/ide/finding-and-using-visual-studio-extensions ↩
-
Microsoft Learn, MFC ActiveX Controls: Licensing an ActiveX Control — design-time / run-time licensing and
.LIC. https://learn.microsoft.com/en-us/cpp/mfc/mfc-activex-controls-licensing-an-activex-control ↩ -
Microsoft Learn, Application Settings, MFC ActiveX Control Wizard — runtime-license generation and
.licfiles. https://learn.microsoft.com/en-us/cpp/mfc/reference/application-settings-mfc-activex-control-wizard ↩ -
Microsoft Learn, License information for this component not found. You don’t have an appropriate license to use this functionality in the design environment. https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/license-information-for-this-component-not-found-you-don-t-have-an-appropriate-l ↩
-
Microsoft Learn, Aximp.exe (Windows Forms ActiveX Control Importer) — converts ActiveX to a WinForms wrapper. https://learn.microsoft.com/en-us/dotnet/framework/tools/aximp-exe-windows-forms-activex-control-importer ↩
-
Microsoft Learn, AxHost Class — the AxHost-based wrapper produced by ActiveX Control Importer. https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.axhost ↩
-
Microsoft Learn, Initializing the COM Library —
CoInitializeEx, per-thread initialization, and the STA message loop. https://learn.microsoft.com/en-us/windows/win32/learnwin32/initializing-the-com-library ↩ ↩2 ↩3 -
Microsoft Learn, Single-Threaded Apartments — STA message loops, marshaling, and
ThreadingModel. https://learn.microsoft.com/en-us/windows/win32/com/single-threaded-apartments ↩ ↩2 ↩3 ↩4 ↩5 -
Microsoft Learn, Creating Registration-Free COM Objects — registry-free activation through activation context. https://learn.microsoft.com/en-us/windows/win32/sbscs/creating-registration-free-com-objects ↩ ↩2
-
Microsoft Learn, Registration-Free COM Interop — .NET Framework registration-free COM interop. https://learn.microsoft.com/en-us/dotnet/framework/interop/registration-free-com-interop ↩
-
Microsoft Learn, Assembly Manifests — سمات
assembly/assemblyIdentity/dependency/file/comClass، ولزوم تطابقassemblyIdentityفي جانب REF وجانب DEF، والتعامل مع حالة الأحرف، وعائلة سماتmiscStatus. ↩ ↩2
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
أسباب توقّف ActiveX على Office 2024 / Microsoft 365 وخطوات التحقّق
عندما يتوقّف ActiveX على Office 2024 / Microsoft 365، نرتّب ترتيب الفصل بين التعطيل الافتراضيّ و 32bit/64bit وتسجيل COM وDLL التابعة و IE...
ما هي COM / ActiveX / OCX: الفروق والعلاقات
تنظيم ما هي COM وما هي ActiveX وما هي OCX، مع الفروق والعلاقات وصلة OLE، وأين تُستخدم، وكيف تُفهم اليوم من منظور العمل اليومي.
كيف تتعامل مع ActiveX / OCX اليوم - جدول قرار للإبقاء أو التغليف أو الاستبدال
عند العثور على ActiveX أو OCX، يبيّن المقال كيف تختار بين الإبقاء والتغليف والاستبدال، مع 32bit / 64bit والتسجيل واعتماد المتصفّح وصيانة ...
دراسة حالة لجسر COM يستدعي DLL بـ 64bit من تطبيق 32bit
عندما يتعذّر استدعاء DLL بـ 64bit مباشرة من تطبيق 32bit، نرتّب هنا فكرة الربط عبر جسر COM مع قيد Windows والتركيبة وتدفّق المعالجة.
ما ينبغي تنظيمه قبل طلب التعهيد أو التطوير التعاقديّ لتطبيق Windows
قبل تكليف جهة خارجية بتطوير تطبيق Windows تعاقديّاً، إليك النقاط التي ينبغي تنظيمها: تعديل البرمجيّات القائمة، وتكامل الأجهزة، وCOM/Activ...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
دعم إعادة استخدام الأصول القديمة وترحيلها
ندعم إعادة استخدام وترحيل الأصول التي تحمل قيود COM / ActiveX / OCX أو 32bit / 64bit.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- كيف أحقّق في 0x80040154 (Class not registered)؟
- رسميًّا هو REGDB_E_CLASSNOTREG، أي خطأ «الصنف غير مسجَّل» حرفيًّا، لكنّه لا يعني دائمًا أنّه غير مسجَّل تمامًا. يحدث أيضًا حين يكون التسجيل في view السجلّ للـ bitness الآخر فقط، أو تحت HKCU حيث لا يراه إلّا ذلك المستخدم. الطريق السريع تثبيت إن كانت عمليّة المضيف 32-bit أم 64-bit أوّلًا، ثمّ التحقّق من أنّ وسيلة التسجيل صحيحة، ثمّ المرور على أين وقع الإدخال فعلًا عبر HKCU / HKLM وعرضَي 32-bit / 64-bit.
- لماذا لا يعمل بعد التسجيل بـ regsvr32؟
- regsvr32 موجَّه لخوادم COM الأصليّة من نوع in-proc (DLL / OCX) التي تصدّر DllRegisterServer؛ وليس أمرًا سحريًّا يسجّل أيّ شيء. كشف تجميعة .NET Framework لـ COM يمرّ عبر Regasm.exe، ومن .NET 5 فما بعد تسجّل بـ regsvr32 ملفّ .comhost.dll الذي يولّده EnableComHosting. على Windows x64 يلزم أيضًا اختيار regsvr32 الصحيح: System32 للجانب 64-bit، و SysWOW64 للجانب 32-bit؛ لأنّ التسجيل في الجانب الخطأ يترك المكوّن غير مرئيّ للعمليّة المستهدفة رغم نجاح الأمر.
- لماذا ينكسر Designer وحده في Visual Studio 2022؟
- لأنّ devenv.exe في Visual Studio 2022 عمليّة 64-bit ولا تستطيع تحميل مكوّنات COM / ActiveX بـ 32-bit مباشرة. التطبيق نفسه قد يعمل x86 وقت التشغيل، لكنّ Designer يعمل داخل عمليّة Visual Studio بـ 64-bit، فينشأ الالتواء الذي يبقى فيه التطبيق حيًّا وقت التشغيل بينما يسقط Designer وحده. التحويل إلى AnyCPU لا يحسمه أيضًا ما دام شيء في سلسلة المراجع يعتمد على مكوّن COM / ActiveX مقيَّد بـ 32-bit.
- لماذا يعمل عند التشغيل كمسؤول ولا يعمل بالصلاحيات العاديّة؟
- المشكلة الحقيقيّة في الغالب ليست الصلاحيات ذاتها بل انحراف نطاق التسجيل أو تصميم التثبيت. ينظر COM إلى HKCU\Software\Classes أوّلًا، و HKEY_CLASSES_ROOT عرض مدمج من HKLM و HKCU. إن سجّل المطوّر A المكوّن يدويًّا تحت مستخدمه، يعمل عادة تحت حساب A ويفشل لمستخدمين آخرين ولحسابات الخدمة. الأساس فصل تسجيل التطوير per-user عن تسجيل الإنتاج per-machine الذي يقوم به الـ installer، وفصل البناء عن التسجيل.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.