post זה הוא השלישי והאחרון בסדרה אודות תמיכה ב WindowsService בסביבת פיתוח
קדמו לו:
הפעם נראה, כיצד נוכל שאותה אפליקציה, תוכל:
-
להתקין את ה Service ב Service Control Managment
-
להסיר את ההתקנה
-
להריץ את ה Service
-
לעצור אותו
-
ובהמשך, ניתן גם פתרון ל Service שרץ תחת סביבה אינטראקטיבית, ולא יכול להשתמש ביכולת שהצגנו ב post הראשון.
כדי לבצע את המשימה, נקלוט את שורת הפקודה, ובהתאם לקלט, נבצע את אחת מן הפקודות הנ"ל.
בנוסף, על האפליקציה לדעת מה שם ניתן לה בSCM (קרי: Service Control Managment) , ולשם כך נשתמש בקונפיגורציה.
אז איך מתקינים?
1: public static void Install(string serviceName)
2: {
3: IDictionary savedState = new Hashtable();
4:
5: using (var installer =
6: new AssemblyInstaller(typeof(TService).Assembly,
7: new string[] { "/logFile=WindowsServiceRunner-" + typeof(TService).Name + "-.log" }) { UseNewContext = true })
8: {
9: CatchExceptions(() =>
10: {
11: installer.Install(savedState);
12: installer.Commit(savedState);
13: });
14: }
15: Start(serviceName);
16: }
17:
המתודה Install, מתקינה את ה Service לתוך ה SCM, את ה log עם התוצאות היא מפיקה לתוך קובץ בספריית ההתקנה, כאשר בשם הקובץ, משובץ שם המחלקה.
בסופו של התהליך נקראת המתודה Start שמריצה את ה Service. (ניתן בהחלט, לנתק בין שתי הפעולות)
הפעולות שקיימת סבירות שיזרקו חריגה, עטופות במתודה CatchExceptions המוגדרת כך:
1: static void CatchExceptions(Action action)
2: {
3: try
4: {
5: action();
6: }
7: catch (Exception ex)
8: {
9: Console.WriteLine(ex.ToString());
10: Console.ReadLine();
11: }
12: }
13:
שאר הפעולות (הסרת ההתקנה, עצירה והרצה) הם פשוטות יותר, וניתן לעיין בהן בקוד המקור המצורף.
כדי לקרוא לפעולות אלו, נשתמש בארגומנטים המתקבלים בשורת הפקודה
(המתודה CompareInput מחלצת ובודקת האם הארגומנטים מתאימים לקלט. כמובן די באחת משתי האפשרויות המוצגות להלן)
1: public static void Run(string[] args, string serviceName)
2: {
3: if (Environment.UserInteractive)
4: {
5: if (args.Length > 0)
6: {
7: if (CompareInput(args[0], "i", "/i"))
8: {
9: ServiceControlManager<TService>.Install(serviceName);
10: }
11: else if (CompareInput(args[0], "u", "/u"))
12: {
13: ServiceControlManager<TService>.Uninstall();
14: }
15: else if (CompareInput(args[0], "r", "/r"))
16: {
17: ServiceControlManager<TService>.Start(serviceName);
18: }
19: else if (CompareInput(args[0], "s", "/s"))
20: {
21: ServiceControlManager<TService>.Stop(serviceName);
22: }
23: else if (CompareInput(args[0], "?", "/?"))
24: {
25: ShowHelp();
26: }
27: else
28: {
29: RunServiceInConsole(args);
30: }
31: }
32: else
33: {
34: RunServiceInConsole(args);
35: }
36: }
37: else
38: {
39: RunService();
40: }
41: }
כדי להתקין את האפליקציה מתוכנת התקנה, נוסיף Commit Action שיקבל כארגומנט i. (ניתן לראות דוגמא בקוד המקור).
וכך נראית ה Main בקוד הדוגמא שלנו:
1: static void Main(string [] args)
2: {
3: WindowsServiceRunner<MyService>.Run(args, Settings.Default.ServiceName);
4: }
אחרי שהצגנו את הפתרון הנ"ל, נוכל לתת פתרון גם ל Service שהינו אינטארקטיבי, ובמקרה זה Environment.UserInteractive מחזיר תמיד true.
לשם כך הוספנו בקוד המקור את המחלקה InteractiveWindowsServiceRunner המטפלת במקרה זה.
כאן הטיפול הוא ע"י העברת ארגומנט d בסביבת הפיתוח:
1: if (CompareInput(args[0], "d", "/d"))
2: {
3: ServiceControlManager<TService>.Install(serviceName);
4: }
להורדת הקוד לכל הסדרה
post זה הוא המשך של קודמו: תמיכה ב WindowsService בזמן פיתוח.
בזמן שננצל את היכולת שהוצגה בpost הקודם, והיא עבודה אינטראקטיבית מליאה בזמן פיתוח, נוכל להציג מידע שחשוב לנו, בחלון ה Console.
יתכן שמידע tracing זה, יהיה חשוב לנו גם בזמן שנריץ את הקוד כ WindowsService ולא רק בזמן ההרצה כ Console Application.
איך נדאג לכך?
ראשית, בזמן עבודה כ Service נצטרך להפרד מידידנו האהוב ה Console. אין שום דרך להציג אותו, הלא בחרנו ב service שאינו אינטראקטיבי.
במקום חלון ה console נוכל להסתפק בקובץ.
אם כך, במה זה שונה מ log? זה לא שונה, פרט להתנהגות אחת: זה אותו המידע שאנו מציגים בחלון ה Console בסביבת הפיתוח.
לשם כך, נשתמש ב Trace (מתוך System.Diagnostics) ולא ב Console.Write.
נדאג שהודעות ה Trace יוצגו הן בחלון ה Console והן בקובץ יעודי, בהתאם לסביבת הריצה.
בדוגמא הבאה, נציג את הקונפיגורציה של שני המצבים כאחד (אם כי בד"כ דרוש לנו רק אחת משתי האפשרויות):
1: <system.diagnostics>
2: <trace autoflush="true" indentsize="4">
3: <listeners>
4: <add name="fileListener" type="System.Diagnostics.TextWriterTraceListener"
5: initializeData="c:\logs\tracing.log" />
6: <add name="consoleListener" type="System.Diagnostics.ConsoleTraceListener"/>
7: </listeners>
8: </trace>
9: </system.diagnostics>
TextWriterTraceListener – כשמו, רושם לתוך הקובץ המוגדר ב initializeData
ConsoleTraceListener – רושם את ההודעות לחלון ה Console.
כדי לתת ממשק זהה ונוח כמו של Console.Write הוספתי class שמבצע זאת. בנוסף, הוא גם מוסיף חתימה של זמן לכל הודעת Trace.
קטע דוגמא:
1: public static class Tracing
2: {
3: public static void WriteLine(string format, params object[] args)
4: {
5: TraceTimestamp();
6: Trace.WriteLine(string.Format(format, args));
7: }
8:
9: //More methods here
10:
11: private static void TraceTimestamp()
12: {
13: Trace.Write(DateTime.Now.ToString("dd/MM/yyy hh:mm:ss.fff => "));
14: }
15: }
16:
להורדת הקוד לכל הסדרה
ה post הבא בנושא: Self Installing WindowsService -שרות שיודע להתקין את עצמו
בעבודה עם WindowsService קיימת אי נוחות בסביבת הפיתוח.
- אי אפשר סתם כך להריץ אותו ב VisualStudio (לא ניתן ללחוץ F5 ולהריץ).
- יש צורך להוסיף שורה שטוענת את ה Debugger , זו שורה שנוספת לקוד על כל המשתמע מכך, ויש גם לדאוג שלא תופיע בסביבת Realese.
- לא ניתן להדפיס הודעות ל Console
סדרת פוסטים זו , נועדה כדי לפתור בעיות אלו.
הרצת ה Service כ Console בזמן debug.
כדי להריץ את ה Service בלחיצת F5 (או סתם כך משורת הפקודה) , יש ליצור הבחנה בין הסביבה בה רצה האפליקציה כ Service לבין הסביבה בה רצה האפליקציה שלא כ Service.
הפתרון הפשוט הוא, לבצע אבחנה האם למשתמש יש אינטראקציה עם שלחן העבודה. למי אין? ל WindowsService (אלא אם כן הוגדר אחרת – ולזה ניתן פתרון אחר).
בעת עליית המערכת נבדוק:
if (Environment.UserInteractive())
בדיקה זו תספק כמעט את כל הפתרון, וכך נוכל לבדל בין שתי סביבות ההרצה.
if (Environment.UserInteractive)
{
RunServiceInConsole();
}
else
{
RunAsWindowsService();
}
המתודה RunAsWindowsService תספק את המימוש הרגיל
private static void RunService()
{
ServiceBase.Run(new[] { new MyService() });
}
המתודה RunServiceInConsole תקרא למתודות של ה Service באמצעות Reflection:
private static void RunServiceInConsole(string[] args)
MyService service = new MyService();
Console.WriteLine("Press <ENTER> to terminate service.");
המתודה service.Start אינה קיימת, אלא זו הרחבה:
public static class WindowsServiceHelper
{
private static MethodInfo MethodInfo { get; set; }
public static void Start(
this ServiceBase service, string[] args)
{
if (MethodInfo == null)
{
MethodInfo = service.GetType()
.GetMethod("OnStart",
BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(MethodInfo != null
, "Can't get method OnStart");
}
MethodInfo.Invoke(service, new[] { args })
}
}
בשיטה המתוארת כאן, נוכל להריץ את ה Service שלנו משורת הפקודה, והוא יריץ אותו כאפליקצית Console רגילה.
במידה ובחרנו שלService שלנו תהיה אינטראקציה עם ה desktop הפתרון המוצע כאן לא יועיל. למקרה זה יהיה פתרון ב post נוסף.
נקודה נוספת שחשוב להדגיש היא, שיש לשנות את סוג הפרויקט ל Console Application
כמובן שעדיף לשפר את הפתרון, ולהשתמש לשם כך ב generics:
public static class WindowsServiceRunner<TService>
where TService : ServiceBase, new()
{
public static void Run(string[] args, string serviceName)
{
if (Environment.UserInteractive)
{
RunServiceInConsole(args);
}
else
{
RunService();
}
}
private static void RunService()
{
ServiceBase.Run(new[] { new TService() });
}
private static void RunServiceInConsole(string[] args)
{
TService service = new TService();
service.Start(args);
Console.WriteLine("Press <ENTER> to terminate service.");
Console.ReadLine();
service.Stop();
}
}
להורדת הקוד לכל הסדרה
הפוסטים הבאים:
אודי תאר מערכת מסחר אלקטרוני ענקית, אותה הוא פיתח.
נתנו להם זמן ומשאבים, כדי שייכתבו כמו שצריך, מכיון שהמערכת הקיימת לא הצליחה.
הם התחילו לפתח לפי מיטב הטכנולוגיה ב SOA.
כשהגיעו לבדיקות העומס, התגלה שלמרות החלוקה היפה למודולים, נוצרו מעגלים של תלויות.
וגרוע מכך, בשל העומס, נוצרו נעילות בבסיס הנתונים באחת הקומפוננטות, שגרמו לכל שאר הקריאות להתקע. כך הם גילו באקראי מה כמות ה threads המקסימלית אותם ניתן להריץ במערכת ההפעלה...
ואז שינו את התפיסה.
למרות שכל הפעולות נראות שהן ברצף: קומפוננטה ששואלת קומפוננטה, ששוב שואלת קומפוננטה אחרת. ניתן היה לשבור ולשנות.
השינוי בא, ע"י שהפכו את מערכת הקומפונננטות לאוסף בלתי תלוי, המפרסם ארועים.
כל קומפוננטה, פירסמה את מה שצריכים ממנה הקומפוננטות האחרות.
מאידך, כל קומפוננטה נרשמה לארועים רלוונטיים לפי הצורך, ושמרה אצלה את המידע הדרוש לה לשימוש מאוחר יותר.
כעת לא היו נעילות, ודרישות החומרה ירדו משמעותית.
השינוי הביא את אנשי ה business עמוק לתוך התהליך.
כעת היה צריך לתת את הדעת, למשל, מתי מתיישנים הנתונים. באיזו תדירות צריך לפרסם, וכן הלאה.
השינוי הביא לתובנה נוספת. חלוקה נכונה של המודולים בתוך הקומפוננטות.
ושוב אנשי ה business שותפו יותר בתהליך.
קבלנו: PUB-SUB-SOA
כמובן, שסקירה של כמה שורות לא מכסה בכלל הרצאה מעולה של מספר שעות. מומלץ למצוא את הדרך להאזין להקלטה, אחרי שאהד ישראלי יעלה את ההקלטה לרשת.
זו הפגישה השניה והמוצלחת של קבוצת נס-ציונה. היה שווה לבוא. (25 דקות מתל-אביב, מודיעין ואשדוד!)
באפלקצייה החלונאית (windows forms) שאנחנו מפתחים, מציגים קובץ עזרה.
את הקובץ מיקמנו בשרת.
התוצאה: רואים את החלקים של ה content ה index וכדומה, אבל את דף העזרה עצמו לא רואים.
דף העזרה, הוא בעצם דף HTML, ולחיצה על נושא כלשהו, מציגה אותו.
מכיון שקובץ העזרה לא ממוקם לוקאלית במכונה של המשתמש, אלא בשרת, windows מונע את הגישה.
לצרה יש גם פתרון: שינוי המדיניות של אפליקציית העזרה, כך שתציג קבצים מאינטראנט או מהאינטרנט או משרתים מסוימים וכדומה.
אנחנו בחרנו, בהרשאה לצפייה בקבצים מהאינטראנט.
ב link הבא יש הסבר מפורט לפתרון: אין אפשרות לפתוח תוכן מרוחק באמצעות פרוטוקול InfoTech לאחר התקנת עדכון אבטחה 896358, עדכון אבטחה 840315 או Windows Server 2003 Service Pack 1
אתמול, היתה הרצאה של ולד אזרקין בנושא Manipulating XML with LINQ-to-XML in C# 3.0
בתוך ה namespace של System.Xml.Linq יש אוסף של אוביקטים עבור XML עם הקדומת X (XDocument, XElement...). האוביקטים כתובים עם כל היכולת של תכנות מונחה עצמים.
זה מאפשר למשל הגדרה פונקציונאלית (= במשפט אחד) של מסמך XML.הוצגו גם יכולות של מניפולציות על הנתונים, חיפושים,
1 var contacts =
2 new XElement("Contacts",
3 new XElement("Contact",
4 new XElement("Name", "Patrick Hines"),
5 new XElement("Phone", "206-555-0144",
6 new XAttribute("Type", "Home")),
7 new XElement("phone", "425-555-0145",
8 new XAttribute("Type", "Work")),
9 new XElement("Address",
10 new XElement("Street1", "123 Main St"),
11 new XElement("City", "Mercer Island"),
12 new XElement("State", "WA"),
13 new XElement("Postal", "68042")
14 )
15 )
16 );
אין ספק שצורה זו של יצירת קובץ XML קצרה יותר וברורה יותר. דוגמא להבדל ניתן לראות בLINQ to XML vs. DOM
בהרצאה הוצגו שפע של דוגמאות של מניפולציות ותיחקור ואפילו ביצוע שאילתת LINQ על WebService של Amazon
אין ספק - המחלקות החדשות - טובות ושימושיות.
מה שלא ברור הוא - מדוע זה חלק מ LINQ. בהיבט של LINQ העבודה זהה , לעבודה עם אוביקטים.
ב VB9 הסיפור כבר שונה לחלוטין. כאן בהחלט יש שינוי מהותי בשפה.
לקחו את הגישה של יצירת קובץ ASP והכניסו אותה ממש לתוך הקוד הרגיל. כאן התוצאה הרבה יותר ברורה
הנה אותו אתחול בדיוק, רק שהפעם הוא ב VB:
1 Dim contacts = _
2 <Contacts>
3 <Contact>
4 <Name>Patrick Hines</Name>
5 <Phone Type="Home">206-555-0144</Phone>
6 <Phone Type="Work">425-555-0145</Phone>
7 <Address>
8 <Street1>123 Main St</Street1>
9 <City>Mercer Island</City>
10 <State>WA</State>
11 <Postal>68042</Postal>
12 </Address>
13 </Contact>
14 </Contacts>
זה נראה כמו string של XML אבל בפועל נוצרים כאן כל האוביקטים שבקוד העליון.
כשבונים מבנה XML בתוך איטרציה, זה כבר ממש דומה ל ASP:(בדוגמא זו מציגים, תחליף ל XSLT(
1 Public Function ExtractAssembly(ByVal assembly As XElement) As XElement
2 Return <html>
3 <head>
4 <title>
5 <%= ExtractName(assembly) %>
6 </title>
7 </head>
8 <body>
9 <div>
10 <h1>Assembly: <%= ExtractName(assembly) %></h1>
11 <%= From ns In assembly.Elements("namespace") _
12 Select ExtractNamespace(ns) %>
13 </div>
14 </body>
15 </html>
16 End Function
כשמדובר במניפולציה של נתונים, יש תחביר חדש ונח, intelisence:
1 Dim query = From customer In doc...<Customers> _
2 Order By CStr(customer.<ContactName>.First()) _
3 Select customer.<ContactName>
לעומת אותו קוד ב C#
1 var query =
2 from
3 customer in doc.Descendants("Customers")
4 orderby
5 (string)customer.Elements("ContactName").First()
6 select
7 customer.Element("ContactName");
נחמה אחת יש למפתחי C#, Microsoft החביאו addin לסביבה שיכול לחולל את הקוד ליצירת מסמך.
מעתיקים ל clipboard תוכן של מסמך XML, ואז מפעילים את הפקודה Paste Xml as XElement.
Scott Hanselman מסביר היכן מפתחי microsoft החביאו את התוסף החביב.
מזל טוב! user group חדש הגיח אתמול לאויר העולם. אהד ישראלי הנמרץ, החליט לעשות מעשה ולייסד קהילה חדשה.
אז מה היה לנו? הרצאה טובה מאוד של אלכס גולש מסלע, אודות Silverlight 2
אלכס הציג, את הרקע והיסודות של הסביבה. את כלי הפיתוח, תוך השוואה ל WPF ולשיטות פיתוח Web שונות.
היה כיף לראות, עוד ועוד הדגמות. גם למי ששאל את המרצה על יכולת שלא תכנן מראש להציג, היה דמו מוכן להצגה תוך שניות.
אז אם אתה גר או עובד בתל-אביב ודרומה, בודאי תשתמח לשמוע, שהקבוצה מתעתדת להפגש בכל יום רביעי השני בחודש.
(כך שעדיין תוכל להגיע לרעננה, לקבוצת VB או C#, שנפגשות בימי רביעי אחרים)
רוצים לכתוב פרוטוטייפ מהיר לחלון אפליקציה או ל web?
Balsamiq הוא כלי מהיר ויעיל.
בנוסף, הוא ניתן חינם למי שתורם: opensource, בלוגרים וכדומה


בשפה התורכית יש שני תווים שונים לאות I. ליתר דיוק, בנוסף לאותיות הרגילות: i ו I יש גם ı ו İ.
אז מה זה משנה שלכולם יש שתי עינים (eye) ולתורכים 4?
אם נכתוב, בדומה למה שהיה מקובל בשפת C:
if ("I".ToUpper() == "i".ToUpper())
התשובה תהיה שלילית. למה? כי i רגילה הופכת לאות גדולה גם כן עם נקודה: İ
ומכיון, שלעולם לא נדע, אם פתאום הקוד שלנו יגיע לתורכיה, לכן יש להשתמש בקוד:
if(string.Compare("i","I",StringComparison.OrdinalIgnoreCase) == 0)
לבעית ה I בתורכית, יש גם הבטים של מיון.
לקריאה נוספת:
לוח מקשים בתורכית
יודגש: עבודה בסטנדרטים שמובאים במאמר, הם חיוניים, גם אם אנחנו בטוחים שלתורכיה לא נגיע עם הקוד. וחוץ מזה, לעולם אינך יודע...
בהמשך לפוסט: טיפול כולל ל-Unhandled Exception או "זה לא באג,הכל בטיפול"
למדתי, החכמתי, וכבר אנחנו מיישמים את הדברים בצוות שלי.
בעקרון, ה handlers שמוגדרים כאן הם שניים: אחד ברמת ה thread. והשני ברמת ה AppDomain. מיקי מוסיף, וזה חשוב להדגיש, שעבור כל thread יש להוסיף handler משלו, אחרת לא נוכל לשלוט על התוצאות.
מנסיונות קודמים שלי, ראיתי, ש Exeption שנזרק מ thread אחר, מגיע אמנם ל handler של ה AppDomain אבל רק מדווח שם ואי אפשר לעצור אותו.
הסיבה: ה Excetion ב thread השני גרם לתעופה, קרי unhandled exception , זו גרמה לתעופה כללית של האפליקציה ולסיומו הבלתי צפוי של ה thread לו הצמדנו את ה handler. לכן דווח יש כאן, אבל יכולת שליטה - אין כאן.
מה לעשות? כבר אמרנו: להצמיד handler לכל thread.
ומה אם אין לנו יכולת לבצע זאת? למשל: שימוש ב API של גורם חיצוני, שמריץ thread אחר?
לדוגמא: פתיחת com port באמצאות SerialPort. במקרה זה, אם נרצה להאזין למה שנרשם ל port, נצטרך לצרף delegate. אם ה port ינותק באופן ברוטלי, ה thread הפנימי, זה שאמור לחזור אלינו עם ה delegate זורק Exception שלא ניתן לתפוס אותו.
בגרסה 1.1 של .NET המצב היה שונה, וה handler של ה AppDomain קיבל שליטה גם על thread-ים אחרים.
אז מה לעשות במקרה שלנו? לבקש יפה מה framework להתנהג כאילו היינו עדיין בגירסה 1.1, לשם כך נוסיף לקובץ הקונפיגורציה את השורות הבאות:
<runtime>
<legacyUnhandledExceptionPolicy enabled="1"/>
</runtime>
הסבר מפורט לכל מה שכתבתי: Exceptions in Managed Threads.
שימו לב למה שכתוב שם:
As a temporary compatibility measure, administrators can place a compatibility flag in the <runtime> section of the application configuration file. This causes the common language runtime to revert to the behavior of versions 1.0 and 1.1.
נקווה לטוב.
כמקובל, אנסה להתוות קו כללי למטרות הבלוג החדש שלי.
אעשה זאת, על ידי תאור של בלוג קודם שהיה לי.
הבלוג נוהל באתר שכתובתו blogs.wdevs.com והתקיים במשך כשנתיים וחצי.
התכנים היו אוספים של מידע בתחומי פיתוח שנחשפתי אליהם באותו זמן, או שאלות של תלמידי ( בקורסים בנושאי .NET או כאלו שהכינו עבודות גמר)
בד"כ כלל הייתי נוהג לעדכן את הפוסטים, במידה והייתי מוצא חומר נוסף באותו נושא.
יום אחד השרת נעלם, ושגיאה 404 נותרה..
עד... עד שאליעזר בירנבוים ממטריקס, סיפר לי על אתר ש"מצלם" את מצב העולם הוירטואלי מדי פעם. מצאתי שם את רוב הפוסטים שלי, ואחדים מהם אביא בהמשך.
WayBack הוא האתר, וניתן לצפות בו במצבו של אתר במשך שנים (החל מינואר 1996) לאחור.
הנה שני צילומי מצב של הבלוג שלי, כשעוד היה חי: Mar 21, 2006 , May 18, 2007.
יוסי תגורי , ממליץ ובצדק, לערוך בלוג עם Windows Live Writer.
לשם כך הוא מפנה להורדה מהכתובת: spaces.live.com
ניסיתי - ולא הצלחתי. ההתקנה היתה מוכנה להתקין תוכנה לתמונות ולדוא"ל, אבל לא את העורך הנכסף.
לאחר חיפושים, מצאתי בכתובת הבאה: WL_Writer.exe הורדה מתאימה.
אגב חיפושים, הופתעתי מכמות ה Plugins שניתן להתקין לתוכנה.
אז הנה ציטוט מתוך: Most Useful Windows Live Writer Plugins
Wish to enhance your blogging experience with Windows Live Writer ? Here are some very useful plug-ins that will supercharge your copy of Windows Live Writer.
Blog This - This is like the Tumble bookmarklet for quick blogging - select some text on a web page in IE or Firefox and click the Blog This button - it will launch a new blog post prepopulated with the title, text snippet and URL of the current webpage. [example]
Insert Video - Use this to quickly embed video clips from YouTube, Google Video or Myspace Videos into your blog posts. Just type the URL of the video and the plugin will generate the code for you.
Flickr4Writer - If you are a Flickr member, this plugin will help you browse your Flickr photos and insert them into your blog posts.
Social Bookmarks - Add social bookmarking icons (like digg, del.icio.us, furl, yahoo!) in your blog posts that will allow readers to save or share your articles on these social sites.
Screen Captures - This is like an offline version of Picnik Screen Capture. Specify a webpage URL and this plugin will insert a screenshot of that page in your blog post that you can later resize or apply image effects.
Syntax Highlighter - Recommended for geeks and developers who frequently share code on their blogs. This plugin will highlight the code syntax and language specific keywords in various colors.
WebSnapr - Allows you to insert thumbnail images of web pages in your blog posts similar to ones that you see on Alexa or Snap.com
Insert Code - It formats a snippet of text in a number of programming languages such as C#, HTML or JavaScript. Makes the source code more readable.
For additional Windows Live Writer plugins, please visit the Live Gallery and the excellent WLWPlugins.com. Want to write your own Windows Writer Plugin ? Try this useful guide by Keyyan Nayyeri (uses C#).