Filip's homepage

Thursday, 11 September 2008

Finding the Default Printer in .NET

Tags: .NET WindowsForms

I'm currently working on a Windows Forms project that uses reporting, and at one point I need to enumerate the installed printers in a combobox and preselect the default one. A quick stroll on Google lists a number of blogs that present an easy solution:

  1. Create a new PrintDocument object
  2. Look at printDoc.PrinterSettings.PrinterName
  3. ???
  4. Profit!

This worked fine, until I found out step 3 might have a little more to it than the legendary question marks conceal. I noticed that when the default printer cannot be reached (say because you are using a company laptop at home), it seems that the call to printDoc.PrinterSettings.PrinterName takes quite a long time (9 seconds in my test case). This was too slow for my application.

No worries though. My first instinct was that for some reason it was not just getting the default printer name, it was also in some way trying to contact the printer. This made me wonder whether there would be a function in the Win32 API that allows me to just get a name - nothing more. And lo and behold, there is a GetDefaultPrinter function that does just that. It took slightly over 15ms, no matter whether the printer was connected or not. The code is as follows:

public string GetDefaultPrinterName()
{
   var builder = new StringBuilder(1024);
   var size = builder.Capacity;
   return GetDefaultPrinter(builder, ref size) ? builder.ToString().Trim() : "";
}

[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool GetDefaultPrinter(StringBuilder pszBuffer, ref int size);

I hope this little blog post finds its way to Google and can serve as a help to other people who encounter the same problem.

Posted by Filip at 16:00. No comments yet.
Friday, 28 September 2007

When MaskedTextBox.SelectAll doesn't Select All...

Tags: .NET WindowsForms WTF

A client recently asked for a seemingly easy feature to be added to an application of his. The application consists of a form with a number of MaskedTextBox objects, and those textboxes are supposed to select their contents once focused, so any keypress after giving focus would overwrite the existing text in the textbox.

Shouldn't be hard, right? Just subscribe to the textbox.Enter event and then call textbox.SelectAll(), that's what, five minutes? An hour later, things still weren't working as they should. For some reason, the SelectAll() call didn't work. It just didn't select anything (although at one point I was able to make it select only a part of the contents, but that was, well, quite useless for my client).

Turns out MaskedTextBox is an eager little sport. If a mask is set, the control does some selecting of its own when it gets focused, and thus it overrides anything you try to do yourself in response to these events. Cute, but annoying. Once I realised that, the solution wasn't too far away: I had to postpone the SelectAll() call so it executes after the focus events have been handled by MaskedTextBox.

Thankfully, Windows Forms provides a BeginInvoke method that will do just that: Control.BeginInvoke will post a Win32 message (you know, WM_*) to the message queue. This message won't be picked up until the focus events have been processed, so we can leverage this to postpone our SelectAll() call.

Doing this is pretty easy using MethodInvoker and anonymous delegates in .NET 2.0:

textbox.BeginInvoke(new MethodInvoker(textbox.SelectAll));

If only they would have mentioned this in the documentation. You know, the thing you read to find out about stuff like that :)

Posted by Filip at 11:30. 2 comments.