اشتراک‌ها
پروژه CsQuery
jQuery Selector در C# با پشتیبانی از CSS3 
پروژه CsQuery
نظرات مطالب
سری فیبوناچی و دات نت 4 !
من یه سوال دارم. امکان اینکه از bigInteger تو c++ استفاده بشه نیست؟
مطالب
آشنایی با CLR: قسمت چهاردهم
در ادامه قسمت قبلی روش‌های زیادی جهت اضافه شدن یک ماژول به یک اسمبلی وجود دارند. اگر شما از کامپایلر سی‌شارپ برای ساخت یک فایل PE با جدول مانیفست استفاده می‌کنید، می‌توانید از سوئیچ AddModule/ استفاده کنید. برای اینکه بدانیم چگونه می‌توان یک اسمبلی چند فایله ساخت بیاید فرض کنیم که دو فایل سورس کد با مشخصات زیر داریم:
RUT.cs: این سورس شامل کدهایی است که به ندرت در برنامه استفاده می‌شود.
FUT.cs: این سورس شامل کدهایی است که به طور مکرر مورد استفاده قرار می‌گیرد.

ابتدا به صورت زیر کد سورسی را که به ندرت استفاده می‌شود، به عنوان یک ماژول جداگانه کامپایل می‌کنم:
csc /t:module RUT.cs
اجرای این خط سبب ایجاد یک فایل به نام RUT.netmodule می‌گردد که یک DLL استاندارد است؛ ولی CLR به تنهایی توانایی بارگیری آن را ندارد. دفعه‌ی بعد سورس کدی را که مکرر استفاده می‌شود، به صورت یک ماژول کامپایل می‌کنیم و از آنجائیکه این ماژول استفاده‌ی زیادی دارد، آن را نگهدارنده‌ی جدول مانیفست معرفی می‌کنیم و به این دلیل که این ماژول نماینده‌ی کل اسمبلی است، نام خروجی آن را به جای FUT.dll به MultiFileLibrary.dll تغییر می‌دهیم:
csc /out:MultiFileLibrary.dll /t:library /addmodule:RUT.netmodule FUT.cs
خط بالا به علت سوئیچ t:library\ فایل MultiFileLibrary.dll را ایجاد می‌کند. این فایل شامل جدول متادیتای مانیفست می‌شود و سوئیچ به آن می‌گوید که باید ماژول RUT.netmodule را جزئی از اسمبلی بداند. این سوئیچ به کامپایلر اعلام می‌کند که ارجاع این فایل در جدول FileDef  و ExportedTypesDef ثبت شود.
بعد از اتمام عملیات کامپایل، مطابق شکل زیر دو فایل ایجاد می‌شود که فایل سمت راست شامل جدول مانیفست است. فایل RUT.netmodule شامل کد IL و جداول متادیتاهای مربوط به خواص و رویدادها و مواردی از این قبیل است که در این ماژول یافت می‌شود. فایل بعدی MultiFileLibrary.dll هست که شامل کد IL کد FUT.CS می‌شوذ بعلاوه جداول متادیتا مثل ماژول قبلی و جدول متادیتای مانیفست که باعث می‌شود به عنوان یک اسمبلی شناخته شود.



البته توجه داشته باشید که جدول مانیفست ارجاعی به نوع‌های عمومی استخراج شده داخل فایل خودش ندارد، زیرا که در جداول اختصاصی خودش موجود است و در ذخیره سازی صرفه جویی می‌گردد.
بعد از اینکه MultiFileLibrary.dll ساخته شد، به منظور آزمایش کردن جداول متادیتا می‌توانید از ابزار ILDasm.exe استفاده کنید تا ارجاع به فایل RUT.netmodule به شما ثابت شود. آنچه در زیر می‌بینید نمایی از جداول FileDef و ExportedTypesDef است:
File #1 (26000001)
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
Token: 0x26000001
Name : RUT.netmodule
HashValue Blob : e6 e6 df 62 2c a1 2c 59 97 65 0f 21 44 10 15 96 f2 7e db c2
Flags : [ContainsMetaData] (00000000)


ExportedType #1 (27000001)
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
Token: 0x27000001
Name: ARarelyUsedType
Implementation token: 0x26000001
TypeDef token: 0x02000002
Flags : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass]
[BeforeFieldInit](00100101)
 
همانطور که در بالا می‌بینید فایل RUT.netmodule با شناسه‌ی (توکن) 0x26000001 به عنوان بخشی از اسمبلی شناخته می‌شود و به نوع کد IL آن اشاره می‌کند.

قابل توجه افراد کنجکاو: توکن‌های جداول متا، مقادیر 4 بایتی است که بایت پر ارزش آن اشاره می‌کند که برای یافتن آن باید به چه جدولی ارجاع کرد. مقادیر زیر این نکته را روشن می‌کند که هر کد ابتدایی به چه جدولی اشاره می‌کند:
 0x01
 TypeRef
 0x02
 TypeDef
 0x23
 AssemblyRef
 0x26
 File
file definition

 0x27
 ExportedType
برای دیدن لیست کاملی از این کدها فایل Corhdr.h را که به همراه فریم ورک دات نت نصب می‌شود، مطالعه فرمایید. سه بایت باقیمانده هم بر اساس جدولی که به آن ارجاع شده است مشخص می‌گردد؛ مثلا در مثال بالا کد 0x26000001  به اولین سطر جدول File اشاره می‌کند. برای اکثر جدول‌ها شماره گذاری سطرها از عدد 1 آغاز می‌شود نه صفر یا برای برای جداول TypeDef عموما از عدد 2 آغاز می‌شود. 

برای اجرای اسمبلی، کامپایلر نیاز دارد که همه‌ی فایل‌های اسمبلی، نصب شده و قابل دسترس باشند و در صورتیکه شما فایل RUT.netmodule را حذف کنید کامپایلر سی شارپ خطای زیر را صادر می‌کند:
fatal error CS0009: Metadata file 'C:\ MultiFileLibrary.dll' could not be opened—'Error importing module 'RUT.netmodule' of assembly 'C:\ MultiFileLibrary.dll'—The system cannot find the file specified'

و این خطا بدین معنی است که برای ساخت اسمبلی باید تمامی فایل‌ها حاضر و مهیا باشند. هر کد کلاینتی که اجرا می‌شود آن متد را صدا می‌زنند. موقعی که یک متد برای اولین بار فراخوانی می‌شود، CLR عملیات شناسایی جهت شناسایی ارجاعات آن در پارامترها، نوع خروجی متد و متغیرهای محلی آن اجرا می‌کند. سپس تلاش می‌کند تا فایل اسمبلی ارجاع شده را که شامل مانیفست هست، بار کند. اگر نوعی که لازم داریم در همین فایل متد وجود داشته باشد، اجرای عملیات را به سمت آن آغاز می‌کند ولی اگر جدول مانیفست ارجاع را به فایل دیگری بدهد، آن فایل در حافظه بار شده و سپس آن نوع را در دسترس قرار می‌دهد. 
خطوط بالا این نکته را روشن می‌کند که فایل‌های اسمبلی را تنها موقعی در حافظه بار میکند که ارجاعی از نوع موجود در آن صدا زده شده باشد؛ یعنی اینکه در زمان اجرای برنامه، لازم نیست که همه‌ی فایل‌ها حاضر و مهیا باشند.
مطالب
آشنایی با CLR: قسمت سیزدهم
ترکیب ماژول‌ها به قالب یک اسمبلی
فایل Program.exe یک فایل PE با جداول متادیتا است که همچنین یک اسمبلی هم می‌باشد. یک اسمبلی مجموعه‌ای از یک یا چند فایل، شامل تعاریف نوع و منابع (ریسورس) می‌باشد و یکی از فایل‌های اسمبلی، برای نگهداری manifest انتخاب می‌شود. این جدول مجموعه‌ای است از جداول متادیتا که به طور کلی شامل نام فایل‌هایی است که قسمتی از اسمبلی را تشکیل می‌دهند. برای همین گفتیم که CLR با اسمبلی‌ها کار می‌کند. ابتدا جداول manifest را خوانده تا نام فایل‌ها را شناسایی کرده تا از آن‌ها را به حافظه بارگزاری کند. اسمبلی‌ها چند خصوصیت دارند که باید آن‌ها را بدانید:
- نوع‌های با قابلیت استفاده‌ی مجدد را تعریف می‌کنند.
- داری شماره‌ی نسخه version هستند.
- می‌توانند شامل اطلاعات امنیتی باشند.
این خواصی است که یک اسمبلی به همراه دارد و فایل‌هایی که شامل می‌شود، نمی‌توانند چنین خاصیتی را داشته باشند؛ مگر اینکه آن فایل‌ها در متای خود جدول manifest داشته باشند.
شما برای بسته بندی، شماره نسخه، مباحث امنیتی و استفاده از نوع‌ها، باید آن‌ها را داخل ماژولی قرار دهید که جزئی از اسمبلی است. یک فایل اسمبلی  همانند program.exe به عنوان یک فایل واحد شناخته می‌شود. با اینکه یک اسمبلی از چند فایل تشکیل می‌شود، فایل‌های PE به همراه جداول متادیتای آن و تعدادی ریسورس مثل فایل‌های gif و jpg است که به شما کمک می‌کند به همه‌ی آن‌ها به عنوان یک فایل منطقی EXE یا dll نگاه کنید.
یکی از دلایلی که در قسمت سوم گفتیم این بود که می‌توانیم فایل‌هایی را که به ندرت استفاده می‌شوند، از طریق اینترنت مورد استفاده قرار دهیم. در حالتیکه نیاز به دسترسی به اسمبلی‌های روی اینترنت دارید، CLR ابتدا کش را بررسی می‌کند تا آیا فایل حاضر است یا خیر؟ اگر پاسخ مثبت بود، در حافظه قرار می‌گیرد. ولی اگر پاسخ منفی بود، CLR به آدرسی که اسمبلی در آن قرار دارد، رجوع کرده و آن را دانلود می‌کند و اگر فایل مد نظر یافت نشد، استثنای FileNotFound  را در حین اجرا صادر خواهد کرد.
آقای جفری ریچر در کتاب خود سه تا از دلایل استفاده‌ی از اسمبلی‌های چند فایله را بر می‌شمارد:
  • جداسازی نوع‌ها در فایل‌های جداگانه که باعث کاهش حجم فایل از طریق اینترنت و بارگزاری حجم کمتر در حافظه  می‌شوند.
  • استفاده از فایل‌های منبع و داده‌ها در اسمبلی: فرض کنید نیاز به محاسبه‌ی اطلاعات بیمه دارید و برای این کار به اطلاعات داخل یک جدول آماری احتیاج دارید. این جدول آماری می‌تواند یک فایل متنی ساده یا یک صفحه‌ی گسترده مثل اکسل یا در قالب ورد و هر چیز دیگری باشد که به جای embed شدن این جداول در سورس کد برنامه، آن‌ها را با استفاده از ابزاری مثل Assembly Linker -AL.exe می‌توانید جزئی از اسمبلی کنید و فقط نیاز است که بدانید چگونه آن فایل را پارس یا تبدیل کنید.
  • استفاده از انواع ایجاد شده در زبان‌های مختلف. در این حالت شما مقداری از کد را با استفاده از #C نوشته اید و مقداری از آن را با Visual Basic می‌نویسید و هر کدام در نهایت به یک ماژول جداگانه کامپایل خواهند شد. ولی تبدیل آن به یک واحد منطقی مثل اسمبلی ممکن است و از این نظر می‌توانید روی ماژول‌های یک دسته کنترل داشته باشید.
اگر چندین نوع دارید که شامل نسخه بندی و تنظیمات امنیتی مشترک هستند، بهتر است در یک اسمبلی قرار گیرند تا اینکه در اسمبلی‌های جداگانه‌ایی قرار بگیرند. دلیل این کار هم ایجاد performance یا کارآیی بهتر است. بارگذاری یک  اسمبلی در حافظه زمانی را برای یافتن آن از CLR و ویندوز می‌گیرد و سپس وارد بارگیری آن‌ها در حافظه و آماده سازی می‌شود. پس هر چه تعداد اسمبلی‌ها کمتر باشد، کارآیی بهتری خواهید داشت، چون کمتر شدن بارگیری برابر با کاهش  صفحات کاری است و پراکندگی fragmentation فضای آدرس دهی آن فرایند را کاهش خواهد داد. نهایتا Ngen می‌تواند در بهینه سازی فایل‌های بزرگتر موفق باشد.
برای ساخت اسمبلی، باید یکی از فایل‌های PE را برای نگهداری جدول manifest انتخاب کنید؛ یا خودتان یک فایل PE جدا درست کنید که تنها شامل جدول مانیفست شود. جدول زیر قالبی از جداول مانیفست هست که بابت ماژول‌های اضافه شده به یک اسمبلی ایجاد می‌شوند.
 AssemblyDef  شامل مدخل ورودی (آدرس شروع حافظه) برای اسمبلی‌هایی است که ماژول عضو آن است. این مدخل شامل نام اسمبلی (بدون مسیر و پسوند)، شماره نسخه یا ورژن، culture، فلگ، الگوریتم هش و کلید عمومی ناشر، که می‌تواند نال باشد، هست.
 FileDef  شامل یک مدخل ورودی برای هر فایل PE و فایل‌های ریسورسی است که قسمتی از اسمبلی را تشکیل می‌دهند. این مدخل ورودی شامل نام و پسوند فایل (بدون ذکر مسیر)، فلگ و مقدار هش می‌شود. اگر تنها یک اسمبلی وجود داشته باشد، این جدول هیچ مدخلی نخواهد داشت.
 ManifestResourceDef  شامل یک مدخل ورودی برای هر فایل ریسورس است. این مدخل شامل نام فایل ریسورس، فلگ و یک اندیس به جدول FileDef است که در آن اشاره‌ای به آن فایل ریسورس یا استریم است.
 ExportedTypesDef  شامل یک مدخل ورودی برای هر نوع عمومی است که از همه ماژول‌های PE استخراج شده است. هر مدخل شامل نام نوع و اندیسی به جدول FileDef و یک اندیس دیگر به جدول TypeDef است. نکته: برای ذخیره سازی حافظه و کم حجم شدن فایل‌ها، نوع‌های استخراج شده از فایلی که شامل مانیفست است دیگر در جدول جاری نام نوع‌ها ذکر نمی‌گردد؛ چرا که این اطلاعات در جدول TypeDef اسمبلی جاری موجود است.
  نکته: اسمبلی که شامل مانیفست است، شامل یک جدول AssemblyRef نیز می‌گردد که به تمام اسمبلی‌های ارجاع شده در آن اسمبلی اشاره می‌کند. با استفاده از ابزارهای موجود می‌توان اسمبلی مدنظر را باز کرده و به این ترتیب لیستی از اسمبلی‌های ارجاع شده را خواهید دید و بدین صورت این اسمبلی یک اسمبلی خود تعریف می‌شود.

کامپایلر سی شارپ با استفاده از سوئیچ‌های زیر یک اسمبلی را تولید می‌کند:
/t[arget]:exe, /t[arget]:winexe, /t[arget]: appcontainerexe, /t[arget]: library, or /t[arget]:winmdobj

سوئئیچ‌های بالا باعث می‌شود که یک فایل PE با جدول مانیفست تولید گردد. در صورتیکه سوئیچ زیر را به کار ببرید، فایل تولید شده شامل جدول مانیفست نمی‌شود.
/t[arget]:module
این فایل PE تولید شده در قالب یک dll است که باید قبل از اینکه CLR به نوع‌های داخل آن دسترسی پیدا کند، به یک اسمبلی اضافه گردد. موقعی‌که شما از سوئیچ بالا استفاده می‌کنید، کامپایلر سی شارپ به طور پیش فرض از پسوند netmodule برای فایل خروجی استفاده می‌کند.

نکته‌ی پایانی: محیط توسعه ویژوال استادیو به طور پیش فرض از اسمبلی‌های چند فایل پشتیبانی نمی‌کند، اگر میخواهید که اسمبلی‌های چند فایله تولید کنید باید در سوئیچ‌های مورد استفاده آن تجدید نظری داشته باشید.
در مقاله آینده این روش‌ها را بررسی خواهیم کرد...