۱۳۸۷/۰۹/۱۰

بررسي نحوه برنامه نويسي سايت نستعلیق آنلاین


سايت نستعلیق آنلاین با استفاده از ASP.Net و فونت ايران نستعليق ايجاد شده است. شايد اين سؤال پيش بيايد كه چگونه اينكار را انجام داده‌اند؟ چگونه متن را به تصوير تبديل كرده‌اند يا از همه مهم‌تر چگونه فونت را به صورت پويا بارگذاري مي‌كنند (چون عموما هاست‌ها فونتي را براي شما نصب نخواهند كرد)؟
براي انجام اينكار از كلاس PrivateFontCollection فضاي نام System.Drawing.Text مي‌توان استفاده كرد. نحوه انجام اين‌كار را در يكي از پروژه‌هاي سايت codeproject مي‌توان ملاحظه نمود.
كمي اين پروژه را اصلاح كردم به همراه افزودن و تنظيم خواص توليد تصوير با كيفيت بالا. پروژه نهايي را به همراه قلم‌هاي مربوطه، از اينجا مي‌توانيد دريافت كنيد.
احتمالا در سايت نستعلیق آنلاین از روش موجود در مقاله بالا استفاده شده است كه مجبور شده‌اند تصوير نهايي را در يك صفحه ديگر نمايش دهند (تخليه بافر در مرورگر). در پروژه‌اي كه ضميمه شد، تصوير در يك پوشه ذخيره شده و سپس نمايش داده مي‌شود. به اين صورت تصوير را مي‌توان در هر جايي از صفحه بدون ارجاع كاربر به صفحه دوم نمايش داد. البته بايد دقت داشت كه يوزر asp.net بايد دسترسي write را بر روي اين فولدر كه در اينجا files نام دارد، داشته باشد.
راه ديگر انجام اينكار استفاده از http handlers است. نحوه پياده سازي اين تكنيك را در اين مقاله مي‌توانيد مشاهده نمائيد.


۱۳۸۷/۰۹/۰۹

آشنايي با انواع Control ID ها در ASP.Net


اگر مطلب تبديل پلاگين‌هاي جي‌كوئري به كنترل‌هاي ASP.Net را مطالعه كرده باشيد، در مورد ClientID بحث شد. با مراجعه به اصول HTML‌ درخواهيم يافت كه هر كنترل يا شيء مربوطه مي‌تواند شامل ID و name باشد. عموما در كدهاي جاوا اسكريپتي براي دسترسي به يك شيء در صفحه از ID آن شيء به صورت document.getElementById استفاده شده و از name براي پردازش‌هاي سمت سرور استفاده مي‌شود. براي مثال اگر به دوران ASP كلاسيك برگرديم، از شيء Request براي دريافت مقادير ارسال شده به سرور با استفاده از name شيء مورد نظر استفاده مي‌شد/مي‌شود.
در حالت فرم‌هاي معمولي ، ID و name عموما يكسان هستند. اما اگر از master page ها استفاده شود يا از يك user control براي تركيب چندين كنترل كمك گرفته شود، ديگر ID و name در فرم رندر شده نهايي ASP.Net يكسان نخواهند بود. براي مثال:
<input name="ctl00$ContentPlaceHolder1$txtID" type="text" id="ctl00_ContentPlaceHolder1_txtID" />

اگر به سورس دات نت فريم ورك مراجعه كنيم علت وجود _ بجاي $ را در مقدار id مي‌توان مشاهده كرد:

public virtual string ClientID
{
get
{
this.EnsureID();
string uniqueID = this.UniqueID;
if ((uniqueID != null) && (uniqueID.IndexOf(this.IdSeparator) >= 0))
{
return uniqueID.Replace(this.IdSeparator, '_');
}
return uniqueID;
}

}

و جهت توليد name به صورت زير عمل مي‌شود (يك الگوريتم بازگشتي است):
<Container>$<Container>$<Container>$<Container>$...$<ID>

زمانيكه يك كنترل توسط ASP.Net رندر مي‌شود، خاصيت UniqueID معادل name آن و ClientID معادل ID آن كنترل در سمت كلاينت خواهد بود. اگر از IE استفاده كنيد به هر نحوي سعي در پردازش اسكريپت شما خواهد كرد و document.getElementById بالاخره جواب خواهد داد. اما اگر از ساير مرورگرها استفاده كنيد، در صورتيكه بجاي ID از name استفاده شده باشد، اسكريپت شما با پيغامي مبني بر يافت نشدن شيء مورد نظر متوقف خواهد شد (چون مقدار اين‌دو همانطور كه ملاحظه كرديد الزاما يكسان نخواهد بود).
اين نكته در طراحي كنترل‌هاي ASP.Net كه از كدهاي جاوا اسكريپتي استفاده مي‌كنند بسيار مهم است. در تست اول و با يك صفحه ساده كنترل شما خوب كار خواهد كرد. اما اگر همين كنترل را بر روي يك صفحه مشتق شده از يك master page قرار دهيد، ديگر ID آن يافت نشده و كدهاي جاوا اسكريپتي شما كار نخواهند كرد. به همين جهت اگر قرار است قسمت جاوا اسكريپتي كنترل شما توسط كنترل به صورت خودكار ايجاد شود و در اين كد ارجاعي به شيء جاري وجود دارد، اين شيء جاري با استفاده از ClientID آن در سمت كلاينت قابل دسترسي خواهد بود و نه با استفاده از ID سمت سرور و يا UniqueID آن.

۱۳۸۷/۰۹/۰۸

يادي از گذشته!


چندين سال قبل مطالبي را در مورد ASM32 و ASP.Net و غيره منتشر كرده بودم كه هنوز هم هفته‌اي يك ايميل دارم به اين مضمون كه لينك‌هاي آن‌ها كار نمي‌كنه و لطفا دوباره آنها را براي من ارسال كنيد. به نظر يكي از دوستان قبلا اين‌كار را انجام داده است. بنده ايشون رو نمي‌شناسم ولي با تشكر از ايشان!، تقريبا اكثر موارد منتشر شده قديمي را از اين آدرس مي‌توانيد دريافت كنيد.


تبديل پلاگين‌هاي jQuery‌ به كنترل‌هاي ASP.Net


امروز داشتم يك سري از پلاگين‌هاي jQuery را مرور مي‌كردم، مورد زير به نظرم واقعا حرفه‌اي اومد و كمبود آن هم در بين كنترل‌هاي استاندارد ASP.Net محسوس است:
Masked Input Plugin
استفاده از آن به صورت معمولي بسيار ساده است. فقط كافي است اسكريپت‌هاي jQuery و سپس اين افزونه به هدر صفحه اضافه شوند و بعد هم مطابق صفحه usage آن عمل كرد.
خيلي هم عالي! ولي اين شيوه‌ي متداول كار در ASP.Net نيست. آيا بهتر نيست اين مجموعه را تبديل به يك كنترل كنيم و از اين پس به سادگي با استفاده از Toolbox ويژوال استوديو آن‌را به صفحات اضافه كرده و بدون درگير شدن با دستكاري سورس html صفحه، از آن استفاده كنيم؟ به‌عبارتي ديگر يكبار بايد با جزئيات درگير شد، آنرا بسته بندي كرد و سپس بارها از آن استفاده نمود. (مفاهيم شيءگرايي)

براي اين‌كار، يك پروژه جديد ايجاد ASP.Net server control را آغاز نمائيد (به نام MaskedEditCtrl).



به صورت پيش فرض يك قالب استاندارد ايجاد خواهد شد كه كمي نياز به اصلاح دارد. نام كلاس را به MaskedEdit تغيير خواهيم داد و همچنين در قسمت ToolboxData نيز نام كنترل را به MaskedEdit ويرايش مي‌كنيم.
براي اينكه مجبور نشويم يك كنترل كاملا جديد را از صفر ايجاد كنيم، خواص و توانايي‌هاي اصلي اين كنترل را از TextBox استاندارد به ارث خواهيم برد. بنابراين تا اينجاي كار داريم:
namespace MaskedEditCtrl
{
[DefaultProperty("MaskFormula")]
[ToolboxData("<{0}:MaskedEdit runat=server></{0}:MaskedEdit>")]
[Description("MaskedEdit Control")]
public class MaskedEdit : TextBox

{

سپس بايد رويداد OnPreRender را تحريف (override) كرده و در آن همان اعمالي را كه هنگام افزودن اسكريپت‌ها به صورت دستي انجام مي‌داديم با برنامه نويسي پياده سازي كنيم. چند نكته ريز در اينجا وجود دارد كه در ادامه به آن‌ها اشاره خواهد شد.
از ASP.Net 2.0 به بعد، امكان قرار دادن فايل‌هاي اسكريپت و يا تصاوير همراه يك كنترل، درون فايل dll آن بدون نياز به توزيع مجزاي آنها به صورت WebResource مهيا شده است. براي اين منظور اسكريپت‌هاي jQuery و افزونه mask edit را به پروژه اضافه نمائيد. سپس به قسمت خواص آنها (هر دو اسكريپت) مراجعه كرده و build action آنها را به Embedded Resource تغيير دهيد (شكل زير):



از اين پس با كامپايل پروژه، اين فايل‌ها به صورت resource به dll ما اضافه خواهند شد. براي تست اين مورد مي‌توان به برنامه reflector مراجعه كرد (تصوير زير):



پس از افزودن مقدماتي اسكريپت‌ها و تعريف آنها به صورت resource ، بايد آنها را در فايل AssemblyInfo.cs پروژه نيز تعريف كرد (به صورت زير).

[assembly: WebResource("MaskedEditCtrl.jquery.min.js", "text/javascript")]
[assembly: WebResource("MaskedEditCtrl.jquery.maskedinput-1.1.4.pack.js", "text/javascript")]

نكته مهم: همانطور كه ملاحظه مي‌كنيد نام فضاي نام پروژه (namespace) بايد به ابتداي اسكريپت‌هاي معرفي شده اضافه شود.

پس از آن نوبت به افزودن اين اسكريپت‌ها به صورت خودكار در هنگام نمايش كنترل است. براي اين منظور داريم:
        protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);

//adding .js files
if (!Page.ClientScript.IsClientScriptIncludeRegistered("jquery_base"))
{
string scriptUrl = Page.ClientScript.GetWebResourceUrl(this.GetType(),
"MaskedEditCtrl.jquery.min.js");
Page.ClientScript.RegisterClientScriptInclude("jquery_base", scriptUrl);
}

if (!Page.ClientScript.IsClientScriptIncludeRegistered("edit_ctrl"))
{
string scriptUrl = Page.ClientScript.GetWebResourceUrl(this.GetType(),
"MaskedEditCtrl.jquery.maskedinput-1.1.4.pack.js");
Page.ClientScript.RegisterClientScriptInclude("edit_ctrl", scriptUrl);
}

if (!Page.ClientScript.IsStartupScriptRegistered("MaskStartup" + this.ID))
{
// Form the script to be registered at client side.
StringBuilder sbStartupScript = new StringBuilder();
sbStartupScript.AppendLine("jQuery(function($){");
sbStartupScript.AppendLine("$(\"#" + this.ClientID + "\").mask(\"" + MaskFormula + "\");");
sbStartupScript.AppendLine("});");
Page.ClientScript.RegisterStartupScript(typeof(Page),
"MaskStartup" + this.ID, sbStartupScript.ToString(), true);
}

}

همانطور كه ملاحظه مي‌كنيد، ابتدا WebResource دريافت شده و سپس به صفحه اضافه مي‌شود. در آخر مطابق راهنماي افزونه mask edit عمل شد. يعني اسكريپت مورد نظر را ساخته و به صفحه اضافه كرديم.

نكته جاوا اسكريپتي: علت استفاده از this.ClientID جهت معرفي نام كنترل جاري اين است كه هنگاميكه كنترل توسط يك master page رندر شود، ID آن توسط موتور ASP.Net كمي تغيير خواهد كرد. براي مثال myTextBox‌ به ctl00_ContentPlaceHolder1_myTextBox تبديل خواهد شد و اگر صرفا this.ID ذكر شده باشد ديگر دسترسي به آن توسط كدهاي جاوا اسكريپت مقدور نخواهد بود. بنابراين از ClientID جهت دريافت ID نهايي رندر شده توسط ASP.Net كمك مي‌گيريم.

در اينجا MaskFormula مقداري است كه هنگام افزودن كنترل به صفحه مي‌توان تعريف كرد.

[Description("MaskedEdit Formula such as 99/99/9999")]
[Bindable(true), Category("MaskedEdit"), DefaultValue(0)]
public string MaskFormula
{
get
{
if (ViewState["MaskFormula"] == null) ViewState["MaskFormula"] = "99/99/9999";
return (string)ViewState["MaskFormula"];
}
set { ViewState["MaskFormula"] = value; }

}

اين خاصيت public هنگام نمايش در Visual studio به شكل زير درخواهد آمد:



نكته مهم: در اينجا حتما بايد از view state جهت نگهداري مقدار اين خاصيت استفاده كرد تا در حين post back ها مقادير انتساب داده شده حفظ شوند.

اكنون پروژه را كامپايل كنيد. براي افزودن كنترل ايجاد شده به toolbox مي‌توان مطابق تصوير عمل كرد:



نكته: براي افزودن آيكون به كنترل (جهت نمايش در نوار ابزار) بايد: الف) تصوير مورد نظر از نوع bmp باشد با اندازه 16 در16 pixel . ب) بايد آنرا به پروژه افزود و build action آن را به Embedded Resource تغيير داد. سپس آنرا در فايل AssemblyInfo.cs پروژه نيز تعريف كرد (به صورت زير).

[assembly: System.Web.UI.WebResource("MaskedEditCtrl.MaskedEdit.bmp", "img/bmp")]

كنترل ما پس از افزوده شدن، شكل زير را خواهد داشت:


جهت دريافت سورس كامل و فايل بايناري اين كنترل، اينجا كليك كنيد.


۱۳۸۷/۰۹/۰۷

لينك‌هاي هفته اول آذر


وبلاگ‌ها و سايت‌هاي ايراني


Visual Studio


ASP. Net


طراحي وب


اس‌كيوال سرور


به روز رساني‌ها


سي‌شارپ


عمومي دات نت


PHP



متفرقه



۱۳۸۷/۰۹/۰۶

تميزكردن زوايد HTML نهايي سايت


آيا مي‌دانيد CSS سايت شما تا چه اندازه مفيد و مورد مصرف بوده و كداميك از selector هاي آن بدون مصرف باقيمانده‌اند؟
خوشبختانه افزونه مفيدي براي فايرفاكس به نام Dust-Me Selectors موجود است كه خروجي سايت را بررسي كرده و اضافات را گوشزد خواهد كرد. اين افزونه را از آدرس زير مي‌توانيد دريافت كنيد:
https://addons.mozilla.org/en-US/firefox/addon/5392
پس از نصب، يك آيكون جارو به status bar فايرفاكس اضافه خواهد شد كه با كليك بر روي آن، صفحه جاري آناليز شده و css selectors بدون استفاده در آن گوشزد خواهند شد.



همچنين مورد ديگري كه عموما رديابي آن مشكل است، تشخيص تصاوير مفقود يك صفحه است. كداميك از عناصري كه در فايل HTML نهايي به آنها ارجاعي وجود دارد واقعا در سايت ما موجود است و از قلم نيفتاده است؟
براي اين منظور ابتدا Firebug را نصب كنيد. سپس افزونه Yslow آنرا نيز بايد نصب نمود.
زمانيكه يك صفحه درحال بارگذاري است، بر روي آيكون Yslow در status bar فايرفاكس كليك كرده و پس از نمايان شدن آن، بر روي Performance كليك كنيد تا كار آناليز عناصر صفحه آغاز شود. پس از پايان كار، بر روي دكمه components كليك نمائيد تا علاوه بر مشاهده تاثير عناصر مختلف صفحه بر نحوه بارگذاري و سرعت سايت شما، عناصر مفقود را با رنگ قرمز نمايان سازد.



۱۳۸۷/۰۹/۰۵

چرا نبايد از كوئري‌هاي select * استفاده كرد؟


عموما اولين پاسخي كه به اين سؤال داده مي‌شود اين است كه اين نوع كوئري‌ها اطلاعات زيادي را باز مي‌گردانند و در نتيجه ترافيك شبكه بي‌جهت افزايش خواهد يافت. اما اگر طراحي ديتابيس صحيح بوده و اصول نرمال سازي در آن پياده سازي شده باشد، اين پاسخ آنچنان صادق نخواهد بود (زيرا جداول اينگونه ديتابيس‌ها از تعداد فيلدهاي بسياري تشكيل نخواهند شد). براي مثال به نتيجه اجراي كوئري‌هاي زير بر روي ديتابيس AdventureWorks با 89 هزار ركورد، دقت بفرمائيد:
SELECT * FROM Production.TransactionHistoryArchive
WHERE ReferenceOrderID < 100

SELECT ReferenceOrderLineID FROM Production.TransactionHistoryArchive
WHERE ReferenceOrderID < 100

اختلاف ترافيك شبكه در اين مثال تنها 15K يا حدودا 10 درصد است (180K در مقابل 165K). هر چند ارزش بررسي و برطرف كردن را دارد اما تفاوت حاصل آنچنان قابل ملاحظه نيست.
مهم‌ترين دليلي كه اينجا بايد به آن دقت داشت، تفاوت چشمگير execution plan اين دو كوئري (Ctrl-L) و بحث index coverage است. اس كيوال سرور براي اجراي بهينه كوئري‌ها از ايندكس‌هاي موجود استفاده خواهد كرد. اگر ايندكس تعريف شده از تمامي فيلدهاي درخواستي شما تشكيل شده باشد، ديگر حتي به سراغ جدول هم نخواهد رفت (به اين مفهوم، پوشش ايندكسي گفته مي‌شود).



براي توليد تصوير فوق، كليدهاي Ctrl+L را در management studio فشار دهيد.
اين ديتابيس را از آدرس زير مي‌توانيد دريافت كنيد:
http://www.codeplex.com/MSFTDBProdSamples

كوئري اول از مزاياي پوشش ايندكسي برخودار نخواهد بود (از روش جستجوي Clustered Index استفاده مي‌كند) و در حالت دوم از Index Seek استفاده مي‌گردد. حالت Index Seek يك‌صد بار بهينه‌تر از استفاده از Clustered Index عمل مي‌كند زيرا در حالت كوئري اول بايد تمامي ركوردهاي جدول بررسي شوند (اين عدد از مقايسه نتايج execution plan بدست آمده است).
تنها در صورتيكه بر روي تمامي فيلدهاي جدول ايندكس تعريف كرده باشيد (كه اصلا توصيه نمي‌شود)، كوئري اول توسط ايندكس‌ها پوشش داده شده و سريع اجرا خواهد شد.
بنابراين اگر از كندي اجراي كوئري‌ها با تعداد ركورد بالا شكايت داريد بهتر است نگاهي به نحوه تعريف آنها داشته باشيد و تنها فيلدهايي را در كوئري تعريف كنيد كه به آنها نياز داريد. در اين حالت از مزاياي پوشش ايندكسي برخودار شده ، كوئري‌هاي سريعتري را خواهيد داشت و همچنين در اين حالت ميزان مصرف CPU و حافظه نيز بر روي سرور كمتر خواهد بود.
همچنين در حالت كوئري‌هايي از نوع دوم ذكر شده، موتور بهينه ساز اس كيوال سرور پيشنهادات بهتري را براي ايجاد ايندكس‌هاي جديد و گوشزد نمودن كمبود آنها با ارائه included columns مناسب، ارائه مي‌دهد.
بعلاوه مشخص ساختن تعداد دقيق فيلدهاي مورد نياز، نگهداري برنامه را ساده‌تر ساخته و فيلدهاي اضافه شده آتي سبب تغيير رفتار كوئري‌ها برنامه نخواهند شد و استفاده نكردن از آن نشانه اين است كه هيچ برآوردي از ابعاد واقعي كار در دست نيست.

مآخذ:
Speed Up Your Site! 8 ASP.NET Performance Tips
The real reason SELECT * queries are bad: index coverage


۱۳۸۷/۰۹/۰۴

شبيه سازي ارسال ايميل در ASP.Net


فرض كنيد مشغول به كار بر روي كامپيوتري هستيد كه دسترسي به هيچ شبكه‌اي ندارد و همچنين نياز است تا قسمت اطلاع رساني برنامه ASP.Net خود را كه از طريق ايميل كار مي‌كند، تست كنيد. براي مثال حداقل يكبار شكل و شمايل و محتواي ايميل واقعي ارسالي آنرا در آوت لوك مشاهده كنيد. براي حل اين مساله چه بايد كرد؟
براي تحقق اين منظور بايد كمي فايل web.config سايت را ويرايش كرد و سطرهاي زير را به آن افزود (پس از بسته شدن تگ system.web):

<system.net>
<mailSettings>
<smtp deliveryMethod="SpecifiedPickupDirectory">
<specifiedPickupDirectory pickupDirectoryLocation="c:\mail"/>
</smtp>
</mailSettings>
</system.net>

و همچنين در اينجا بايد دقت داشت كه هنگام كد نويسي ديگر نيازي به ذكر smtp server نخواهد بود و new SmtpClient().Send تنظيمات خودش را از فايل كانفيگ خواهد خواند.
اكنون با هر بار ارسال ايميل، نتيجه حاصل (مطابق تصاوير زير) در مسير c:\mail ذخيره خواهد شد و فرمت حاصل با استفاده از outlook قابل مشاهده است.





شايان ذكر است كه اين روش با برنامه‌هاي غير ASP.Net نيز كار مي‌كند و تنها كافي است يك فايل app.config‌ به برنامه اضافه كرده و تنظيمات فوق را به آن اعمال نمائيد.

۱۳۸۷/۰۹/۰۳

درست كردن فايل راهنماي CHM از توضيحات XML يك پروژه


تا حالا هيچ وقت براي شما اين سؤال پيش اومده كه اين فايل‌هاي CHM راهنماي زيبايي كه براي مثال به‌عنوان مستندات يك كتابخانه در دات نت ارائه مي‌شوند با چه نرم‌افزار يا نرم‌افزارهايي توليد مي‌شوند؟ يا اينكه به نظر يك يا چند نفر ساعت‌ها وقت مي‌گذارند، صفحات HTML مربوطه را توليد مي‌كنند و در آخر با استفاده از ابزارهاي توليد فايل CHM ، فايل راهنما را نهايي مي‌كنند؟
اين فايل‌ها به صورت خودكار بر اساس XML code comments ارائه شده براي يك متد ، كلاس و امثال آن توليد مي‌شوند. براي مثال به توضيحات زير دقت بفرمائيد:

/// <summary>
/// استخراج ايميل‌هاي يك فايل متني و ذخيره آن در فايلي جديد
/// </summary>
/// <param name="inFilePath">فايل ورودي</param>
/// <param name="outFilePath">فايل خروجي</param>
public static void ExtractEmails(string inFilePath, string outFilePath)

هر چند VS.Net‌ در ايجاد خوكار قالب اوليه اين نوع كامنت‌ها بسيار خوب عمل مي‌كند اما نكات پيشرفته‌تري نيز در اين‌باره موجود هستند كه در كيفيت فايل راهنماي توليد شده بر اساس اين توضيحات بسيار مؤثرند. راهنماي كاملي در اين‌باره را از اينجا مي‌توانيد دريافت كنيد.
در ادامه نحوه توليد خودكار اين نوع راهنماها را بررسي خواهيم كرد.

الف) نصب برنامه‌هاي مورد نياز
براي ايجاد فايل chm از توضيحات xml ايي ارائه شده، ابتدا دو برنامه سورس باز زير را دريافت و نصب كنيد:
سپس نياز به HTML Help 2.0 compiler خواهد بود. اين كامپايلر به همراه SDK ويژوال استوديو ارائه مي‌شود. بسته به نگارش VS مورد استفاده، نياز است تا يكي از موارد زير را دريافت و نصب كنيد:
برنامه hxcomp.exe ذكر شده، عموما در مسير زير نصب خواهد شد:
%Program Files%\Common Files\Microsoft Shared\Help 2.0 Compiler\
ب) تنظيمات VS.Net
مرحله بعد به تنظيمات VS.Net مربوط مي‌شود. به صفحه خواص پروژه مراجعه كنيد و در برگه Build ، گزينه توليد XML documentation file را انتخاب كنيد. سپس مجددا پروژه خود را كامپايل كنيد.

ج) تنظيمات Sandcastle Help File Builder
يك پروژه جديد را در اين برنامه آغاز كرده و سپس فايل اسمبلي و xml توليد شده آنرا انتخاب كنيد. (بر روي دكمه add كليك كرده و هر دو فايل exe يا dll مورد نظر را به همراه فايل xml آن كه در قسمت ب توليد كرديم، انتخاب كنيد. در صورت عدم انتخاب يكي از موارد فايل راهنما توليد نخواهد شد)
اكنون نوبت به تنظيمات پروژه مي‌رسد.
در قسمت Build:
گزينه Help File Format را انتخاب كرده و سپس html help 2x را نيز تيك بزنيد. (در صورت تمايل به توليد اين نوع فرمت)
در قسمت Dependencies ، تمام اسمبلي‌هايي را كه پروژه شما به آن وابسته است، اضافه كنيد.
توسط گزينه Framework Version ، نگارشي از دات نت فريم ورك كه اسمبلي شما بر اساس آن كامپايل شده است را انتخاب كنيد.
در قسمت Help File:
Presentation Style را بر روي VS2005 قرار دهيد. اين‌كار اجباري نيست اما راهنماي حاصل زيباتر خواهد بود.
در قسمت Paths :
مسيرهاي كامپايلرهاي راهنما را مشخص كنيد. بر روي سيستم من اين مسيرها مطابق شكل زير هستند:


اگر HTML Help Workshop بر روي سيستم شما نصب نبود، آنرا از اين آدرس دريافت نمائيد.

د) ساخت فايل راهنما
بر روي آيكون build در نوار ابزار برنامه كليك كنيد (و يا انتخاب گزينه documentation->build)

تا اينجا اگر هر دو نوع Help1xAndHelp2x را در قسمت build انتخاب كرده باشيد، دو نوع راهنماي مستقل و همچنين قابل يكپارچه شدن با سيستم راهنماي VS.net را توليد كرده‌ايد.



ه) يكپارچه سازي Help2x توليد شده با سيستم راهنماي VS.Net
پروژه جديدي را در VS.Net از نوع Other Project Types > Extensibility > Help Integration Wizard ايجاد كنيد. در مرحله اول، ايجاد setup و نوع VS را انتخاب كرده و در صفحه بعد فايل‌هاي Help2x خود را اضافه كنيد (فايلهايي با پسوند hxs). دو مرحله آخر را مطابق نيازهاي كاري خود تنظيم نموده و پروژه را در آخر build كنيد. نصاب توليد شده فايل‌هاي راهنماي شما را با سيستم راهنماي VS.Net يكپارچه خواهد ساخت.

چند نكته:
1- جهت سفارشي سازي بيشتر راهنماي توليد شده مي‌توان از ابزار سورس باز زير نيز كمك گرفت:
http://www.codeplex.com/DocProject
2- افزونه‌اي رايگان براي VS.Net جهت سهولت توليد توضيحات XML در آدرس زير قابل دريافت است:
http://www.roland-weigelt.de/ghostdoc

۱۳۸۷/۰۹/۰۲

استفاده از Google Analytics در ASP.Net


قبل از استفاده از بلاگر، در سايت wordpress وبلاگ داشتم، كه به‌دلايلي كنسل شد. تفاوت محسوسي را كه اينجا مشاهده مي‌كنم، نبود قسمت آمار سايت است. در سايت wordpress آمار مبسوطي را از بازديد كنندگان سايت مي‌توانيد در كنترل پنل مديريتي وبلاگ مشاهده كنيد، اما در اينجا خير.
به همين جهت اولين كاري را كه انجام دادم استفاده از سرويس رايگان persianstat بود كه انصافا هم با كيفيت است و قابل مقايسه با آماري كه wordpress ارائه مي‌دهد، مي‌باشد.
جالب اينجا است كه هر چند هاست اينجا، گوگل است اما استفاده‌ي خودكار از ابزار Google analytics در آن مهيا نيست. احتمالا علت آن آماده نبودن API آن است كه قرار است به زودي ارائه شود، بنابراين ارزش وقت گذاشتن را دارد.



براي استفاده از Google analytics ، پس از ثبت نام و ورود به آن، سايت مورد نظر را معرفي كرده (در قسمت Add Website Profile) و نهايتا يك كد جاوا اسكريپتي به شما خواهد داد كه مي‌توانيد آنرا به صفحات مورد نظر خود در سايت اضافه نمائيد تا تحت كنترل آماري قرار گيرد. محدوديتي هم در مورد تعداد سايت وجود ندارد و با يك اكانت مي‌توانيد چندين سايت را معرفي كرده و تحت كنترل قرار دهيد.
اگر از ASP.Net استفاده مي‌كنيد، تنها كافي است به master page سايت مراجعه كنيد و پيش از بسته شدن تگ body ، اسكريپت مربوط به Google analytics را اضافه كنيد تا تمام سايت را تحت كنترل قرار دهيد.
يا اگر علاقمند بوديد كه اينكار را به صورت "شيك‌تري" انجام دهيد، مي‌توان از اين http module استفاده كرد. به اين صورت ابتدا تگ بسته شدن body به صورت خودكار پيدا شده و سپس اسكريپت به پيش از آن اضافه مي‌شود.
اين روش بار بزرگ تهيه آمار سايت را حذف خواهد كرد. عموما ديتابيس جمع آوري آمار سايت خيلي زود (براي مثال پس از گذشت 6 ماه) حجيم مي‌شود و تاثير مشهودي را بر روي كارآيي سايت خواهد گذاشت. بنابراين، اين سؤال مطرح مي‌شود كه چرا گوگل اينكار را براي ما انجام ندهد؟! هزينه بانك اس كيوال سرور بر روي هاست‌هاي اينترنتي بالا بوده و حجمي را هم كه در اختيار قرار مي‌دهند محدود است. در صورت نياز به حجم‌هاي بالاتر بايد هزينه بيشتري را پرداخت كرد. بنابراين هم از لحاظ قيمت و هچنين كارآيي سايت، استفاده از اين سرويس واقعا مقرون به صرفه است. بعلاوه از تنوع آماري كه ارائه مي‌دهد نيز نمي‌توان چشم پوشي كرد. براي مثال كاربران چه واژه‌هاي كليدي را در موتورهاي جستجو وارد كرده‌اند تا به سايت شما رسيده‌اند؟ چند درصد كاربر وفادار داريد؟! (كاربرهاي وفادار، منظور افرادي هستند كه به صورت منظم به سايت سر مي‌زنند) و امثال اين. انصافا تهيه چنين ماژولي براي يك سايت از لحاظ برنامه نويسي شايد با برنامه نويسي كل يك سايت برابري كند.
اگر هم نياز به يك برنامه سورس باز داشتيد كه هر روز به اكانت Google analytics شما سر بزند و اطلاعات آنرا استخراج كرده و در يك بانك SQL server ذخيره كند، مي‌توانيد به پروژه سي شارپ زير مراجعه نمائيد:
Google Analytics Data Extractor

البته بايد دقت داشت كه پس از ارائه API كامل Google analytics ، ديگر نيازي به اين نوع روش‌هاي ابتكاري وجود نداشته و استخراج داده از آن بسيار ساده‌تر خواهد شد.

۱۳۸۷/۰۹/۰۱

بررسي جزئيات برنامه نويسي افزونه تاريخ فارسي براي outlook 2007 - قسمت دوم


اضافه كردن يك ستون در آوت لوك كار ساده‌اي است. براي مثال زمانيكه inbox باز است ، بر روي قسمت نمايش ايميل‌ها كليك راست كرده و مسير زير را طي كنيد:
در اينجا فرض بر اين است كه از منوي اصلي view->reading pane->bottom انتخاب شده است.
Customize current view -> fields -> new field

به اين صورت مي‌شود يك UserDefinedProperties را تعريف و سپس ‌آن‌را به ViewFields موجود اضافه كرد. نحوه انجام اينكار را در تابع addNewCol مي‌توانيد مشاهده نمائيد.

اما مقدار دهي رديف‌هاي اين ستون ايجاد شده كار ساده‌اي نيست و نياز است تا با نكته زير آشنا بود:
<view type="table">
<viewname>Messages</viewname>
<column>
<heading>تاريخ دريافت</heading>
<prop>http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/تاريخ%20دريافت</prop>
<type>string</type>
<width>150</width>
<style>padding-left:3px;;text-align:left</style>
<editable>1</editable>
</column>
</view>

Outlook ساختار ستون‌هاي موجود را با فرمت xml نگهداري مي‌كند و اگر نياز داشتيد تا رديف ستوني را مقدار دهي كنيد بايد ابتدا مقدار تگ prop مربوط به آن ستون را دريافت كرده و سپس بر اساس آن، كار مقدار دهي را انجام دهيد. نحوه انجام اين‌كار را در تابع getColumnProp مي‌توانيد ملاحظه نمائيد و نهايتا نحوه استفاده از اين خاصيت دريافت شده جهت مقدار دهي يك رديف در تابع expSelectionChange ارائه شده است.
اگر علاقمند بوديد كه مقدار كامل اين ساختار را مشاهده نمائيد در تابع addNewCol ، پس از تعريف curView سطر زير را اضافه كنيد:

MessageBox.Show(curView.XML);


۱۳۸۷/۰۸/۳۰

لينك‌هاي هفته آخر آبان


وبلاگ‌ها و سايت‌هاي ايراني


Visual Studio


ASP. Net


طراحي وب


اس‌كيوال سرور


به روز رساني‌ها


سي‌شارپ


عمومي دات نت


PHP


ويندوز


گوگل


متفرقه




۱۳۸۷/۰۸/۲۹

فشرده سازي اسمبلي‌هاي دات نت


ابزارهاي زيادي براي محافظت و يا فشرده سازي و رمزنگاري اسمبلي‌هاي دات نت موجود هستند كه اكثر آنها تجاري هستند. برنامه netz نمونه‌اي است سورس باز و رايگان كه تنها كار فشرده سازي اسمبلي موجود را انجام مي‌دهد. همچنين با استفاده از آن سورس اسمبلي شما به‌وسيله برنامه reflector قابل مرور نخواهد بود. هر چند اين برنامه سورس باز است و امكان unpack كردن نتيجه آن نيز احتمالا با اندكي سعي ميسر خواهد بود اما باز هم يك مرحله پيشرفت محسوب مي‌شود! خصوصا اينكه مي‌توان براي آن Custom Compression Provider نوشت و براي مثال فايل زيپ شده نهايي را رمزنگاري نيز كرد.

قبل از عمل:



بعد از عمل:


نحوه استفاده:

فشردن كردن يك فايل exe توسط آن
netz app.exe

الحاق كردن فايل zip.dll همراه با فايل exe (بدون نياز به توزيع فايل zip.dll):
netz -z app.exe

يكي كردن تمام dll هاي برنامه با فايل exe در قالب يك فايل نهايي:
netz -s app.exe lib1.dll lib2.dll

نكته:
در اينجا به صورت پيش فرض از فايل zip.dll براي فشرده سازي استفاده مي‌شود (كه براي تمام نگارش‌هاي دات نت قابل استفاده است). در نگارش‌هاي جديد دات نت، فشرده سازي نيز به كلاس‌هاي استاندارد اضافه شده است كه امكان استفاده از آن نيز در اينجا مهيا است (و ديگر نيازي به استفاده از zip.dll آن نخواهد بود).
netz.exe -r net20comp.dll  app.exe

نحوه برنامه نويسي يك compression provider سفارشي براي آن در آدرس زير توضيح داده شده است. (اعمال موارد امنيتي دلخواه و استفاده از آن)
http://madebits.com/netz/compress.php

و موارد ديگري كه در راهنماي سايت آن توضيح داده شده‌اند.

۱۳۸۷/۰۸/۲۸

پيدا كردن ليست SQL server هاي نصب شده در يك شبكه



با آمدن SQL server 2008 استفاده از كتابخانه SQL-DMO براي انجام يك سري از امور بر روي اس كيوال سرور با استفاده از برنامه نويسي منسوخ شد. يكي از توانايي‌هاي اين كتابخانه ليست كردن سرورهاي اس كيوال (قابل دسترسي) موجود در شبكه بود.
براي مثال توسط اين كتابخانه به صورت زير مي‌توان اينكار را انجام داد:
در قطعه كد زير فرض بر اين است كه ارجاعي به كتابخانه sqldmo را در برگه com مربوط به project->add reference اضافه كرده‌ايد:

using SQLDMO;
using System.Collections.Generic;

public static List<string> GetSQLServersList2()
{
List<string> result = new List<string>();
ApplicationClass sqlApp = new ApplicationClass();
NameList lst = sqlApp.ListAvailableSQLServers();
for (int i = 1; i <= lst.Count; i++)
result.Add(lst.Item(i));
lst = null;
sqlApp = null;

return result;

}

با منسوخ شدن اين كتابخانه COM (كه تنها تا اس كيوال سرور 2005 پشتيباني مي‌شود)، در نگارش‌هاي جديد (و قديم) اس كيوال سرور، با استفاده از قطعه كد زير مي‌توان ليست تمام SQL server هاي نصب شده در يك شبكه به همراه instance هاي آنها را بدست آورد.

using System.Collections.Generic;
using System.Data;
using System.Data.Sql;

public class CListServers
{
public static List<string> GetSQLServersList()
{
List<string> result = new List<string>();

// Retrieve the enumerator instance and then the data.
var instance = SqlDataSourceEnumerator.Instance;
var table = instance.GetDataSources();

// Display the contents of the table.
foreach (DataRow row in table.Rows)
{
result.Add(string.Format("{0}\\{1}", row[0], row[1]));
}

return result;
}
}

راه ديگر:
كتابخانه COM ياد شده (SQL-DMO) در SQL server 2008 با كتابخانه SMO جايگزين شده است.
در اين حالت خواهيم داشت:

using System.Collections.Generic;
using System.Data;
using Microsoft.SqlServer.Management.Smo;

public class CListServers
{
public static List<string> GetSQLServersListSMO()
{
List<string> result = new List<string>();
DataTable dt = SmoApplication.EnumAvailableSqlServers(false);
if (dt.Rows.Count > 0)
{
foreach (DataRow dr in dt.Rows)
{
result.Add(dr["Name"].ToString());
}
}
return result;
}
}

تقريبا كليه اعمالي كه از طريق management studio قابل انجام هستند با كمك اين كتابخانه نيز از طريق برنامه نويسي مي‌توان به آن‌ها پرداخت. براي مثال تهيه اسكريپت كليه جداول ، تريگرها و غيره.

۱۳۸۷/۰۸/۲۷

استخراج آدرس‌هاي ايميل از يك متن


در قسمت اول بررسي نحوه برنامه نويسي افزونه outlook ، در مورد استفاده از regular expressions اندكي توضيح داده شد. امروز مثالي ديگر از همين دست را بررسي خواهيم كرد.

چند روز قبل يك ايميل تبليغاتي به دست من رسيد كه فرد ارسال كننده انبوهي از ايميل‌ها را در قسمت To قرار داده بود (بجاي قسمت BCC (رونوشت مخفي)).
خوب، براي جدا كردن انبوهي از ايميل‌هاي مخلوط با ساير متون چه بايد كرد؟ چند ساعت وقت گذاشت و تك تك آنها را به صورت دستي جدا كرد؟ (براي ذخيره سازي در يك ديتابيس براي مثال :) )
يا براي مثال برنامه‌هاي download manager توانايي استخراج لينك‌هاي موجود در يك متن كپي شده در حافظه را دارند. آنها به چه صورتي عمل مي‌كنند؟ چگونه مي‌توانند لينك‌ها را با دقتي بالا و بسيار سريع از لابلاي متن موجود تشخيص دهند؟

بهينه‌ترين و سريعترين‌ راه براي اين نوع جستجوها استفاده از كتابخانه regular expressions (عبارات با قاعده) در دات نت فريم ورك است. اگر نياز به يك برگه تقلب (!) در اين زمينه داشتيد مي‌توانيد به اينجا مراجعه كنيد. همچنين در همان سايت، كاربران بسياري را خواهيد يافت كه الگوهاي ابداعي خود را با ديگران به اشتراگ مي‌گذارند.

براي مثال فرض كنيد فايلي را كه حاوي مخلوطي از متن و ايميل است را در يك رشته بارگذاري كرده‌ايد. نحوه استخراج ايميل‌هاي موجود با استفاده از اين امكانات به صورت زير خواهد بود:
using System.IO;
using System.Text.RegularExpressions;
using System.Text;

class CRegEx
{
/// <summary>
/// استخراج ايميل‌هاي يك فايل متني و ذخيره آن در فايلي جديد
/// </summary>
/// <param name="inFilePath">فايل ورودي</param>
/// <param name="outFilePath">فايل خروجي</param>
public static void ExtractEmails(string inFilePath, string outFilePath)
{
string data = File.ReadAllText(inFilePath); //خواندن فايل متني
//ايجاد شيء عبارت با قاعده بر اساس الگوي تشخيص ايميل‌ها
Regex emailRegex = new Regex(@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*",
RegexOptions.IgnoreCase);
//پيدا كردن گروه تطابق يافته با الگوي ما
MatchCollection emailMatches = emailRegex.Matches(data);
//ايجاد شيء استرينگ بيلدر براي ذخيره سازي سريع اطلاعات دريافتي
StringBuilder sb = new StringBuilder();
//ذخيره ايميل‌هاي استخراج شده
foreach (Match emailMatch in emailMatches)
{
sb.AppendLine(emailMatch.Value);
}
//ذخيره كردن اطلاعات استخراج شده در فايلي جديد
File.WriteAllText(outFilePath, sb.ToString());
}
}

راستي، اگر روزي خواستيد تعداد بالايي ايميل ارسال كنيد، آنها را به قسمت bcc اضافه كنيد (Message.Bcc.Add)، در قالب يك ايميل، نه چند هزار ايميل متوالي (در طي يك حلقه براي مثال). به اين صورت (استفاده از قسمت BCC) ميل سرور تمام آدرس‌ها را در صف قرار خواهد داد و متحمل بار اضافي شديد نخواهد شد. در اين حالت اگر ميل باكس خود را چك كنيد شايد بلافاصله ايميل مورد نظر را دريافت نكنيد. نگران نباشيد، انجام عمليات در صف قرار گرفته و در طي دقايق و يا حتي ساعات بعدي پردازش خواهد شد (بسته به بار سرور).
چند نكته را بايد در اينجا در نظر داشت. حتما آدرس‌هاي اضافه شده را با استفاده از عبارات باقاعده يكبار پيش از اضافه شدن بررسي نمائيد (Regex.IsMatch). در صورتيكه يكي از ايميل‌ها فرمت غيراستانداردي داشته باشد كل كار برگشت خواهد خورد.
و همچنين بايد دقت داشت كه براي اين موضوع حد نصاب وجود دارد. بر روي يكي از ميل سرورهاي يك هاست ايراني تست كردم، حداكثر 100 رونوشت مخفي را بيشتر قبول نمي‌كرد. بنابراين هر بار مي‌شود 100 ايميل را به صورت يكجا ارسال كرد (كه باز هم از روش استفاده از حلقه‌اي كه 100 بار ايميل مي‌زند بسيار بهتر است و هاست دار به علت ايجاد بار اضافي شديد بر روي سرور با شما تماس نخواهد گرفت)

۱۳۸۷/۰۸/۲۶

استفاده از واژه‌نامه آنلاين babylon در فايرفاكس


مطلبي را امروز در حين جستجو در سايت اسكريپت‌هاي گريس مانكي ديدم كه محض اطلاعات عمومي بد نيست :)

ابتدا گريس مانكي را نصب كنيد :)
https://addons.mozilla.org/en-US/firefox/addon/748
يكبار فايرفاكس را ببنديد و باز كنيد.
اكنون به آدرس زير رفته و بر روي دكمه install this script در بالاي صفحه كليك كنيد:
http://userscripts.org/scripts/show/9671
بعد از نصب آن، به آدرس زير مراجعه كنيد و همين عمليات را تكرار كنيد يعني install this script
http://userscripts.org/scripts/show/36230
خوب، الان دكمه F4 را فشار دهيد. يك صفحه مشكي در پائين صفحه باز خواهد شد. (با فشردن مجدد F4 حذف خواهد شد)
تذكر: اگر با فشردن دكمه F4 تغييري را مشاهده نكرديد، يكبار فايرفاكس را ببنديد و باز كنيد تا اسكريپت‌ها كاملا بارگذاري شوند.
بر روي babylon.persian كليك كنيد تا زرد شود (فعال شود)
اكنون بر روي هر كلمه‌اي در صفحه دوبار كليك كنيد تا انتخاب شود، بلافاصله معناي فارسي آنرا در پائين صفحه خواهيد ديد.

شايان ذكر است كه نيازي به نصب babylon نيست و مستقل عمل مي‌كند.

نكته: براي زياد كردن ارتفاع آن بر روي فلش به سمت بالا كليك كنيد، ماوس را نگه داشته و به سمت بالا حركت دهيد (يا برعكس به سمت پائين)




اسكريپت‌هاي جالبي را با گريس مانكي بر روي فايرفاكس اجرا مي‌كنند. (اگر لينك‌هاي سايت رو پيگيري كرده باشيد تشخيص سالم بودن لينك‌هاي رپيد شير در يك صفحه واقعا كارآمد بود و نمونه‌هاي بي‌شماري كه در سايت اسكريپت‌هاي آن مي‌توانيد پيدا كنيد يا از آنها ايده بگيريد)

بررسي جزئيات برنامه نويسي افزونه تاريخ فارسي براي outlook 2007 - قسمت اول


قبل از شروع بحث، سورس كامل پروژه را از اينجا دريافت كنيد (يك پروژه VSTO از نوع outlook add-in در VS.Net 2008 SP1).

توضيحات مربوطه را به دو قسمت تقسيم كرده‌ام. قسمت اول يافتن تاريخ‌هاي sent و فارسي كردن آنها و قسمت بعدي نحوه اضافه كردن يك ستون و مقدار دهي آن (در روزي ديگر).

متن ايميل‌هاي دريافتي در آوت‌لوك‌هاي جديد عموما به دو فرمت HTML و يا RichText دريافت مي‌شوند. حالت‌هاي ديگري هم مانند plain و unspecified هم موجود هستند كه حتي اگر ايميلي را به صورت plain ارسال نمائيد، با فرمت RichText نمايش داده خواهد شد (بنابراين بر اساس آزمايشات من بررسي اين دو فرمت كفايت مي‌كند).

براي اينكه قسمت‌هاي sent را پيدا كنيم در ابتدا بايد سورس صفحه را بررسي نمائيم (كليك راست و view source).
در حالت فرمت HTML داريم:

<p class=MsoNormal><b><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif"'>From:</span></b><span
style='font-size:10.0pt;font-family:"Tahoma","sans-serif"'> Nasiri, Vahid <br>
<b>Sent:</b> <span lang=AR-SA dir=RTL>our date goes here</span><br>
<b>To:</b> xyz<br>
<b>Subject:</b> our subject<o:p></o:p></span></p>


و در حالت ايميل‌هاي RichText خواهيم داشت:

From: tst@tst.net<br>
Sent:<span lang=AR-SA dir=RTL>our date goes here</span><br>
To: Nasiri, Vahid<br>
Subject: <span lang=AR-SA dir=RTL>xyz</span><br>

خوب، براي پيدا كردن عبارت تاريخ قسمت sent چه بايد كرد؟ (our date goes here در اينجا)
استفاده از روش‌هاي متداول كار با رشته‌ها در اينجا به علت انبوهي از تگ‌هاي HTML اصلا مقرون به صرفه نيست و كند خواهد بود. خوشبختانه با وجود كتابخانه regular expressions در دات نت، پيدا كردن عباراتي كه از يك الگوي خاص پيروي مي‌كنند به سادگي و با سرعت بسيار بالايي قابل انجام است.
پيشنهاد من براي دو فرمت بالا به صورت زير بوده: (شايد شما الگوي ديگري را يافتيد، زيبايي اوپن سورس :))

private const string REGEXHTMLPATTERN = @"(?s)>\s(.+?)<br>";
private const string REGEXPLAINTEXTPATTERN = "(?s)Sent:(.+?)<br>";

براي مثال در حالت دوم هر چيزي كه بين sent و br قرار مي‌گيرد در كل متن بررسي خواهد شد (با استفاده از MatchCollection فضاي نام System.Text.RegularExpressions). در اينجا اگر Convert.ToDateTime آن عبارت موفق بود يعني تاريخ قابل تبديل است (البته قبل از تبديل تمام تگ‌هاي HTML احتمالي هم تميز خواهند شد) و ما آنرا با استفاده از تابع DateTimeToFarsiStr در كلاس cDate به نمونه شمسي تبديل كرده (لطفا به سورس برنامه مراجعه كنيد) و نهايتا آنرا در متن جايگزين مي‌كنيم.
سرعت استفاده از RegularExpressions فوق العاده بالا است و براي نمونه در ايميلي با بيش از 20 ريپلاي در كسري از ثانيه كل اين عمليات انجام خواهد شد.

تا اينجا بررسي كلي الگوريتم مورد استفاده قسمت اول به پايان مي‌رسد.

بيشترين وقتي كه در اين پروژه صرف شد نحوه پيدا كردن شيء MailItem جاري باز شده با استفاده از رخدادهاي آوت‌لوك بود (مدت مديدي را براي اين مورد وقت گذاشتم! چون عملا در هيچ كتابي به اين مباحث پرداخته نمي‌شود و بايد كل نت را زير و رو كرد). دو مورد را بايد بررسي كرد. الف) inspector ها (صفحه‌اي كه جهت ايجاد يك ايميل جديد يا ريپلاي به ايميل جاري باز مي‌شود، inspector نام دارد) ب) ActiveExplorer ها (صفحه‌اي كه ليست ايميل‌ها را نمايش مي‌دهد و اين صفحه مي‌تواند در فولدرهاي مختلفي كه شما ايجاد كرده‌ايد نيز نمايش داده شود بنابراين بررسي inbox به تنهايي كافي نيست)
نحوه ايجاد اشياء مربوطه و تحت نظر قرار دادن آنها را در روال ThisAddIn_Startup فايل ThisAddIn.cs مي‌توانيد مشاهده نمائيد. نكته مهمي كه اينجا وجود دارد، تعريف اين اشياء در سطح كلاس است. در غيراينصورت با اولين خانه تكاني garbage collector ، اشياء شما (بدليل نبود ارجاعي فعال به آنها) معدوم خواهند شد(!) و ديگر روال‌هاي رخداد گردان تعريف شده كار نخواهند.

۱۳۸۷/۰۸/۲۵

افزونه تاريخ فارسي براي outlook 2007


عموما در محيط كاري اگر شبكه ويندوزي و mail server مورد استفاده هم ms exchange باشد، به طور قطع از outlook براي انجام امور روزمره ارسال و دريافت ايميل استفاده مي‌شود.
طبق معمول هم مشكل ما تاريخ فارسي است! يكي از شركت‌هاي ايراني كه در اين‌باره محصولي را ارائه داده با hook كردن تاريخ ويندوز، هر جايي كه تاريخي قرار است نمايش داده شود، آنرا فارسي مي‌كند. اين محصول دو ايراد دارد: الف) رايگان نيست! ب) اين hook بر روي عملكرد ساير برنامه‌ها تاثيرگذار است. براي مثال برنامه‌هاي دات نت تاريخ قمري را نمايش خواهند داد، بر روي عملكرد و كارآيي كلي سيستم تاثير منفي دارد و مشكلاتي از اين دست.

افزونه زير بدون دستكاري تاريخ ويندوز، دو كار را در MS outlook 2007 انجام خواهد داد:

الف) اضافه كردن ستون "تاريخ دريافت" شمسي



ب) در متن دريافتي، تمام تاريخ‌هاي sent موجود را يافته و شمسي مي‌كند



دريافت افزونه:
لطفا اينجا كليك كنيد.

تذكر مهم:
نصب دو بسته به روز رساني سيستم و دات نت فريم ورك را پيش از نصب اين افزونه فراموش نكنيد.
http://vahidnasiri.blogspot.com/2008/11/word-2007.html


در كل براي من كار راه انداز بوده :)

در طي روزهاي آتي، سورس كامل و نحوه برنامه نويسي آن‌را بررسي خواهيم كرد.

۱۳۸۷/۰۸/۲۴

Contact me


لطفا نظرات، پيشنهادات و انتقادات (غير عمومي) خود را از طريق فرم زير ارسال نمائيد.





اين مطالب در سايت منتشر نخواهند شد.

بلاگ‌ها و مطالب مطالعه شده در هفته قبل (هفته سوم آبان)


وبلاگ‌ها و سايت‌هاي ايراني


ASP. Net


طراحي وب


به روز رساني‌ها


ابزارها


سي‌شارپ


عمومي دات نت


دلفي



ويندوز


متفرقه

  • كدام سايت‌ها مطالب شما را كپي كرده‌اند؟! (البته شبيه به اين كار را با Google alerts هم مي‌شود انجام داد. فقط كافي است آدرس سايت خودتان را در گوگل alert اضافه كنيد. هر جايي لينكي به شما داده شود يا امثال آن، يك ايميل آني يا روزانه بسته به تنظيمات براي شما ارسال خواهد كرد.)


۱۳۸۷/۰۸/۲۳

مرور چند تجربه كوتاه با Microsoft virtual pc


از virtual pc كه يك ويندوز اكس پي بر روي آن نصب كرده‌ام براي اتصال به VPN‌ استفاده مي‌كنم. (متاسفانه آخرين نگارش vmware براي اين‌كار جواب نداد)
زمان اتصال به VPN كل سيستم وارد شبكه مورد نظر خواهد شد و اين مورد شايد به‌دلايلي براي مثال قطع اينترنت و يا اعمال پاليسي‌هاي شبكه بر روي كامپيوتر كاري جالب نباشد (استفاده از VPN براي اتصال به يك شبكه دومين ويندوزي). اما با نصب ماشين مجازي و اجراي يك سيستم عامل ديگر به موازات سيستم عامل اصلي، كار اتصال به VPN از داخل ماشين مجازي صورت خواهد گرفت و تمام اين اعمال هم از سيستم عامل مادر مجزا و ايزوله خواهند بود.
يك ويندوز اكس پي با اختصاص 200 مگ رم هم كار مي‌كند و عملا باري را بر روي سيستم عامل مادر تحميل نخواهد كرد. همچنين حتما از منوي action گزينه install or update virtual machine additions را انتخاب كنيد تا كارآيي سيستم عامل مجازي را بهبود بخشيد. حداقل فايده آن اين است كه اشاره‌گر ماوس را به سادگي مي‌توان از ماشين مجازي خارج كرد و هر بار نيازي به فشردن دكمه‌ ALT سمت راست نخواهد بود!
اولين مشكلي كه هنگام كار با يك ماشين مجازي خود نمايي مي‌كند بحث انتقال فايل بين سيستم عامل مادر (ويندوزي كه شما ماشين مجازي را روي آن نصب كرده‌ايد) و ماشين مجازي است. عمومي‌ترين راه، ايجاد يك فايل iso از فايل‌هاي مورد نظر است و سپس انتخاب منوي CD و گزينه capture iso image . اين روش بر روي vmware هم جواب مي‌دهد (معرفي فايل iso بعنوان CD-ROM آن). خوشبختانه عمل drag & drop (از سيستم عامل مادر به ماشين مجازي) كه شايد در وحله اول به ذهن نرسد اينجا بخوبي كار خواهد كرد و مشكل ساخت فايل‌هاي iso را برطرف مي‌كند. (البته vmware كمي پيشرفته‌تر است و حتي copy و Paste را نيز پشتيباني مي‌كند. اما خوب، رايگان نيست!)
مشكل بعدي با ms virtual pc افزايش تدريجي حجم آن است. روز اول 2 گيگ، روز سوم 4 گيگ، هفته بعد مي‌شود 6 گيگ! براي فشرده سازي آن مي‌توان به روش زير عمل كرد:
به مسير زير مراجعه كنيد: (اگر پيش‌فرض‌هاي نصب را پذيرفته‌ايد)
C:\Program Files\Microsoft Virtual PC\Virtual Machine Additions
فايل Virtual Disk Precompactor.iso را از طريق منوي CD و گزينه capture iso image باز كنيد. برنامه‌اي به صورت خودكار اجرا خواهد شد كه سيستم عامل مجازي را آماده فشرده سازي مي‌كند. پس از پايان كار، سيستم عامل مجازي را خاموش كنيد. سپس به منوي file گزينه virtual disk wizard مراجعه نمائيد. در صفحه بعدي گزينه ويرايش يك ماشين مجازي موجود را انتخاب كرده و فايل ماشين مجازي مورد نظر را كه پيشتر براي فشرده سازي آماده كرديم به آن معرفي كنيد. در صفحه بعد گزينه compact it را انتخاب كرده و در ادامه مي‌توانيد مسير جديدي را مشخص كنيد يا انتخاب كنيد كه فايل نهايي فشرده شده جايگزين فايل موجود شود.
با اينكار يك ماشين مجازي 6 گيگابايتي به 3 گيگ كاهش حجم يافت كه قابل توجه است.
براي استفاده از اينترنت سيستم عامل مادر در ms virtual pc مي‌شود از منوي edit ، گزينه setting و انتخاب networking در صفحه ظاهر شده، تنظيم اولين adapter شبكه را بر روي shared networking NAT قرار داد و همه چيز به خوبي كار خواهد كرد. (البته براي استفاده از اينترنت در vmware بايد روي كانكشن اينترنت خود در سيستم عامل مادر كليك راست كرد و سپس انتخاب گزينه advanced و فعال سازي internet connection sharing بر روي كارت شبكه مجازي نصب شده آن ضروري خواهد بود)



۱۳۸۷/۰۸/۲۲

تشخيص كمبود ايندكس‌ها در SQL server


در مطالب قبلي به اختصار در مورد dynamic management views كه از SQL server 2005 به بعد ارائه شده‌اند مثال‌هايي كاربردي ارائه گشتند. يكي ديگر از قابليت‌هاي فوق العاده مهم اين DMV ها، پيشنهاد ايجاد ايندكس بر روي جداول است. اين پيشنهادات بر اساس آمارهاي جمع آوري شده توسط موتور بهينه ساز اجراي كوئري‌ها در اس كيوال سرور به شما ارائه خواهند شد. براي مثال كوئري زير را در management studio اجر نمائيد:
USE master; 
SELECT d.database_id,
d.object_id,
d.index_handle,
d.equality_columns,
d.inequality_columns,
d.included_columns,
d.statement AS fully_qualified_object,
gs.*
FROM sys.dm_db_missing_index_groups g
JOIN sys.dm_db_missing_index_group_stats gs
ON gs.group_handle = g.index_group_handle
JOIN sys.dm_db_missing_index_details d
ON g.index_handle = d.index_handle



خروجي حاصل ليستي است كه بر اساس تفاسير موتور بهينه ساز اجراي كوئري‌ها بدست آمده است. equality_columns بر اساس حالت‌هايي مانند table.column = constant_value پيش بيني شده‌است. inequality_columns بر اساس حالت‌هايي مانند table.column > constant_value و included_columns براي حالت‌هايي است كه مي‌خواهيم ايندكس ايجاد شده محدوديت اندازه 900 بايت را نداشته باشد، يا نوع داده‌اي مورد استفاده براي مثال nvrachar max و امثال آن باشد (text و ntext مجاز نيست) و مواردي از اين دست.
fully_qualified_object هم مشخص مي‌كند كه اين ايندكس دقيقا بايد بر روي چه ديتابيس و جدولي ايجاد شود.

تذكر: اين آمارهاي جمع‌آوري شده پس از هر بار ري‌استارت سرور، صفر خواهند شد.

اكنون اين سؤال مطرح مي‌شود كه چگونه از اين اطلاعات استفاده كنيم؟
دقيقا بر اساس EQUALITY_COLUMNS ، INEQUALITY_COLUMNS و INCLUDED_COLUMNS گزارش فوق، مي‌توان به صورت زير عمل كرد:
CREATE NONCLUSTERED INDEX <unique index name>
ON <FULL_TABLE_NAME> (<EQUALITY_COLUMNS>,<INEQUALITY_COLUMNS>) -- exclude INEQUALITY_COLUMNS if NULL
INCLUDE (<INCLUDED_COLUMNS>); -- exclude INCLUDED_COLUMNS if NULL

خوب، پس از گزارشگيري، ممكن است ليست بلند بالايي تهيه شود. كوئري زير عبارات create index مورد نظر را بر اساس اين قابليت جديد توليد خواهد كرد:
SELECT mig.index_group_handle,
mid.index_handle,
migs.avg_total_user_cost AS AvgTotalUserCostThatCouldbeReduced,
migs.avg_user_impact AS AvgPercentageBenefit,
'CREATE INDEX missing_index_' + CONVERT (varchar, mig.index_group_handle)
+ '_' + CONVERT (varchar, mid.index_handle)

+ ' ON ' + mid.statement

+ ' (' + ISNULL (mid.equality_columns,'')

+ CASE
WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns
IS NOT NULL THEN ','
ELSE ''
END

+ ISNULL (mid.inequality_columns, '')

+ ')'

+ ISNULL (' INCLUDE (' + mid.included_columns + ')', '') AS
create_index_statement

FROM sys.dm_db_missing_index_groups mig

INNER JOIN sys.dm_db_missing_index_group_stats migs ON migs.group_handle = mig.index_group_handle

INNER JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle




مزاياي ايجاد ايندكس‌هاي صحيح بر اساس نيازهاي واقعي كاري:
  • سريعتر شدن اجراي كوئري‌هاي جستجو در تعداد ركوردهاي بالا
  • مرتب سازي سريعتر نتايج (sorting)
  • كوئري‌هايي كه بر اساس عبارت GROUP BY ايجاد شده‌اند، سريعتر اجرا خواهند شد


۱۳۸۷/۰۸/۲۱

خواندن فيد گزارش آب و هواي ياهو با استفاده از روش Xml serialization


در مطلب قبلي (در مورد كتابخانه anti-xss مايكروسافت) از روش xml serialization براي خواندن فايل xml حملات استفاده كرديم.
ايجاد اين كلاس و نگاشت اشياء با توجه به ساختار ساده آن به صورت دستي و به‌سادگي انجام شد. اكنون به مثال زير دقت بفرمائيد:
سرويس آب و هواي ياهو براي شهرهاي مختلف ايران از طريق لينك زير قابل استفاده است:
http://weather.yahoo.com/regional/IRXX.html
اگر به صفحات شهرهاي مختلف مراجعه نمائيد، يك فيد rss هم مشاهده خواهيد كرد، براي مثال در مورد تهران داريم:
http://weather.yahooapis.com/forecastrss?p=IRXX0018&u=c
ساختار اين فايل xml تا حدودي با يك rss استاندارد تطابق دارد. اما اگر به سورس xml آن دقت كنيم تگ‌هاي ديگري را نيز مشاهده خواهيم كرد كه براي مثال دما ، تاريخ و شرايط جوي را به صورت دقيقي و با استفاده از اصول xml ارائه مي‌دهند.

<yweather:condition text="Partly Cloudy" code="29" temp="10" date="Tue, 11 Nov 2008 5:30 pm IRT" />

خوب، براي دريافت اين اطلاعات چه بايد كرد؟ يكي از روش‌هاي متداول براي كار با اين نوع داده‌ها، استفاده از كلاس DataSet در دات نت و فراخواني متد ReadXml آن است (يك آدرس اينترنتي را هم مي‌تواند دريافت كند). سپس مطابق روش‌هاي معمول ADO.Net مي‌توان به تگ‌ها ومقادير آنها دسترسي داشت.
روش‌ بالا هر چند مشكلي ندارد اما به زيبايي كار با خواص يك كلاس متناظر با آن فايل xml نيست. اما در اينجا براي استفاده از روش xml serialization يك مشكل وجود دارد! ايجاد دستي اين كلاس كه بيانگر عملكرد آن فايل xml است كار ساده‌اي نيست.
خوشبختانه به همراه SDK‌ دات نت فريم ورك 2، برنامه‌اي به نام xsd.exe نيز همراه است كه كار ايجاد يك كلاس cs يا vb را از يك فايل xml جهت اين منظور انجام مي‌دهد (اين برنامه براي مثال در مسير C:\Program Files\Microsoft.NET\SDK\v2.0\Bin قرار دارد).

براي ايجاد فايل كلاس به صورت خودكار از روي يك فايل xml موجود بايد به ترتيب زير عمل كرد:
الف) ايجاد فايل xsd متناظر (XML Schema Definition)
براي اينكار در خط فرمان تايپ كنيد:
xsd.exe file.xml

نكته 1:
روش ديگر انجام اين كار : فايل xml را در VS.net باز كنيد، از منوي بالاي صفحه گزينه xml را انتخاب نموده و بر روي دكمه Create Schema كليك كنيد.

ب) ايجاد فايل cs يا vb از روي فايل(هاي) xsd ايجاد شده
در اينجا براي فيد آب و هواي ياهو سه فايل xsd توليد خواهد شد. براي تبديل آنها به كلاس cs بايد دستور زير را در خط فرمان اجرا كرد:

Xsd.exe file_1.xsd file_2.xsd file_3.xsd /c

اين مورد نكته مهمي است و تنها اگر يكي از فايل‌ها اينجا ذكر شوند، كلاس ناقصي تشكيل خواهد شد. (براي نمونه فايل xssAttacks.xml مطلب قبلي، ساختار ساده‌اي داشته و تنها به يك فايل xsd ختم خواهد شد)

نكته 2:
براي انتخاب زبان VB (با توجه به اين‌كه پيش فرض آن CS است) مي‌توان به صورت زير عمل كرد:
xsd.exe file.xsd /c  /l:vb

نكته 3:
براي توليد فايل xsd ، از برنامه Infer.exe نيز مي‌توان استفاده كرد (خروجي نهايي دقيق‌تري را ارائه مي‌دهد). اين برنامه را از اينجا دريافت كنيد.

تصاوير زير مقايسه دو فايل كلاس نهايي توليد شده از xsd هاي اين دو برنامه است:






پس از طي اين مراحل فايل كلاس ما براي xml serialization آماده خواهد شد. مرحله بعد دريافت اطلاعات و نگاشت آن به اين كلاس توليد شده است:

public static rss DeserializeFromXML()
{
XmlSerializer deserializer =
new XmlSerializer(typeof(rss));
using (XmlReader reader = XmlReader.Create("http://weather.yahooapis.com/forecastrss?p=IRXX0018&u=c"))
{
return (rss)deserializer.Deserialize(reader);
}
}

كلاس rss از فيد xml و فايل‌هاي xsd آن كه توليد كرديم به صورت خودكار ايجاد شده است.
اكنون براي مثال خواندن وضعيت فعلي جوي از فيد دريافتي به سادگي زير است:

rss data = DeserializeFromXML();
MessageBox.Show(data.channel.item.condition.text);


۱۳۸۷/۰۸/۲۰

بررسي Microsoft Anti-Cross Site Scripting Library


هنگام نمايش اطلاعات در وب بايد اطلاعات خام دريافتي از كاربر را encode كرده و سپس نمايش داد تا از حملات XSS يا cross site scripting attacks در امان ماند. مثلا وبلاگي را طراحي كرده‌ايد و يك نفر اطلاعات زير را بجاي توضيحات ارسال كرده است:
<SCRIPT>alert('XSS')</SCRIPT>

اگر اطلاعات به همين شكل دريافت و بدون تغيير هم نمايش داده شود، يك ضعف امنيتي براي سايت شما به‌حساب خواهد آمد. (بحث دزديدن اطلاعات كوكي و امثال آن از اين طريق با معرفي HttpOnly cookies در IE‌هاي جديد و فايرفاكس 3 به بعد تقريبا منتفي شده است اما مي‌توانند با ارسال انبوهي اسكريپت، مشاهده صفحه را با crash‌ كردن مرورگر كاربران همراه كنند)
مايكروسافت براي اين منظور Microsoft Anti-Cross Site Scripting Library را ارائه داده است. نمونه بهبود يافته HttpUtility.HtmlEncode كه در فضاي نام System.Web موجود است.

در اينجا قصد داريم اين كتابخانه را با ليست زير آزمايش كنيم:
http://ha.ckers.org/xss.html
در همان صفحه اگر دقت كنيد، ليست حملات را به صورت يك فايل xml هم ارائه داده است:
http://ha.ckers.org/xssAttacks.xml
براي خواندن اين فايل xml در دات نت روش‌هاي زيادي وجود دارد منجمله XML serialization .

ساختار اين فايل به شكل زير است:
<?xml version="1.0" encoding="UTF-8"?>
<xss>
<attack>
<name>x1</name>
<code>x2</code>
<desc>x3</desc>
<label>x4</label>
<browser>x5</browser>
</attack>
.
.
.

بنابراين شيء‌ نمايانگر آن مي‌تواند به صورت ليستي از كلاس زير باشد:
    public class attack{
public string name { get; set; }
public string code { get; set; }
public string desc { get; set; }
public string label { get; set; }
public string browser { get; set; }
}

براي دريافت اين ليست و بارگذاري فايل xml مربوطه با استفاده از روش XML serialization خواهيم داشت:
      
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

public static List<attack> DeserializeFromXML(string path)
{
XmlRootAttribute root = new XmlRootAttribute("xss");
XmlSerializer deserializer =
new XmlSerializer(typeof (List<attack>),root);
using (TextReader textReader = new StreamReader(path))
{
return (List<attack>)deserializer.Deserialize(textReader);
}
}

در ادامه فرض بر اين است كه ارجاعي از اسمبلي AntiXssLibrary.dll به پروژه اضافه شده است، همچنين فايل xssAttacks.xml فوق نيز در كنار فايل اجرايي برنامه ، مثلا يك برنامه كنسول قرار گرفته است:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.Security.Application;

private static void testMethod()
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("<html>{0}", Environment.NewLine);
sb.AppendFormat("<body>{0}", Environment.NewLine);

List<attack> data = XMLParser.DeserializeFromXML("xssAttacks.xml");
foreach (attack atk in data)
{
string cleanSafeHtmlInput = AntiXss.HtmlEncode(atk.code);
sb.AppendFormat("{0}<br>{1}", cleanSafeHtmlInput, Environment.NewLine);
}

sb.AppendFormat("</body>{0}", Environment.NewLine);
sb.AppendFormat("</html>");

File.WriteAllText("out.htm", sb.ToString());
}

پس از اجراي تابع فوق، خروجي ما يك فايل html خواهد بود به نام out.htm . آنرا در مرورگر خود باز كنيد. بدون هيچ مشكلي باز خواهد شد و خروجي امني را مشاهده خواهيد كرد. براي مشاهده اثر واقعي اين كتابخانه، قسمت AntiXss.HtmlEncode را از كد فوق حذف كنيد و يكبار ديگر برنامه را اجرا كنيد. اكنون فايل نهايي را در مرورگر باز كنيد. با انبوهي از alert هاي جاوا اسكريپتي مواجه خواهيد شد كه اهميت كتابخانه فوق را جهت ارائه خروجي امن در صفحات وب مشخص مي‌سازد.

۱۳۸۷/۰۸/۱۹

نصب يك اسمبلي دات نت در GAC


افزونه فارسي به پارسي را قبل از ارائه در سايت، بر روي يك ماشين مجازي هم تست كردم. براي اين منظور از Microsoft virtual pc استفاده شد. البته در مقابل امكانات VMware شايد حرفي براي گفتن نداشته باشد ولي خوب جهت مقاصد تست نرم افزار بر روي يك سيستم عاري از وسايل برنامه نويسي مناسب است. (براي نصب يك سيستم عامل توسط آن براي مثال مي‌شود از سي دي آن OS يك فايل ISO تهيه كرد و مسير اين فايل ISO را به ماشين مجازي معرفي كرد . سپس سيستم بوت شده و روال نصب مطابق معمول خواهد بود)
اولين مشكلي كه پس از تست بر روي سيستم مجازي رخ داد، پيغام يافت نشدن اسمبلي مربوط به SQLite بود. نرم افزار word هنگام اجراي افزونه‌هاي دات نت، آنها را در مسيري با يك نامگذاري منحصربفرد كپي مي‌كند و تنها هم همان اسمبلي افزونه را كپي مي‌كند و نه ساير موارد همراه را. براي پيدا كردن اين مسير مي‌شود از روش زير استفاده كرد:
using System.Reflection;
Assembly.GetExecutingAssembly().Location

در اين مسير اسمبلي SQLite وجود ندارد و به همين دليل هم بارگذاري نخواهد شد. بهترين راه حل براي رفع اين مشكل، نصب اسمبلي مربوطه در GAC يا global assembly cache است.
براي نصب اسمبلي در GAC استفاده از برنامه gacutil توصيه شده است. اين برنامه به همراه SDK دات نت فريم ورك ارائه مي‌شود و الزامي ندارد كه كاربر نهايي اين برنامه را داشته باشد. خوشبختانه با استفاده از برنامه نويسي هم مي‌شود يك نمونه از برنامه Gacutil را خودمان ايجاد كنيم (براي مثال ايجاد يك برنامه كنسول و دريافت مسير از طريق آرگومان‌هاي ارسالي به آن):

new System.EnterpriseServices.Internal.Publish().GacInstall(path);

در اينجا بايد ارجاعي از System.EnterpriseServices نيز به برنامه اضافه شود.
اين روش در مورد اسمبلي SQLite كه داراي امضاي ديجيتال است كار خواهد كرد. اما اگر قصد داشته باشيد به صورت عمومي از آن استفاده كنيد، بايد ابتدا بررسي كرد كه آيا فايل اسمبلي داراي امضاي ديجيتال است يا خير. براي اين منظور مي‌توان مقدار عبارت زير را ارزيابي كرد:
Assembly.LoadFile(path).GetName().GetPublicKey().Length

اگر اين طول بزرگتر از صفر بود به اين معنا است كه فايل اسمبلي داراي امضاي ديجيتال است و مي‌توان آنرا در GAC نصب كرد.
لازم به ذكر است كه متد معرفي شده براي نصب در GAC در صورت عدم موفقيت هيچ پيغام خطا يا exception ايي را در برنامه توليد نخواهد كرد. اما پيغام خطاي حاصل را در event log ويندوز مي‌توان مشاهده كرد.


۱۳۸۷/۰۸/۱۸

گزارشگيري از تاريخچه‌ي اجراي كوئري‌ها در SQL Server


چند روز قبل مشكلي رخ داده بود به اين شرح!
سروري كهSQL server بر روي آن نصب بود بخاطر SQL server ، بيش از 50 درصد CPU usage مداوم پيدا كرده بود. عموما مصرف CPU اس كيوال سرور روي سرورهاي قوي بالا نيست و تداوم اين حالت به اين شدت يعني بروز مشكل.
اينجا است كه اين سؤال پيش مياد، SQL Server الان داره چكار ميكنه كه تا اين حد به صورت مداوم مصرف CPU آن بالا رفته؟ (حدودا 2 ساعت تمام به صورت مداوم مصرف CPU بالاي 50 درصد بود)
با استفاده از كوئري زير مي‌شود، عبارات SQL ايي را كه هم اكنون در حال اجرا هستند را به صورت زنده مشاهده كرد: (در اس كيوال سرور 2005 به بعد)
USE master;
SELECT st.text, r.session_id, r.status, r.command, r.cpu_time, r.total_elapsed_time
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS st

مشكل از بروز يك loop بود كه به اين صورت دقيقا كوئري مربوطه تشخيص داده شد و برطرف شد.

همچنين با استفاده از كوئري زير مي‌توان آخرين 50 كوئري اجرا شده در SQL server را به همراه زمان اجراي آنها گزارشگيري كرد:
SELECT TOP 50
deqs.last_execution_time AS [Time],
dest.text AS [Query]
FROM sys.dm_exec_query_stats AS deqs
CROSS apply
sys.dm_exec_sql_text(deqs.sql_handle) AS dest
ORDER BY
deqs.last_execution_time DESC

اگر علاقمند باشيد در مورد اين نوع كوئري‌ها اطلاعات بيشتري كسب كنيد، مطالعه مقاله زير توصيه مي‌شود:
http://www.sqlteam.com/article/dynamic-management-views

۱۳۸۷/۰۸/۱۷

جزئيات برنامه نويسي افزونه فارسي به پارسي


اين افزونه با استفاده از ابزار Visual Studio Tools for Office كه به VSTO مشهور شده است، تهيه شد. در بسته به روز رساني سيستم كه در ذيل (معرفي افزونه) نيز معرفي شد نگارش sp1 vsto3.0 آن به صورت خودكار نصب خواهد شد.
براي ايجاد اين پروژه در VS.Net 2008 ، تنها كافي است يك پروژه جديد Word add-in را آغاز نمائيم. (شكل زير)





قبل از ادامه بحث، بهتر است در مورد بانك اطلاعاتي مورد استفاده نيز توضيح داده شود. در اينجا از SQLite استفاده شد. (بسيار سبك، كم حجم و سريع است و اساسا يك كاربر نهايي براي تنظيمات آن نيازي نيست اطلاعاتي داشته باشد). بسته به روز رساني سيستم (در مطلب قبلي)، اين مورد را نيز به صورت خودكار نصب خواهد كرد (در GAC بايد نصب شود وگرنه افزونه قادر به يافتن آن نخواهد شد).
براي ايجاد اين بانك اطلاعاتي، از افزونه SQLite manager براي فايرفاكس استفاده شد. (اين افزونه رايگان شما را از هر ابزار جانبي براي مديريت يك بانك اطلاعاتي SQLite بي‌نياز مي‌كند)
براي مثال فايل ErrorsBank.sqlite برنامه افزونه فارسي به پارسي را توسط افزونه SQLite manager فايرفاكس باز كنيد (اين فايل را در محل نصب افزونه مي‌توانيد پيدا كنيد). در اينجا مي‌توان جداول جديد را ايجاد كرد، كوئري‌هاي دلخواه را اجرا نمود و يا اطلاعات را مرور كرده، حذف يا ويرايش كرد (شكل زير).




و خوشبختانه اين بانك اطلاعاتي و محصور كننده‌هاي آن با اطلاعات يونيكد فارسي هيچ مشكلي ندارند و براي كارهايي با وسعت كم و تعداد ركورد پائين يكي از بهترين انتخاب‌ها به‌شمار مي‌روند.
نحوه استفاده از SQLite نيز در دات نت بسيار ساده است. اگر با ADO.Net كار كرده باشيد، پس از افزودن ارجاعي از اسمبلي System.Data.SQLite.DLL به پروژه و معرفي فضاي نام آن به پروژه، تنها كافي است در كدهاي قبلي خود براي مثال SqlConnection را به SQLiteConnectionتغيير دهيد و امثال آن. يعني دانش ADO.Net شما در اينجا نيز كاملا قابل استفاده خواهد بود و نيازي نيست مدتي را صرف آشنا شدن با كلاس‌ها و مفاهيم جديد نمائيد (البته اين تنها زماني معنا خواهد داشت كه به ويزاردها عادت نكرده باشيد و كارهاي خود را با كد نويسي انجام داده باشيد).
تنها يك نكته را بايد به‌خاطر داشت و آن هم مربوط است به ساز و كار دروني SQLite . هنگام انجام عمليات update يا insert حتما از transaction استفاده كنيد تا سرعت كوئري‌هاي شما در SQLite به نحو شگفت انگيزي افزايش يابد. مثالي در اين مورد را در فايل chm راهنماي SQLite.NET مي‌توانيد پيدا كنيد.

مطلب ديگري كه پيش از پرداختن به كد نويسي افزونه بايد با آن آشنا شويم، مفهوم smart tags در مجموعه آفيس است كه در اين پروژه از آن استفاده گرديد.
smart tags در مجموعه آفيس برچسب‌هايي هستند كه به صورت خودكار توسط يكي از محصولات آفيس مثلا ورد يا اكسل و امثال آن، پس از تشخيص يك كلمه خاص ايجاد مي‌شوند و مي‌توان اعمالي را به اين برچسب ايجاد شده انتساب داد. براي مثال در اينجا امكان جايگزين كردن كلمه فارسي با معادل پارسي در نظر گرفته شد.
ويديويي در مورد نحوه ايجاد اسمارت تگ‌ها در VS.Net و يا مثالي پيشرفته‌تر در مورد تشخيص دماي فارنهايت در يك متن و ايجاد smart tag مخصوص به آن براي تبديل به سلسيوس. (از regular expressions جهت يافتن يك الگو در متن استفاده شده است)

در اين پروژه، حدود 3800 واژه فارسي به‌ يك smart tag انتساب داده مي‌شود (در روال استاندارد ThisAddIn_Startup). سپس در هنگام نمايش آن، معادل پارسي كلمه نيز به منوي باز شده افزوده گشته و در روال رخداد كليك آن، تعويض كلمه تشخيص داده شده با واژه پيدا شده صورت خواهد گرفت.

در ادامه فرض بر اين است كه يك پروژه جديد word add-in را در VS.Net ايجاد كرده‌ايد و همچنين ارجاعي را به فايل System.Data.SQLite.DLL افزوده‌ايد.

using System;
using System.Diagnostics;
using Microsoft.Office.Tools.Word;
using Action = Microsoft.Office.Tools.Word.Action;

private SmartTag _st;
private void init()
{
try
{
//Enable Smart Tags in Word
if (!Application.Options.LabelSmartTags)
{
//ممكن است اسمارت تگ‌ها در ورد غيرفعال باشند. به اين صورت مي‌شود آنها را فعال كرد
Application.Options.LabelSmartTags = true;
}

_st = new SmartTag(@"www.microsoft.com/Demo#FarsiSmartTag", @"فارسى به پارسى");

//دريافت واژه‌هاي فارسي از ديتابيس و افزودن خودكار آنها به اسمارت تگ‌ها
if (!DBhelper.AddSmartTagItems(_st, "select distinct farsi from tblFarsiToParsi")) return;

Action stActions = new Action("تبديل");//تعريف يك اكشن جديد
stActions.Click += stActions_Click;//انتساب روال‌هاي رخداد گردان
stActions.BeforeCaptionShow += stActions_BeforeCaptionShow;
_st.Actions = new[] { stActions };
VstoSmartTags.Add(_st);//افزودن اسمارت تگ به مجموعه
}
catch (Exception ex)
{
EventLog.WriteEntry("FarsiToParsi", ex.ToString(), EventLogEntryType.Error, 7);
}
}

private void ThisAddIn_Startup(object sender, EventArgs e)
{
init();
}

دو روال رخداد گردان زير نيز جهت تغيير عنوان پيش فرض به واژه يافته شده در لحظه نمايش منو و روال كليك نيز ايجاد خواهد شد:

static void stActions_BeforeCaptionShow(object sender, ActionEventArgs e)
{
try
{
Action clickedAction = sender as Action;
if (clickedAction != null)
{
string parsi = DBhelper.FindParsi(e.Text);//معادل پارسي از ديتابيس دريافت مي‌شود
clickedAction.Caption = (parsi == string.Empty ? e.Text : parsi);
}
}
catch (Exception ex)
{
EventLog.WriteEntry("FarsiToParsi", ex.ToString(), EventLogEntryType.Error, 7);
}
}

static void stActions_Click(object sender, ActionEventArgs e)
{
try
{
Action clickedAction = sender as Action;
if (clickedAction != null)
{
e.Range.Text = clickedAction.Caption;//جايگزيني متن موجود با عنواني كه پيشتر پارسي شده است
}
}
catch (Exception ex)
{
EventLog.WriteEntry("FarsiToParsi", ex.ToString(), EventLogEntryType.Error, 7);
}
}

نكته‌اي را كه در اينجا بايد حتما رعايت كرد بحث exception handling‌ است. خصوصا در روال استاندارد ThisAddIn_Startup . اگر در اين روال خطايي مديريت نشده رخ دهد، word افزودني شما را به صورت غيرفعال به مجموعه اضافه خواهد كرد و فعال سازي بعدي آن پس از اصلاح كد واقعا مشكل خواهد بود. همانطور كه ملاحظه مي‌كنيد تمامي خطاها در event log‌ ويندوز نوشته مي‌شوند.
همچنين بايد دقت داشت كه اگر متغيري در سطح كلاس تعريف نشود به احتمال زياد تا دقايقي بعد توسط garbage collector به ديار باقي خواهد شتافت (تعريف st_ در اينجا). اينجاست كه شايد ساعت‌ها وقت صرف كنيد كه چرا روال‌هاي رخ‌داد گردان ديگر اجرا نمي‌شوند. چرا افزونه ديگر كار نمي‌كند.

همين! كل سورس اين add-in منهاي بحث دريافت اطلاعات از ديتابيس همين بود! وظيفه‌ي تشخيص كلمات معرفي شده به ms-word به‌عهده‌ي خود آن است و اين‌كار را نيز به‌خوبي انجام مي‌دهد. در گذشته‌هاي نچندان دور ايجاد يك افزونه براي word واقعا مشكل بود كه با اين روش بسياري از موانع برطرف شده است.

كلاس DBHelper كه كار دريافت اطلاعات واژه‌ها را از ديتابيس SQLite انجام مي‌دهد به شرح زير است:

using System;
using System.Data.SQLite;
using System.Diagnostics;
using System.Reflection;
using Microsoft.Office.Tools.Word;

namespace Farsi2Parsi
{
class DBhelper
{
#region Methods (2)

// Public Methods (2)

public static bool AddSmartTagItems(SmartTag st, string strSQL)
{
SQLiteDataReader myReader = null;
SQLiteCommand sqlCmd = null;
bool ret = false;
try
{
SQLiteConnection sqlCon = new SQLiteConnection
{
ConnectionString = "Data Source=" + ConStr.ConnectionString
};
sqlCon.Open();
sqlCmd = new SQLiteCommand(strSQL, sqlCon);
myReader = sqlCmd.ExecuteReader();

if (myReader != null)
while (myReader.Read())
{
if (myReader.GetValue(0) != DBNull.Value)
st.Terms.Add(myReader.GetValue(0).ToString());
}

ret = true;
}
catch (Exception ex)
{
EventLog.WriteEntry("FarsiToParsi", ex + "\n" + Environment.CurrentDirectory + "\n" +
Assembly.GetExecutingAssembly().Location, EventLogEntryType.Error, 7);
}
finally
{
if (myReader != null)
myReader.Close();

if (sqlCmd != null)
sqlCmd.Connection.Close();
}
return ret;
}

public static string FindParsi(string farsi)
{
SQLiteDataReader myReader = null;
SQLiteCommand sqlCmd = null;
string ret = string.Empty;
string strSQL = "select parsi from tblFarsiToParsi where farsi='" + farsi.Replace("'", "''") + "'";
try
{
SQLiteConnection sqlCon = new SQLiteConnection
{
ConnectionString = "Data Source=" + ConStr.ConnectionString
};
sqlCon.Open();
sqlCmd = new SQLiteCommand(strSQL, sqlCon);
myReader = sqlCmd.ExecuteReader();

if (myReader != null)
{
myReader.Read(); //اولين مورد كافي است
if (myReader.GetValue(0) != DBNull.Value)
ret = myReader.GetValue(0).ToString();
}
}
catch (Exception ex)
{
EventLog.WriteEntry("FarsiToParsi", ex + "\n" + Environment.CurrentDirectory + "\n" +
Assembly.GetExecutingAssembly().Location, EventLogEntryType.Error, 8);
}
finally
{
if (myReader != null)
myReader.Close();

if (sqlCmd != null)
sqlCmd.Connection.Close();
}
return ret;
}
#endregion Methods
}
}

همانطور كه پيشتر نيز عنوان شد اگر با ADO.net آشنايي داشته باشيد، هيچ نكته‌ي خاص جديدي را در اينجا مشاهده نخواهيد كرد و تنها يك سري امور روزمره كاري با ADO.net مطرح شده است، باز كردن كانكشن، اجراي كوئري، دريافت اطلاعات و پاكسازي نهايي. (قسمت finally را با استفاده از عبارت using مي‌شود حذف كرد)

هنگام نصب برنامه، مسير پوشه نصب در رجيستري ويندوز توسط نصاب نوشته خواهد شد. از همين مورد براي ايجاد رشته اتصالي به ديتابيس استفاده گرديد.

class ConStr
{
public static string ConnectionString
{
get
{
return Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\FarsiToParsi").GetValue("folder") + "\\ErrorsBank.sqlite";
}
}
}

سورس كامل اين افزونه را به صورت يك پروژه VS.Net 2008 SP1 از اينجا مي‌توانيد دريافت كنيد.
نصاب برنامه با استفاده از NSIS ايجاد شده كه در روزي ديگر درباره‌ي آن توضيح خواهم داد.
اگر قصد داشته باشيد از روش‌هاي متداول استفاده كنيد، مشاهده ويديوي زير توصيه مي‌شود:
http://msdn.microsoft.com/en-us/office/bb851702.aspx

براي توزيع اين نوع افزونه‌ها علاوه بر دات نت فريم ورك، به چهار به روز رساني ديگر نيز نياز خواهد بود:
به روز رساني نصاب ويندوز (كه احتمالا نصب هست)
WindowsInstaller-KB893803-v2-x86.exe
Microsoft Office System Update: Redistributable Primary Interop Assemblies :
o2007pia.msi
نصب vsto و همچنين sp1 آن
vstor30.exe
vstor30sp1-KB949258-x86.exe

اين موارد را من در بسته به روز رساني سيستم قرار داده‌ام كه به صورت خودكار و يكي پس از ديگري اجرا و نصب خواهند شد.
پس از آن با كليك بر روي فايلي با پسوند vsto كه در پوشه build برنامه موجود است، مي‌توان افزونه را نصب كرد (click once installation).




ساير اطلاعات در مورد پروژه‌هاي VSTO را مي‌توان از طريق وبلاگ رسمي آنها دنبال كرد:
http://blogs.msdn.com/vsto/

ايده‌هاي ديگري را هم در همين رابطه مي‌توان پياده سازي كرد. براي مثال درست كردن يك افزونه براي بررسي آئين نگارش فارسي در متون word. دقيقا با همين روش قابل پياده سازي است و يا ايجاد غلط ياب بهتري نسبت به آن‌چه كه هم اكنون براي آفيس 2003 توسط مايكروسافت ارائه شده است (اين غلط ياب با صفحه كليد استاندارد تايپ ايران همخواني ندارد، به همين جهت با استقبال نيز مواجه نشد).


بلاگ‌ها و مطالب مطالعه شده در هفته قبل (هفته دوم آبان)


وبلاگ‌ها و سايت‌هاي ايراني


Visual Studio


امنيت


ASP. Net


طراحي وب


اس‌كيوال سرور


به روز رساني‌ها


ابزارها


سي‌شارپ

  • تازه‌هاي سي شارپ 4 ، واژه كليدي dynamic ، قسمت‌هاي يك و دو و سه

عمومي دات نت


CPP


دلفي


ويندوز


Office

  • آشنايي با يك سري از اصطلاحات outlook 2007 براي برنامه نويس‌ها. (اگر قصد داشته باشيد يك add-in را براي outlook 2007 با استفاده از امكانات VSTO توسعه دهيد، آشنايي با اين اصطلاحات بسيار ضروري خواهد بود)

متفرقه