לאחרונה התבקשתי לכתוב אפליקצייה שעוצרת כל הדפסה שנשלחת למדפסת ושואלת את המשתמש האם הוא רוצה להדפיס דו צדדי, במידה וכן לשנות את הגדרות ההדפסה.
לכאורה זה נשמע ממש קל, אבל לאחר שמצאתי את הפתרון אני חייב להגיד שזה היה אחד מהפרוייקטים היותר מאתגרים שעשיתי, כמות המידע שלמדתי בזמן חיפוש אחר פתרון היא באמת רבה, ואני אנסה להעביר את החלקים המעניינים מתוך הפתרון המלא בכמה פוסטים כך שלא יהיה יותר מידי חומר בפוסט אחד.
בפוסט הזה אני אדגים את השלב הראשון בפתרון.
די ברור שעל ההתחלה צריך איכשהו להאזין לשליחת הדפסות ולאפשר לעצור אותם, ב - windows הדרך להאזין לבקשות הדפסה היא בעזרת שאילתת WMI (טכנולוגיה המאפשרת להגדיר שאילתות על מערכת ההפעלה ולקבל את המידע (החל ממידע על דיסק שחובר למחשב ועד לכשלים בתקשורת).
נגדיר מחלקה סטטית בשם WMI.(צריך להוסיף reference ל - System.Management.dll)
public static class WMI
{
בתוך המחלקה נגדיר את השדות הבאים:
private static ManagementEventWatcher _printEventWatcher;
private static double? _queryInterval;
הראשון הוא אובייקט שיודע להאזין למערכת הפעלה לפי שאילתא מוגדרת, והשני יגדיר לשדה הראשון כל כמה זמן להריץ את השאילתא.
נרצה לחשוף אירוע של "הדפסה נשלחה" כך שבמחלקות אחרות יוכלו לקבל את האירוע ולעשות משהו (למשל, לעצור את ההדפסה)
public static event EventHandler<GenericEventArgs<int>> JubSent;
כעת נגדיר מאפיין שייאתחל את המשתנה queryInterval. (במידה ויש הגדרה בקונפיג נקח את הערך משם, אחרת נגדיר ברירת מחדל)
private static double QueryInterval
{
get
{
if (_queryInterval == null)
{
double result;
if (double.TryParse(ConfigurationManager.AppSettings["QueryInterval"], out result))
{
_queryInterval = result;
}
else
{
_queryInterval = 0.001;
}
}
return _queryInterval.Value;
}
}
נגדיר את השאילתא למערכת ההפעלה
private static string PrintJobQuery
{
get
{
return "SELECT * FROM __InstanceCreationEvent WITHIN " +
QueryInterval +
" WHERE TargetInstance ISA \"Win32_PrintJob\"";
}
}
כעת נגדיר static ctor עבור המחלקה שיאתחל את השדות:
static WMI()
{
_printEventWatcher = new ManagementEventWatcher();
_printEventWatcher.Query = new EventQuery(PrintJobQuery);
_printEventWatcher.Scope = new ManagementScope("\\\\" + Dns.GetHostName() + "\\root\\CIMV2");
_printEventWatcher.EventArrived += _printEventWatcher_EventArrived;
}
נגדיר מתודה שנפעיל בזמן שהדפסה מתקבלת:
private static void _printEventWatcher_EventArrived(object sender, EventArrivedEventArgs e)
{
if (JubSent != null)
{
OnJobSent(e);
}
}
private static void OnJobSent(EventArrivedEventArgs e)
{
int jobId = int.Parse(GetValue(e, "JobId").ToString());
var args = new GenericEventArgs<int>(jobId);
JubSent.Invoke(null, args);
}
private static object GetValue(EventArrivedEventArgs e, string key)
{
object val = ((ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value).Properties[key].Value;
return val;
}
המתודה האחרונה מחלצת מתוך ה - WMI ערך שנרצה.
כעת כל מה שנשאר זה להפעיל את השאילתא ולחכות להדפסות (כמובן שמישהו צריך להרשם לאירוע ולעשות משהו - לעצור את ההדפסה - את זה נראה באחד הפוסטים הבאים)
private static bool _enable;
public static bool Enable
{
get
{
return _enable;
}
set
{
if (value)
_printEventWatcher.Start();
else
_printEventWatcher.Stop();
_enable = value;
}
}
באחד הפוסטים הבאים אני אספר כיצד ניתן לשנות את ההגדרות (חלק מהם) לאחר שההדפסה נשלחה ומה המשמעות של spool file וכיצד הוא בנוי.