سجل التعديلات (2 تحديثات، آخر تحديث 2 Sep، 2026)
سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.
- أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
- أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
- النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621394)
هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.
小村 豪 (2026). استخدام DLL لـ .NET 8 من VBA بأنواع صريحة: نشر COM وTLB بـ dscom. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621394 https://comcomponent.com/ar/blog/2026/03/16/007-dotnet8-dll-typed-vba-com-dscom-tlb/
- DOI (أحدث نسخة)
- 10.5281/zenodo.21621394
- DOI (هذه النسخة)
- 10.5281/zenodo.22240850
ما زالت مشاهد استدعاء معالجة .NET 8 من VBA شائعة. خصوصاً عندما تُبقى أصول Excel أو Access كما هي، ويُراد إخراج الجزء الثقيل فقط إلى C#: معالجة ثقيلة، معالجة نصوص، HTTP، تشفير، منطق عمل.
غير أن الميل إلى الربط المتأخّر بـ CreateObject يملأ جانب VBA بـ Object. تضعف IntelliSense، وخطأ اسم المنهج لا يُكتشف حتى التنفيذ، وينزلق الأمر تدريجياً إلى وحل الاعتماد على النصوص.
flowchart TB
accTitle: وحل الربط المتأخّر
accDescr: مخطّط يبيّن أن الميل إلى الربط المتأخّر بـ CreateObject يملأ جانب VBA بـ Object، فتضعف IntelliSense، ولا يُكتشف خطأ اسم المنهج حتى التنفيذ.
lb1["ربط متأخّر بـ CreateObject"] --> lb2["جانب VBA ممتلئ بـ Object"]
lb2 --> lb3["تضعف IntelliSense"]
lb2 --> lb4["خطأ الكتابة لا يُكتشف حتى التنفيذ"]
الشكل 1: كلّما ملت إلى الربط المتأخّر، غصت في وحل الاعتماد على النصوص.
لذلك هذه المرّة نحصر الترتيب في نشر DLL لـ .NET 8 عبر COM، وتوليد مكتبة أنواع (TLB) بـ dscom، واستخدامها من VBA بربط مبكّر وأنواع صريحة.
حديث .NET Framework + RegAsm القديم، وكتابة IDL يدوياً وتثبيتها بـ MIDL، وReg-Free COM، نضعها جانباً هذه المرّة. هنا مسار واحد فقط: .NET 8 / COM host / dscom / VBA early binding.
الكود الوارد في المقالة منشور على GitHub كعيّنة كاملة قابلة للبناء والتحقق (مكتبة نشر COM، سكربتات توليد TLB والتسجيل، وحدة VBA، اختبارات وحدة).
dotnet8-dll-typed-vba-com-dscom-tlb - komurasoft-blog-samples (GitHub)
البيئة المطلوبة
| البند | المطلوب |
|---|---|
| نظام التشغيل | Windows. ستُجري تسجيل COM، فيلزم تشغيل regsvr32 بامتياز مدير |
| .NET SDK | .NET 8 SDK. EnableComHosting ميزة من .NET 5 فما بعده |
| Office | Excel أو Access. تحقّق أولاً أهو إصدار 32 بت أم 64 بت (الفصل 3) |
| أداة توليد TLB | dscom. طريق الحصول يختلف بين 64 بت و32 بت (الفصل 6) |
| جهاز العميل | زمن تشغيل .NET 8 بنفس bitness مكتب Office (الفصل 9) |
قبل الدخول في الخطوات، يُنصح بقوّة بتسجيل إصدارات بيئتك. عندما يصير «نفس الخطوات لا تعمل»، لا يبقى ما تقارن به إلا هذه المعلومات.
# .NET SDK とランタイムの一覧(x64 / x86 のどちらが入っているかも分かります)
dotnet --info
# Windows のビルド番号
winver
إصدار Office وبتّاته يُؤكَّدان من Excel: ملف > حساب > معلومات عن Excel. في نهاية سطر عنوان الحوار يظهر 32 بت أو 64 بت.
1. الخلاصة أوّلاً
إن رُتّبت الخلاصة فقط، فالمسار كالتالي.
- ابنِ مكتبة أصناف .NET 8 بـ
EnableComHosting=true - اصنع واجهة صريحة وصنفاً يُعرضان لـ COM
- اجعل الصنف
ClassInterfaceType.Noneولا تهرب إلىAutoDual - اجعل الواجهة التي يستخدمها VBA هي
InterfaceIsDual - من
*.dllالناتجة بعد البناء ولّد*.tlbبـdscom tlbexport - سجّل
*.comhost.dllبـregsvr32 - سجّل
*.tlbبـdscom tlbregister - أضف مرجعاً في VBA واستخدم بأنواع صريحة مثل
Dim x As اسم المكتبة.IYourInterface
باختصار: مدخل COM هو *.comhost.dll الذي يصنعه .NET SDK، ومعلومات النوع هي *.tlb التي يصنعها dscom، وVBA يرى تلك TLB ويربط ربطاً مبكّراً.
flowchart TB
accTitle: المسار الواحد حتى الاستخدام بأنواع صريحة
accDescr: مخطّط يبيّن المسار: البناء بـ EnableComHosting، وتوليد TLB بـ dscom tlbexport، وتسجيل comhost بـ regsvr32، وتسجيل TLB بـ dscom tlbregister، ثم الاستخدام بأنواع صريحة من مراجع VBA.
st1["ابنِ بـ EnableComHosting"] --> st2["ولّد TLB بـ dscom tlbexport"]
st2 --> st3["سجّل comhost بـ regsvr32"]
st3 --> st4["سجّل TLB بـ dscom tlbregister"]
st4 --> st5["أضف مرجعاً في VBA واستخدم بأنواع صريحة"]
الشكل 2: إن سرت بالترتيب: بناء، توليد TLB، تسجيلان، مرجع، استطعت الاستدعاء بأنواع صريحة.
خريطة المعرفة لهذه المقالة
استخدام مكتبة أصناف .NET 8 من VBA بأنواع صريحة يتطلّب مكتبة أنواع، أي معلومات نوع COM، وأداة اسمها dscom تولّدها وتسجّلها خلفاً لـ tlbexp.exe وRegAsm.exe اللذين أُلغيا مع .NET Framework. في جانب .NET، ضيف COM مبني بـ EnableComHosting مدخل تشغيل COM، وتسجيل regsvr32 وتطابق bitness مع Office شرطان مسبقان. في جانب VBA، تحميل مكتبة الأنواع عبر مرجع يتيح الربط المبكّر، وهو أأمن أنواع من الربط المتأخّر بـ CreateObject. التوافق بعد النشر يعتمد على معاملة IID وCLSID وعلى اختيار ClassInterfaceType، وتوليفة ClassInterfaceType.None وInterfaceIsDual وDispId هي الطريقة المعيارية لتفادي كسر مراجع VBA.
flowchart LR
accTitle: خريطة معرفة استخدام DLL لـ .NET 8 من VBA بأنواع صريحة
accDescr: مخطّط يبيّن أن VBA يحتاج مكتبة أنواع ليستخدم COM بأنواع صريحة عبر الربط المبكّر، وأن dscom يولّد تلك TLB ويسجّلها، وأن جانب .NET 8 يُكشَف كضيف COM، وكيف يؤثّر ClassInterfaceType ومعاملة IID وCLSID في توافق مراجع VBA
vba["VBA (Visual Basic for Applications)"]
dscom["dscom"]
type_library["مكتبة الأنواع (TLB)"]
com_early_binding["ربط مبكّر (VBA)"]
com_late_binding["ربط متأخّر (CreateObject)"]
tlbexp_regasm["tlbexp.exe / RegAsm.exe"]
comhost["COM host (*.comhost.dll)"]
regsvr32["regsvr32"]
dotnet[".NET (Core وما بعده)"]
com["COM (Component Object Model)"]
iid["IID (معرّف الواجهة)"]
clsid["CLSID (Class ID)"]
vba_reference_break["كسر مرجع VBA أو التسجيل"]
classinterfacetype_autodual["ClassInterfaceType.AutoDual"]
classinterfacetype_none["ClassInterfaceType.None"]
dispid_attribute["DispIdAttribute"]
interface_is_dual["InterfaceIsDual (واجهة مزدوجة)"]
hresult["HRESULT"]
dotnet_exception["استثناء .NET"]
com_visible_attribute["ComVisibleAttribute"]
bitness_match_requirement["متطلّب تطابق bitness"]
vba -.->|"يشترط"| type_library
com_early_binding -->|"يشترط"| type_library
vba -->|"يستخدم"| com_early_binding
vba -->|"يستخدم"| com_late_binding
com_late_binding -.->|"غير موصى به لـ"| vba
dscom -->|"ينفّذ"| type_library
dscom -->|"يخلف"| tlbexp_regasm
type_library -.->|"يُكوَّن بـ"| dscom
comhost -->|"يُكوَّن بـ"| regsvr32
comhost -->|"يشترط"| dotnet
comhost -->|"ينفّذ"| com
vba -.->|"يستخدم"| com
com -->|"يشترط"| iid
com -->|"يشترط"| clsid
iid -.->|"قد يسبّب"| vba_reference_break
clsid -.->|"قد يسبّب"| vba_reference_break
classinterfacetype_autodual -.->|"قد يسبّب"| vba_reference_break
classinterfacetype_autodual -->|"غير موصى به لـ"| vba
classinterfacetype_none -->|"موصى به لـ"| vba
dispid_attribute -->|"يخفّف"| vba_reference_break
interface_is_dual -->|"موصى به لـ"| vba
com -->|"يستخدم"| hresult
dotnet_exception -.->|"يُتحقّق بـ"| hresult
dotnet -->|"يُكوَّن بـ"| com_visible_attribute
comhost -->|"يشترط"| bitness_match_requirement
dscom -.->|"يشترط"| bitness_match_requirement
regsvr32 -->|"يشترط"| bitness_match_requirement
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 27، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
2. الصورة الكلّية لهذا التكوين
أولاً، دور كل جزء في ورقة واحدة.
flowchart LR
VBA["VBA / Excel / Access"] -->|الحصول على معلومات النوع من TLB المضافة مرجعاً| TLB["VbaTypedComSample.tlb"]
VBA -->|استدعاء COM| COMHOST["VbaTypedComSample.comhost.dll"]
COMHOST --> DOTNET["VbaTypedComSample.dll (.NET 8)"]
DOTNET --> RUNTIME[".NET 8 Runtime"]
الشكل 3: يحصل VBA على معلومات النوع من TLB، ويستدعي جسم تنفيذ .NET 8 عبر comhost.
أدوار كل ملف كالتالي.
| الملف | الدور |
|---|---|
VbaTypedComSample.dll |
جسم تنفيذ .NET 8 |
VbaTypedComSample.comhost.dll |
المدخل الذي يُستدعى من COM |
VbaTypedComSample.tlb |
معلومات النوع التي يراها VBA |
VbaTypedComSample.deps.json |
معلومات حل الاعتمادات |
VbaTypedComSample.runtimeconfig.json |
معلومات إقلاع زمن تشغيل .NET |
المهم هنا: ما يحتاجه VBA لمعرفة الأنواع هو TLB، وما يحتاجه مدخل تشغيل COM هو comhost.
تمرير *.dll وحدها لا يكفي: هذا الجانب غير المستقيم في عالم COM.
3. ما يُقرَّر أولاً: مواءمة 32 بت / 64 بت
إن أسقطت هذا، فباحتمال عالٍ تتدحرج نحو «لا يمكن إنشاء كائن لمكوّن ActiveX».
واءم bitness بين Office / VBA وخادم COM.
| جانب الاستخدام | مؤشر جانب .NET | توليد TLB | أمر التسجيل |
|---|---|---|---|
| Office 64 بت | x64 / win-x64 |
dscom |
C:\Windows\System32\regsvr32.exe |
| Office 32 بت (على Windows 64 بت) | x86 / win-x86 |
dscom32.exe |
C:\Windows\SysWOW64\regsvr32.exe |
في COM host من .NET 5+ فما بعده، إبقاء AnyCPU يميل *.comhost.dll إلى جانب 64 بت فيصطدم بـ Office 32 بت أحياناً. لذلك الأأمن التصريح بـ x86 / x64 وفق Office.
flowchart TB
accTitle: عدم التطابق الذي يجلبه ترك AnyCPU
accDescr: مخطّط يبيّن أن إبقاء AnyCPU يميل comhost إلى جانب 64 بت فيصطدم بـ Office 32 بت أحياناً، لذلك الأمان التصريح بـ x86 أو x64 وفق Office.
b1["إبقاء AnyCPU"] --> b2["يميل comhost إلى جانب 64 بت"]
b2 --> b3["يصطدم بـ Office 32 بت"]
b3 -.-> b4["صرّح بالبتّات وفق Office"]
الشكل 4: انحراف bitness طريق مختصر إلى «لا يمكن إنشاء الكائن».
كود هذه المقالة مثال موجّه إلى Office 64 بت. لـ Office 32 بت اقرأ x64 لاحقاً على أنه x86، و win-x64 على أنه win-x86.
4. اصنع جانب .NET 8
هنا عيّنة دنيا يمكن استدعاء Add و Divide و Hello منها من VBA.
4.1 .csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<EnableComHosting>true</EnableComHosting>
<PlatformTarget>x64</PlatformTarget>
<NETCoreSdkRuntimeIdentifier>win-x64</NETCoreSdkRuntimeIdentifier>
</PropertyGroup>
</Project>
الجوهر EnableComHosting. إن أضفته وُلد VbaTypedComSample.comhost.dll عند البناء.
4.2 اجعل التجميعة كلّها غير ظاهرة لـ COM افتراضياً
تريد ComVisible(true) للأنواع المعروضة لـ COM فقط، فالأسهل جعل التجميعة كلّها false.
using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
4.3 اكتب الواجهة والصنف المنشورين
using System.Runtime.InteropServices;
namespace VbaTypedComSample;
[ComVisible(true)]
[Guid("2A1BBEDE-DE6E-4C34-AD60-2E9E0E33E999")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ICalculator
{
[DispId(1)]
int Add(int x, int y);
[DispId(2)]
double Divide(double x, double y);
[DispId(3)]
string Hello(string name);
}
[ComVisible(true)]
[Guid("FAD1C752-0BB6-4DDD-889F-FE446350847A")]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(ICalculator))]
public class Calculator : ICalculator
{
public Calculator()
{
}
public int Add(int x, int y) => checked(x + y);
public double Divide(double x, double y)
{
if (y == 0)
{
throw new ArgumentOutOfRangeException(nameof(y), "لا يمكن القسمة على 0.");
}
return x / y;
}
public string Hello(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return "Hello";
}
return $"Hello, {name}";
}
}
ما ينبغي تثبيته في هذا الكود:
- اجعل
Guidللواجهة وللصنف منفصلين - اجعل
ClassInterfaceType.Noneولا تعتمد على واجهة صنف مولَّدة تلقائياً - اجعل
InterfaceIsDualليسهل التعامل من VBA - تعيين
DispIdيقلّل حوادث العبث بترتيب المناهج بعد النشر - يُنشأ من COM بـ
New، فجهّز منشئ public بلا وسائط
flowchart TB
accTitle: تركيب النوع المنشور
accDescr: مخطّط يبيّن تركيباً يضع InterfaceIsDual وDispId على الواجهة الصريحة ICalculator، وينفّذ الصنف Calculator بـ ClassInterfaceType.None، ويفرّق Guid بين الواجهة والصنف.
if1["ICalculator (واجهة صريحة)"] --> d1["InterfaceIsDual وDispId"]
cl1["Calculator (صنف)"] -->|"ينفّذ"| if1
cl1 --> d2["ClassInterfaceType.None"]
if1 -.-> g1["Guid منفصل لكل منهما"]
cl1 -.-> g1
الشكل 5: جوهر النشر بأنواع صريحة توليفة واجهة صريحة وصنف معيَّن None.
5. ابنِ
ابنِ بإصدار Release.
dotnet build -c Release
بعد البناء تصطف في مجلد الإخراج على الأقل ملفات كهذه.
bin/
Release/
net8.0-windows/
VbaTypedComSample.dll
VbaTypedComSample.comhost.dll
VbaTypedComSample.deps.json
VbaTypedComSample.runtimeconfig.json
هذا المجلد هو ما يُستخدم في التوزيع والتسجيل. إن غيّرت موضع النشر لاحقاً، لزم إعادة التسجيل أيضاً.
6. ولّد TLB بـ dscom
6.1 ما هو dscom
dscom أداة سطر أوامر مفتوحة المصدر لتوليد مكتبة أنواع COM (TLB) من تجميعة .NET وتسجيلها. تنشرها شركة dSPACE، والرخصة Apache-2.0.
لماذا تلزم: لأن tlbexp.exe و RegAsm.exe أُلغيا من .NET 5 فما بعده. في عصر .NET Framework كان الاثنان يولّدان TLB ويسجّلان التجميعة، أما .NET 5+ فلا يحمل خلفاً قياسياً. dscom أداة سُدّ بها ذلك الفراغ.
flowchart TB
accTitle: الفراغ الذي يسدّه dscom
accDescr: مخطّط يبيّن أنه في عصر .NET Framework كان tlbexp.exe وRegAsm.exe يولّدان TLB ويسجّلان التجميعة، وأن كليهما أُلغي بلا خلف قياسي من .NET 5 فما بعده، فيسدّ dscom ذلك الفراغ.
old1["عصر .NET Framework"] --> old2["tlbexp.exe وRegAsm.exe"]
new1["من .NET 5 فما بعده"] --> new2["كلاهما أُلغي بلا خلف"]
new2 --> ds1["dscom يسدّ الفراغ"]
الشكل 6: أداة توليد TLB من .NET 5 فما بعده صارت dscom.
يكفي حفظ الأوامر الفرعية التالية.
| الأمر الفرعي | الدور |
|---|---|
tlbexport |
كتابة TLB من التجميعة |
tlbregister |
تسجيل TLB في النظام |
tlbunregister |
إلغاء تسجيل TLB |
tlbdump |
إخراج محتوى TLB للفحص |
tlbembed |
تضمين TLB في ملف |
tlbdump مفيد للتأكد قبل فتح VBA من أن الأنواع المقصودة دخلت TLB المولَّدة.
6.2 حالة 64 بت
إن أردت صنع TLB 64 بت فقط، تدخل بـ dotnet tool.
dotnet tool install --global dscom
ثم ولّد TLB من التجميعة المبنية.
dscom tlbexport .\bin\Release\net8.0-windows\VbaTypedComSample.dll --out .\bin\Release\net8.0-windows\VbaTypedComSample.tlb
6.3 حالة Office 32 بت: من أين تحصل على dscom32.exe
هنا أكثر موضع يُحتبَس فيه دعم Office 32 بت.
dscom الداخل بـ dotnet tool install يعالج تجميعات AnyCPU أو 64 بت فقط، ولا يولّد إلا TLB 64 بت. لصنع TLB 32 بت تحتاج ملف تنفيذ آخر هو dscom32.exe، وهذا يُنزَّل من صفحة إصدارات GitHub لا من NuGet.
- مصدر الحصول: https://github.com/dspace-group/dscom/releases
dscom.exe… يصنع TLB 64 بت من تجميعة AnyCPU أو 64 بتdscom32.exe… يصنع TLB 32 بت من تجميعة AnyCPU أو 32 بت
في مثال هذه المقالة يُوضع dscom32.exe المنزَّل في مجلد tools تحت المشروع. موضع الوضع حر، لكن لا توزّعه مع مخرجات البناء. أداة وقت تطوير، ولا تلزم وقت التشغيل.
شرط يسهل إسقاطه أيضاً: لتشغيل dscom32.exe يلزم زمن تشغيل .NET بإصدار x86. بسبب تحميل dscom لـ hostfxr.dll، لا يعمل في بيئة فيها إصدار x64 فقط. في قائمة خرج dotnet --info تحقّق من وجود زمن تشغيل x86.
flowchart TB
accTitle: إعداد صنع TLB 32 بت
accDescr: مخطّط يبيّن أن dscom الداخل بـ dotnet tool لا يصنع إلا TLB 64 بت، لذلك تُصنع TLB 32 بت بـ dscom32.exe من صفحة إصدارات GitHub، وتشغيله يحتاج زمن تشغيل .NET بإصدار x86.
p1["تلزم TLB 32 بت"] --> p2["احصل على dscom32.exe من صفحة الإصدارات"]
p2 --> p3["تحقّق من وجود زمن تشغيل x86"]
p3 --> p4["tlbexport بـ dscom32.exe"]
p1 -.-> p5["إصدار dotnet tool لـ TLB 64 بت فقط"]
الشكل 7: دعم 32 بت يختلف من مسار الحصول على الأداة، لذلك يسهل الاحتباس هنا.
.\tools\dscom32.exe tlbexport .\bin\Release\net8.0-windows\VbaTypedComSample.dll --out .\bin\Release\net8.0-windows\VbaTypedComSample.tlb
وثائق dscom نفسها أيضاً توصي، إن استخدمت 32 بت، بتجميع التجميعة نفسها 32 بت، لأن إبقاء AnyCPU يولّد *.comhost.dll على أنه 64 بت. نفس خلاصة الفصل 3.
إن سئمت الطرق اليدوي عند كل بناء، حزمة dSPACE.Runtime.InteropServices.BuildTasks تولّد TLB تلقائياً عند التجميع.
7. سجّل COM host وTLB
نفّذ هنا من موجّه أوامر / PowerShell بامتياز مدير.
7.1 حالة Office 64 بت / COM 64 بت
$out = Resolve-Path .\bin\Release\net8.0-windows
C:\Windows\System32\regsvr32.exe "$out\VbaTypedComSample.comhost.dll"
dscom tlbregister "$out\VbaTypedComSample.tlb"
7.2 حالة Office 32 بت (على Windows 64 بت)
$out = Resolve-Path .\bin\Release\net8.0-windows
C:\Windows\SysWOW64\regsvr32.exe "$out\VbaTypedComSample.comhost.dll"
.\tools\dscom32.exe tlbregister "$out\VbaTypedComSample.tlb"
ما يُفعل هنا أمران.
- تسجيل
*.comhost.dllكخادم COM بـregsvr32 - تسجيل
*.tlbكمكتبة أنواع بـtlbregister
flowchart TB
accTitle: التسجيل مساران
accDescr: مخطّط يبيّن تسجيلين بامتياز مدير: regsvr32 يسجّل comhost كخادم COM، وdscom tlbregister يسجّل TLB كمكتبة أنواع.
adm["تشغيل بامتياز مدير"] --> r1["سجّل comhost بـ regsvr32"]
adm --> r2["سجّل TLB بـ tlbregister"]
r1 -.-> m1["تسجيل مدخل تشغيل COM"]
r2 -.-> m2["تسجيل معلومات النوع التي يراها VBA"]
الشكل 8: مدخل التشغيل ومعلومات النوع شيئان مختلفان، لذلك التسجيل زوج.
8. أضف مرجعاً في VBA واستخدم بأنواع صريحة
- افتح Excel أو Access
- افتح محرّر VBA (VBE) بـ
Alt+F11. من الشريط: تبويبمطور>Visual Basic. إن لم يظهر تبويبمطور، ضع علامة علىمطورفيملف>خيارات>تخصيص الشريط - في قائمة VBE:
أدوات>مراجع - قائمة
ملفات المكتبات المتاحةمرتّبة أبجدياً. إن نجح التسجيل ظهر فيها اسم المكتبة (افتراضياً اسم التجميعة نفسهVbaTypedComSample)، فضع علامة في خانة الاختيار يساراً ثمموافق - إن لم تظهر في القائمة، اختر
VbaTypedComSample.tlbمباشرة من زراستعراض...
إن لم تظهر في القائمة، السبب غالباً أحد اثنين: عدم تطابق bitness في الفصل 3، أو عدم مرور tlbregister في الفصل 7. Office 32 بت لا يرى TLB المسجّلة 64 بت.
flowchart TB
accTitle: عزل سبب غياب المكتبة عن المراجع
accDescr: مخطّط يبيّن أن سبب غياب المكتبة عن قائمة المراجع غالباً عدم تطابق bitness أو عدم مرور tlbregister، وأن Office 32 بت لا يرى TLB المسجّلة 64 بت.
q1["المكتبة لا تظهر في القائمة"] --> a1["شكّ في عدم تطابق bitness في الفصل 3"]
q1 --> a2["شكّ في فشل tlbregister في الفصل 7"]
a1 -.-> nt1["Office 32 بت لا يرى TLB 64 بت"]
الشكل 9: سبب الغياب عن القائمة ينحصر تقريباً في bitness أو التسجيل.
هل دخل المرجع يُؤكَّد بفتح عرض > مستعرض الكائنات (F2) واختيار VbaTypedComSample في منتقي المكتبة أعلى اليسار. إن ظهرت ICalculator و Calculator و Add / Divide / Hello، فقد وُلدت TLB صحيحاً.
Option Explicit
Public Sub UseCalculator()
Dim calc As VbaTypedComSample.ICalculator
Set calc = New VbaTypedComSample.Calculator
Debug.Print calc.Add(10, 20)
Debug.Print calc.Divide(10, 4)
Debug.Print calc.Hello("VBA")
End Sub
ضع المؤشّر على هذا الإجراء ونفّذه بـ F5، وافتح النافذة الفورية بـ Ctrl + G، فتظهر ثلاثة أسطر وفق تنفيذ الفصل 4.
30
2.5
Hello, VBA
إن طابق هذا التوقّع، فقد مرّ المرجع وتسجيل COM وإقلاع زمن التشغيل وتمرير الوسائط والقيم الراجعة جميعاً. بالمقابل، إن لم تطابق القيم فشكّ في تنفيذ جانب .NET، وإن تعذّر التشغيل أصلاً فشكّ في الفصلين 3 و7.
flowchart TB
accTitle: العزل بنتيجة التشغيل
accDescr: مخطّط يبيّن أنه إن طابقت نتيجة العيّنة التوقّع فقد مرّ المسار من المرجع حتى التمرير، وإن لم تطابق القيم فشكّ في تنفيذ جانب .NET، وإن تعذّر التشغيل فشكّ في bitness الفصل 3 وتسجيل الفصل 7.
r1["شغّل عيّنة VBA"] --> q1{"ما النتيجة"}
q1 -->|"وفق التوقّع"| ok1["المسار كلّه نافذ"]
q1 -->|"القيم لا تطابق"| ng1["شكّ في تنفيذ جانب .NET"]
q1 -->|"لا يعمل"| ng2["شكّ في bitness والتسجيل"]
الشكل 10: ثلاثة أسطر خرج وحدها تقرّر أي طبقة تشكّ فيها.
عندها ينال جانب VBA هذه الفوائد.
- تعمل IntelliSense
- يسهل اكتشاف خطأ اسم المنهج قبل التنفيذ
- يمكن فحص واجهة برمجيّة منشورة في Object Browser
- أوضح قراءة من كتابة
Objectصريحة
8.1 الاستثناء يصير خطأ COM في جانب VBA
مثلاً إن أُلقي استثناء في جانب .NET مثل Divide(10, 0)، يراه جانب VBA خطأ COM.
Option Explicit
Public Sub UseCalculatorWithErrorHandling()
On Error GoTo EH
Dim calc As VbaTypedComSample.ICalculator
Set calc = New VbaTypedComSample.Calculator
Debug.Print calc.Divide(10, 0)
Exit Sub
EH:
Debug.Print Err.Number
Debug.Print Hex$(Err.Number)
Debug.Print Err.Description
End Sub
إن ثبتت قراءة القيم الظاهرة هنا، سرع العزل.
| البند | ما يدخل |
|---|---|
Err.Number |
HRESULT الموافق لاستثناء .NET، كـ Long موقّع. العشري صعب القراءة فحوّله إلى ست عشري بـ Hex$(Err.Number) |
Err.Description |
عبر IErrorInfo لـ COM تدخل رسالة استثناء .NET كما هي. في الكود أعلاه سلسلة تتضمّن لا يمكن القسمة على 0. |
قيمة HRESULT ثابتة لكل نوع استثناء. الموافق لـ ArgumentOutOfRangeException هو COR_E_ARGUMENTOUTOFRANGE، والقيمة 0x80131502. أي إن صار Hex$(Err.Number) هو 80131502، فقد وصل ArgumentOutOfRangeException من جانب .NET كما هو متوقّع.
الأبرز كالتالي.
| استثناء .NET | ثابت HRESULT | القيمة |
|---|---|---|
ArgumentException |
COR_E_ARGUMENT |
0x80070057 |
ArgumentOutOfRangeException |
COR_E_ARGUMENTOUTOFRANGE |
0x80131502 |
InvalidOperationException |
COR_E_INVALIDOPERATION |
0x80131509 |
NotSupportedException |
COR_E_NOTSUPPORTED |
0x80131515 |
| استثناء عام غير ما سبق | COR_E_EXCEPTION |
0x80131500 |
إن أردت فصل المعالجة حسب نوع الاستثناء في جانب VBA، فستتفرّع بهذا HRESULT. غير أن تصميماً يتفرّع بـ HRESULT ضعيف أمام تغيير نوع الاستثناء في جانب .NET، لذلك الفشل العملي أستقر على الحدود إن أُرجع بقيمة راجعة أو رمز خطأ لا باستثناء.
flowchart TB
accTitle: حتى يصل استثناء .NET إلى VBA
accDescr: مخطّط يبيّن أن الاستثناء المُلقى في جانب .NET يُحوَّل إلى HRESULT عند حدود COM، فيدخل ذلك HRESULT في Err.Number كـ Long موقّع، ورسالة الاستثناء عبر IErrorInfo في Err.Description.
x1["يُلقى استثناء في جانب .NET"] --> x2["يُحوَّل إلى HRESULT عند حدود COM"]
x2 --> x3["يدخل موقّعاً في Err.Number"]
x2 --> x4["الرسالة في Err.Description"]
x3 -.-> h1["يُقرأ ست عشرياً بـ Hex$"]
الشكل 11: الاستثناء يتغيّر هيئة إلى HRESULT عند حدود COM ويصل إلى Err في VBA.
9. النظرة عند التوزيع
المهم عند التوزيع وضع مجموعة الإخراج لا توزيع DLL وحدها.
VbaTypedComSample.dll
VbaTypedComSample.comhost.dll
VbaTypedComSample.deps.json
VbaTypedComSample.runtimeconfig.json
VbaTypedComSample.tlb
(مجموعة DLL الاعتماد إن لزم)
ثم يلزم جهاز العميل زمن تشغيل .NET 8 الموافق. تشغيل COM host ليس توزيعاً self-contained، بل framework-dependent أساساً.
ما يُثبَّت تحديداً كالتالي.
- صفحة التوزيع تنزيلات .NET 8
- المطلوب زمن تشغيل لا SDK. عيّنة هذه المقالة مكتبة أصناف بلا شاشة، فيكفي
.NET Runtime. إن استخدمت أنواعاً من WPF أو Windows Forms لزم.NET Desktop Runtime - bitness يُواءم مع Office. Office 64 بت فزمن تشغيل x64، وOffice 32 بت فـ x86. لنفس سبب التصريح بـ
x64/x86في الفصل 3، إن لم يتطابق هنا لا يقلع - هل ثُبّت يُعرف بتشغيل
dotnet --list-runtimesعلى جهاز العميل والنظر إلى سطرMicrosoft.NETCore.App 8.x
في مواد التوزيع اكتب حتماً «نوع زمن التشغيل المطلوب وإصداره وbitness». إن سقط من خطوات التثبيت، رأيت في الميدان «لا يمكن إنشاء كائن لمكوّن ActiveX» وشككت في bitness وأضعت وقتاً.
flowchart TB
accTitle: ما يُكمَل عند التوزيع
accDescr: مخطّط يبيّن أن التوزيع يضع مجموعة الإخراج لا DLL وحدها، ويثبّت على جهاز العميل زمن تشغيل .NET 8 بنفس bitness مكتب Office، ويكتب نوع زمن التشغيل وإصداره وbitness في مواد التوزيع.
h1["ضع مجموعة الإخراج"] --> u1["يعمل على العميل"]
h2["زمن تشغيل .NET 8 بنفس bitness"] --> u1
h3["اكتب معلومات زمن التشغيل في المواد"] -.-> u1
الشكل 12: DLL وحدها لا تعمل، ولا تعمل إلا باكتمال المجموعة وزمن التشغيل.
10. مواضع الاحتباس
10.1 لا تترك AnyCPU كما هو
إن انحرف bitness VBA / Office عن bitness COM host، جاء الفشل بشكل مزعج جدّاً.
- Office 64 بت فـ
x64/win-x64 - Office 32 بت فـ
x86/win-x86
10.2 لا تستخدم ClassInterfaceType.AutoDual
يبدو سهلاً، لكنه ينكسر بسهولة إن لمست ترتيب الأعضاء أو التركيب بعد النشر.
إن أردت استخداماً مستقرّاً بأنواع صريحة من VBA، فالقاعدة تعريف واجهة صريحة، والصنف ClassInterfaceType.None.
10.3 لا تولّد GUID بلا رويّة من جديد
في COM المعرّف GUID هو العقد نفسه. استبدال IID أو CLSID بلا رويّة بعد النشر يكسر مراجع VBA أو التسجيل القائمة.
10.4 لا تكسر واجهة منشورة
COM قد لا يمرّ بسلام حتى مع «منهج واحد أُضيف لاحقاً فقط».
- أبقِ
ICalculator - إن كبر التغيير فأنشئ
ICalculator2 - يجوز للصنف تنفيذ الاثنين
flowchart TB
accTitle: طريقة حفظ واجهة منشورة
accDescr: مخطّط يبيّن حفظ التوافق بإبقاء ICalculator المنشور كما هو، وإنشاء ICalculator2 إن كبر التغيير، وجواز تنفيذ الصنف للاثنين.
k1["ICalculator المنشور"] --> k2["أبقه كما هو"]
k3["تريد تغييراً كبيراً"] --> k4["أنشئ ICalculator2"]
k2 --> k5["يجوز للصنف تنفيذ الاثنين"]
k4 --> k5
الشكل 13: أسلوب COM إبقاء العقد القائم وإضافة عقد جديد إلى جانبه.
10.5 قرّب الأنواع بتواضع
عند الحدود المعروضة لـ VBA، عدم التأنّق أأمن.
ما يتوافق جيّداً أولاً من هذا القبيل.
intdoubleboolstringDateTimedecimalenum
10.6 لا تحدّث وOffice مفتوح
قد يمسك Excel أو Access بـ DLL، فيصير البناء أو إعادة التسجيل مزعجاً.
- أغلق Office
- ألغِ التسجيل إن لزم
- أعد البناء
- سجّل مرّة أخرى
إلغاء التسجيل يتم بالترتيب العكسي لتسجيل، وبنفس أوامر bitness التي استُخدمت عند التسجيل. امتياز المدير لازم كما عند التسجيل.
# 64bit Office / 64bit COM の場合
$out = Resolve-Path .\bin\Release\net8.0-windows
dscom tlbunregister "$out\VbaTypedComSample.tlb"
C:\Windows\System32\regsvr32.exe /u "$out\VbaTypedComSample.comhost.dll"
# 32bit Office(64bit Windows 上)の場合
$out = Resolve-Path .\bin\Release\net8.0-windows
.\tools\dscom32.exe tlbunregister "$out\VbaTypedComSample.tlb"
C:\Windows\SysWOW64\regsvr32.exe /u "$out\VbaTypedComSample.comhost.dll"
/u في regsvr32 خيار إلغاء التسجيل. إن استخدمت regsvr32 مختلفاً عن التسجيل لم يُلغَ (ما سُجّل 64 بت لا يُزال بـ regsvr32 في SysWOW64). إن نقلت المجلد أو حذفته قبل الإلغاء، بقي في السجل تسجيل لمسار غير موجود.
flowchart TB
accTitle: طي التسجيل قبل التحديث وبعده
accDescr: مخطّط يبيّن مسار التحديث: إغلاق Office، ثم إلغاء التسجيل إن لزم بالترتيب العكسي ونفس أوامر bitness المستخدمة عند التسجيل، ثم إعادة البناء، ثم التسجيل مرّة أخرى.
u1["أغلق Office"] --> u2["ألغِ بالعكس إن لزم"]
u2 --> u3["أعد البناء"]
u3 --> u4["سجّل مرّة أخرى"]
u2 -.-> u5["بنفس أوامر bitness المستخدمة عند التسجيل"]
الشكل 14: تجنّباً لـ DLL الممسوكة والتسجيل المتبقي، اجعل الإلغاء وإعادة التسجيل زوجاً.
11. الخلاصة
حديث استخدام DLL لـ .NET 8 بأنواع صريحة من VBA، إن حُصر في نشر COM + توليد TLB بـ dscom، ليس خطوات مخيفة إلى ذلك الحد. في جانب .NET 8 اجعل EnableComHosting=true وجهّز واجهة صريحة (الصنف ClassInterfaceType.None، ولـ VBA InterfaceIsDual)، وصنع TLB بـ dscom tlbexport، وسجّل *.comhost.dll بـ regsvr32 و *.tlb بـ dscom tlbregister. بعدها يكفي إضافة مرجع في VBA والربط المبكّر.
عند الحيرة، الحيلة فصل التفكير في COM host عن TLB.
- مدخل التشغيل
*.comhost.dll - معلومات النوع
*.tlb - جسم التنفيذ
*.dll
روابط مرجعية
- مجموعة عيّنات كود هذه المقالة (مكتبة نشر COM، سكربتات، VBA، اختبارات) - komurasoft-blog-samples (GitHub)
- Microsoft Learn, Expose .NET components to COM
- Microsoft Learn, Qualify .NET types for COM interoperation
- Microsoft Learn, ComInterfaceType enumeration
- Microsoft Learn, ClassInterfaceType enumeration
- Microsoft Learn, COM callable wrapper
- Microsoft Learn, DispIdAttribute class
- dscom - NuGet Gallery
- dspace-group/dscom - GitHub (جسم dscom. قائمة الأوامر الفرعية وشرح دعم 32 بت)
- صفحة إصدارات dscom (مصدر الحصول على
dscom32.exe) - Microsoft Learn, How to map HRESULTs and exceptions
- Microsoft Support, How to use the Regsvr32 tool and troubleshoot Regsvr32 error messages
- .NET 8 downloads
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
مشكلة بقاء EXCEL.EXE عند التعامل مع Excel من C# ── أنماط تحرير مراجع COM وقرار الاستبدال
نرتّب مشكلة بقاء عملية EXCEL.EXE عند تشغيل Excel عبر COM من C#، انطلاقاً من آلية عدّ مراجع COM وRCW. نشرح فخ قاعدة النقطتين، وأنماط التحر...
ترحيل ماكرو Excel VBA إلى Power Automate ── النطاق الذي يُستبدل بـ Office Scripts، والنطاق الذي يبقى على VBA
نرتب إمكانية ترحيل ماكرو Excel VBA إلى Power Automate. نشرح النطاق الذي يمكن استبداله بـ Office Scripts، وما لا يُنجز إلا عبر VBA، والقيم...
التوافق الخلفي لواجهات DLL وCOM ── جدول قرار لتحديد أيّ تغيير يكسر جهة الاستدعاء
أيّ تغيير في مكوّنات DLL أو COM يكسر جهة الاستدعاء؟ نرتّب الطبقات الثلاث للتوافق: الثنائيّ، والمصدريّ، والسلوكيّ، ونقدّم جدول قرار حسب نو...
هل تعمل تطبيقات الأعمال على Windows بإصدار Arm؟ ── واقع محاكاة x64 (Prism) ومكتبات DLL وCOM الأصليّة
نجيب المطوّرين ومسؤولي الأنظمة عن سؤال «هل تعمل تطبيقات الأعمال على Windows بإصدار Arm؟». نستعرض آليّة محاكاة x64 (Prism)، والطبقات التي ...
كيف تختار وسيلة الاتّصال بين عمليّات Windows ── جدول قرار للأنابيب المسمّاة / TCP / gRPC / الذاكرة المشتركة / COM
كيف تختار وسيلة التواصل بين تطبيقات Windows؟ ننظّم في جدول قرار مجالات تفوّق ومطبّات الأنابيب المسمّاة، وTCP المحليّ، وgRPC، والذاكرة الم...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
دعم إعادة استخدام الأصول القديمة وترحيلها
ندعم إعادة استخدام وترحيل الأصول التي تحمل قيود COM / ActiveX / OCX أو 32bit / 64bit.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- ما المطلوب لاستخدام DLL لـ .NET 8 من VBA بأنواع صريحة (ربط مبكّر)؟
- ابنِ مكتبة أصناف .NET 8 بـ EnableComHosting=true لتوليد *.comhost.dll، ثم ولّد *.tlb بـ dscom tlbexport. بعدها سجّل *.comhost.dll بـ regsvr32 و*.tlb بـ dscom tlbregister، وأضف تلك TLB في مراجع VBA، فتستخدم Dim x As اسم المكتبة.IYourInterface بأنواع صريحة. توزيع الأدوار: مدخل تشغيل COM هو *.comhost.dll، ومعلومات النوع التي يراها VBA هي *.tlb، وجسم التنفيذ هو *.dll.
- ما سبب ظهور «لا يمكن إنشاء كائن لمكوّن ActiveX»؟
- السبب النموذجي عدم تطابق bitness بين Office/VBA وخادم COM. لـ Office 64 بت ابنِ بـ x64/win-x64 وسجّل بـ regsvr32 في System32. لـ Office 32 بت (على Windows 64 بت) ابنِ بـ x86/win-x86 وسجّل بـ regsvr32 في SysWOW64، ولتوليد TLB استخدم dscom32.exe. في COM host لـ .NET 5 فما بعده، إبقاء AnyCPU يميل *.comhost.dll إلى جانب 64 بت فيصطدم بـ Office 32 بت، لذلك الأمان التصريح بـ x86/x64 وفق Office.
- هل يُمنع استخدام ClassInterfaceType.AutoDual؟
- يبدو سهلاً، لكنه ينكسر بسهولة إن لمست ترتيب الأعضاء أو التركيب بعد النشر، فينبغي تجنّبه. إن أردت استخداماً مستقرّاً بأنواع صريحة من VBA، عرّف واجهة صريحة واجعل الصنف ClassInterfaceType.None، واجعل الواجهة التي يستخدمها VBA هي InterfaceIsDual. تعيين DispId يقلّل حوادث تغيير ترتيب المناهج. في COM المعرّف GUID هو العقد نفسه، فتوليد IID أو CLSID بلا رويّة بعد النشر يكسر مراجع VBA أو التسجيل القائمة.
- عند التوزيع، هل يكفي تمرير DLL وحدها؟
- DLL وحدها لا تعمل. ضع معاً جسم التنفيذ *.dll و*.comhost.dll و*.deps.json و*.runtimeconfig.json و*.tlb ومجموعة DLL الاعتماد إن لزم. وعلى جهاز العميل زمن تشغيل .NET 8 الموافق، وتشغيل COM host ليس توزيعاً self-contained بل framework-dependent أساساً. إن غيّرت موضع النشر لاحقاً لزم إعادة التسجيل أيضاً.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.