Temporary files in application specific folder.

Sometimes you come across something you wish you knew 5 years ago. I can’t count the number of projects I’ve written or inherited that work with temporary files. Out of the box, .NET provides two functions to help you work with temp files.

  • System.IO.Path.GetTempFileName Creates a uniquely named, zero-byte temporary file on disk and returns the full path of that file.
  • System.IO.Path.GetTempPath Returns the path of the current user’s temporary folder ending with a backslash.

I was working on a service recently that’s going to be creating / deleting lots of temp files, and I thought it would be helpful to move them to their own folder. Some of the projects I’ve worked on have used this technique, so I pulled one up to see how it was implemented. I was surprised to find code like:

string fileName = Path.GetTempFileName();
File.Move(fileName, Path.Combine(Path.GetTempPath(), "MySubfolder\\"))

Which works, but there’s no guarantee that the file created in the parent folder will still be unique in the child. I’m a pessimist, and I wanted something better. I started thinking there should be a SetTempPath method to changes where GetTempFileName creates it’s files. But SetTempPath doesn’t exist. Oh wait, yes it does! But the secret is hidden in the documentation details of GetTempPath

This method checks for the existence of environment variables in the following order and uses the first path found:

  1. The path specified by the TMP environment variable.
  2. The path specified by the TEMP environment variable.
  3. The path specified by the USERPROFILE environment variable.
  4. The Windows directory.

Reading this, If I change where TMP points to, I change where GetTempFileName creates files. And that’s easy enough:

string tmp = Path.GetTempPath() + "MySubfolder";
Directory.CreateDirectory(tmp);
Environment.SetEnvironmentVariable("TMP", tmp, 
                                   EnvironmentVariableTarget.Process);

I added these lines of code to the entry point of my service, and GetTempFileName now puts my temp files exactly where they need to go. Specifying EnvironmentVariableTarget.Process ensures I’m not changing the behavior of any other application.

Detecting errors in a custom ConfigurationSection

The service I am currently writing has some complex configuration settings. I’ve created custom ConfigurationSections to handle validation. My goal is to validate the entire configuration at startup. In the process I’ve observed some interesting behaviors about ConfigurationSection’s.

To illustrate, I begin with the definition of a simple configuration section:

class MySection : ConfigurationSection
{
    [ConfigurationProperty("lions", DefaultValue="X", IsRequired=true)]
    [StringValidator(MinLength=1, MaxLength=10)]
    public string Lions
    {
        get { return (string)this["lions"]; }
        set { this["lions"] = value; }
    }

    [ConfigurationProperty("tigers", DefaultValue="", IsRequired=true)]
    [StringValidator(MinLength=0, MaxLength=10)]
    public string Tigers
    {
        get { return (string)this["tigers"]; }
        set { this["tigers"] = value; }
    }

    [ConfigurationProperty("bears", DefaultValue="chicago", IsRequired=true)]
    [CallbackValidator(Type=typeof(MySection), CallbackMethodName="OhMy")]
    public string Bears
    {
        get { return (string)this["bears"]; }
        set { this["bears"] = value; }
    }

    public static void OhMy(object value)
    {
        string s = (string)value;
        Console.WriteLine("Checking value '{0}'", s);
        if (s.ToLowerInvariant().Contains("panda"))
        {
            Console.WriteLine("Raising \"No panda's allowed\"");
            throw new ConfigurationErrorsException("No Pandas Allowed!");
        }
    }
}

And a program to load/access it:

class Program
{
    static void TestProperty(ConfigurationSection s, string n)
    {
        try
        {
            // access to s[n] is protected so...
            Console.WriteLine("Testing {0}='{1}'", n,
                                    s.ElementInformation.Properties[n].Value);
        }
        catch (ConfigurationErrorsException x)
        {
            Console.WriteLine("Testing {0}= {1}", n, 
                                     x.GetBaseException().Message);
        }
    }

    static void Main(string[] args)
    {
        try
        {
            Console.WriteLine("Opening configuration");
            var c = ConfigurationManager.
                         OpenExeConfiguration(ConfigurationUserLevel.None);
            Console.WriteLine("Loading 'mySection'");
            var s = (MySection)c.GetSection("mySection");
            Console.WriteLine("mySection loaded? {0}", null != s);
            if (null != s)
            {
                // bypass accessors to avoid 3 try/catch blocks in sample
                TestProperty(s, "lions");
                TestProperty(s, "tigers");
                TestProperty(s, "bears");
            }
        }
        catch (Exception x)
        {
            Console.WriteLine(x.GetBaseException().Message);
        }

        if (Debugger.IsAttached) // make F5 work like CTRL+F5
        {
            Console.WriteLine("Press any key to continue . . .");
            Console.ReadKey();
        }
    }

Running the program with the following configuration:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
  </configSections>
</configuration>

Produces:

Opening configuration
Loading 'mySection'
mySection loaded? False

Since the section isn’t defined, the expected outcome is that it is not loaded. But now it starts getting interesting because this configuration:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="mySection" type="UnitTest.MySection,UnitTest"/>
  </configSections>
</configuration>

Produces:

Opening configuration
Loading 'mySection'
Checking value 'chicago'
mySection loaded? True
Testing lions='X'
Testing tigers=''
Testing bears='chicago'


Even though there is no <mySection> tag in the configuration file, a section is created using all the default values. Further, those values are validated as evidenced by the "Checking value 'chicago'" reported by the CallbackValidator.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="mySection" type="UnitTest.MySection,UnitTest"/>
  </configSections>
  <mySection />
</configuration>

Produces:

Opening configuration
Loading 'mySection'
Checking value 'chicago'
Required attribute 'lions' not found.
(c:\dev\foo\UnitTest\UnitTest\bin\Debug\UnitTest.exe.Config line 6)


Here all the default values were again loaded as reported by the CallbackValidator. Then the section was checked for required values. Since none were supplied, the expected exception was raised and then trapped by line 35 of the program. This configuration:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="mySection" type="UnitTest.MySection,UnitTest"/>
  </configSections>
  <mySection lions="mufasa" tigers="tigger" bears="smokey"/>
</configuration>

Produces:

Opening configuration
Loading 'mySection'
Checking value 'chicago'
Checking value 'smokey'
mySection loaded? True
Testing lions='mufasa'
Testing tigers='tigger'
Testing bears='smokey'

With a fully valid configuration, it's plain that both default values and supplied values are validated as the section gets loaded. Also note that when the bears property is read, the validation method is not called a third time.

Now the really confusing bit. This Configuration:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="mySection" type="UnitTest.MySection,UnitTest"/>
  </configSections>
  <mySection lions="mufasa" tigers="tigger" bears="panda"/>
</configuration>

Produces:

Opening configuration
Loading 'mySection'
Checking value 'chicago'
Checking value 'panda'
Raising "No panda's allowed"
mySection loaded? True
Testing lions='mufasa'
Testing tigers='tigger'
Testing bears= The value for the property 'bears' is not valid.
The error is: No Pandas Allowed!
(C:\dev\foo\unittest\unittest\bin\debug\UnitTest.exe.Config line 6)

Despite there being an invalid value 'panda' in the configuration file, the section loaded successfully. In contrast to the missing required value error above, no exceptions were during the load even though "Raising No Panda's Allowed" indicates that the throw line did execute. Only later when the property is accessed does the exception get raised, but where did that exception come from if the callback was not executed again? One last configuration to examine. This configuration:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="mySection" type="UnitTest.MySection,UnitTest"/>
  </configSections>
  <mySection lions="mufasa_too_long" tigers="tigger" bears="smokey"/>
</configuration>

Produces:

Opening configuration
Loading 'mySection'
Checking value 'chicago'
Checking value 'smokey'
mySection loaded? True
Testing lions= The value for the property 'lions' is not valid.
The error is: The string must be no more than 10 characters long.
(C:\dev\foo\unittest\unittest\bin\debug\UnitTest.exe.Config line 6)
Testing tigers='tigger'
Testing bears='smokey'

This illustrates that this strange behavior isn't limited to the CallbackValidator. What's happening here is that failures reported by ConfigurationValidators are trapped by the framework, They get added to a collection found at section.ElementInfo.Errors. Looking at the documentation of ElementInfo another useful property is IsPresent which provides a nice way to tell if the tag existed in the file or if it was created on the fly.

And my updated program to detect error conditions before trying to use attributes:

class Program
{
    static void TestProperty(ConfigurationSection s, string n)
    {
        try
        {
            // access to s[n] is protected so...
            Console.WriteLine("Testing {0}='{1}'", n, s.ElementInformation.Properties[n].Value);
        }
        catch (ConfigurationErrorsException x)
        {
            Console.WriteLine("Testing {0}= {1}", n, x.GetBaseException().Message);
        }
    }

    static void Main(string[] args)
    {
        try
        {
            Console.WriteLine("Opening configuration");
            var c = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            Console.WriteLine("Loading 'mySection'");
            var s = (MySection)c.GetSection("mySection");

            // Make sure there is a section definition.
            if (null == s)
                throw new ConfigurationErrorsException(
                     "No section named 'mySection' in configuration file.");

            // Make sure there's a matching section element.
            if (!s.ElementInformation.IsPresent)
                throw new ConfigurationErrorsException(
                     "No element 'mySection' in configuration file.");
            
            // Make sure there were no validation errors on load.
            // (Just throw the first one, though it could be nice and build a list.)
            foreach(Exception x in s.ElementInformation.Errors)
                throw new ConfigurationErrorsException("Validation Error", x);

            // all validation tests have passed!
            TestProperty(s, "lions");
            TestProperty(s, "tigers");
            TestProperty(s, "bears");
        }
        catch (Exception x)
        {
            Console.WriteLine(x.GetBaseException().Message);
        }

        if (Debugger.IsAttached) // make F5 work like CTRL+F5
        {
            Console.WriteLine("Press any key to continue . . .");
            Console.ReadKey();
        }
    }
}

I can't explain the quirks in the validation process, but hopefully I've provided a reasonable workaround.

iPad AirPrint using Ubuntu server and a network printer

Like a lot of other folks, I recently updated my iPad to iOS 4.2.1 and began looking to see what worked and what didn’t. After getting over the mute switch, I wanted to try AirPrint. I don’t know how much I’ll ever use this feature but it bugs me that support is limited to so few printers. I have an HP OfficeJet Pro L7680 All in One. I loathe this printer, and don’t recommend buying one but it’s the printer I own. Anyway I thought maybe if I could find enough information on the Internet I could configure my Linux VM to serve up my “incompatible” printer.

Most of what follows is based on a blog entry from Ryan Finne with added help from a blog entry by tjfontaine. I won’t repeat their great work here. Instead, I hope to add more of the noob perspective since I didn’t even know what CUPS was until I began this quest.

My Linux VM is a minimal install of Ubuntu 10.04 server with samba, vsftpd, and nfs. I use it primarily to share a big USB drive to the Windows boxes in my home and also to the ESXi server hosting it. My VM is named allspice. However, I do not run a local DNS so everything was done using it’s static IP address of 192.168.8.199. Your mileage may vary.

Step 1 was to get the files necessary to support printing from the VM:

sudo apt-get install cups cups-pdf hplip

This got me the CUPS files, the PDF filter, and the support files for my HP printer. With no GUI on the server, the next step was to gain access to the CUPS web interface from another box. I had to modify 0/etc/cups/cupsd.conf because the default file only allows localhost access. Below is a summary of my changes:

#Listen localhost:631
#Listen /var/run/cups/cups.sock
Port 631
ServerAlias *

<Location />
 Order allow,deny
 Allow @LOCAL
</Location>

<Location /admin>
 Order allow,deny
 Allow @LOCAL
</Location>

CUPS Server SettingsAfter restarting CUPS, I could browse https://192.168.8.199:631 and see the web interface. There I changed the basic settings on the configuration tab as noted. Once the server restarted and the page refreshed, I clicked the Find New Printers button. My network printer was located and included in the list of printers to add. In my case my printer model showed up twice in the list. Both entries appeared identical HP HP Officejet Pro L7600 (Officejet Pro L7600 [9CD796]) however viewing the page source revealed that one had a URI of:

dnssd://Officejet%20Pro%20L7600%20%5B9CD796%5D._pdl-datastream._tcp.local

while the other had a URI of:

dnssd://Officejet%20Pro%20L7600%20%5B9CD796%5D._printer._tcp.local

I chose the one ending in _printer._tcp.local. I named it HPL7680, and chose the appropriate driver from the offered list. The last thing I did was to print a test page and verify that the VM was capable of printing.

Next I jumped in head first and followed my source’s instructions only to get nowhere fast. Something was missing. After a half a day of no-progress I slept on it, and woke up with a fresh idea. I have one other Linux box, an Asus EEE netbook which also is running Ubuntu. I pulled that out and sat down to try and print Ubuntu to Ubuntu. Once I fed it (just) the address of the print server, the netbook detected my printer at ipp://192.168.8.199:631/printers/HPL7680 and I was able to print a test page.

Now I knew my print server was serving up my configured printer to my network. But the iPad still wouldn’t display it. The problem had to lie in the advahi configuration. I re-ran tjfontaine’s script and had a look at the file it generated:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
  <name replace-wildcards="yes">AirPlay HPL7680 @ %h</name>
  <service>
    <type>_ipp._tcp</type>
    <subtype>_universal._sub._ipp._tcp</subtype>
    <port>631</port>
    <txt-record>txtvers=1</txt-record>
    <txt-record>qtotal=1</txt-record>
    <txt-record>Transparent=T</txt-record>
    <txt-record>URF=none</txt-record>
    <txt-record>rp=/printers/HPL7680</txt-record>
    <txt-record>note=HP Officejet Pro L7600</txt-record>
    <txt-record>product=(GPL Ghostscript)</txt-record>
    <txt-record>printer-state=3</txt-record>
    <txt-record>printer-type=0x80901c</txt-record>
    <txt-record>pdl=application/octet-stream,application/pdf,application/postscript,image/gif,image/jpeg,image/png,image/tiff,text/html,text/plain,application/openofficeps,application/vnd.cups-banner,application/vnd.cups-pdf,application/vnd.cups-postscript</txt-record>
    <txt-record>adminurl=ipp://localhost:631/printers/HPL7680</txt-record>
  </service>
</service-group>

Using the default options, the file it created contained adminurl=ipp://localhost:631/printers/HPL7680 which obviously won’t work remotely. I manually changed this to ipp://192.168.8.199:631/printers/HPL7680 and restarted the avahi-daemon.

My netbook could see AirPlay HPL7680 @ allspice but not my iPad. I tried configuring the printer on the netbook using the AirPlay configuration. Still no test page was printed. Browsing the CUPS error_log revealed this line:

E [26/Nov/2010:09:31:20 -0500] Bad URI "//printers/HPL7680" in request!

After I changed the line <txt-record>rp=/printers/HPL7680</txt-record>
to remove the leading ‘/’. I restarted the avahi-daemon and finally my iPad could see and more importantly print to the printer.

Stoopit Code, Stoopit Space, Stoopit Comment

I was in the middle of updating an 830 line function in my favorite web application when I came across a single line of code that gave me pause:

CString strSpace = _T(" "); // This is a Space character

As much as I am a proponent of commenting, sometimes people just don’t get the purpose behind them. I don’t know who wrote this. But sitting here 5-10 years later I have to wonder exactly what they were thinking when this line was keyed in. The comment, at first glance appears incorrect. A character in c++ is written with single quotes. So _T(‘ ‘) if you include the macro for UNICODE.  This is not a Space character. This is a zero terminated string containing a single space.

Names are important and strSpace is pretty specific. It’s not strTemp or strToBeAnnounced. Seeing strSpace sets the mind to thinking “this thing holds a space and probably won’t change”.  Why bother? What use can there be in declaring strSpace?

For methods taking a constant reference:

void MyMethod(const CString& strInput);

_T(“”) can be swapped in in for the tiniest of performance hits.

For methods that take a non-constant reference:

void MyMethod(CString& strInOut);

Here passing _T(“”) wrapped inside a variable might be required but calling it strSpace is a lie if it is eventually to be changed to some other value.

So neither is a very valid reason for the declaration.  I thought about it some more.  One other reason for declaring fixed values is to guard against change.  Under what circumstances would strSpace or _T(” “) need to be changed to a different value? What possible change was the original developer guarding against by declaring strSpace?

To answer that I needed to examine the context in which it was used.  Unfortunately there is no context. The only place strSpace is used in that 830 line method is right there in the variable declaration.

Which in a crazy stupid way validates the comment. It is indeed just Space.

(Sort of like this blog)

A Rain of Arrows

Today I spent some time creating new arrow images for a web application I’m maintaining. For a while now I’ve been toying off and on with a freeware vector graphics program called Inkscape. This gave me a chance to do something real.

Not known for my artistic talent, I glued a triangle and a square together pulled on some corners and quickly came up with:

left.png and up.png which isn’t too bad for 10 minutes work.

But I wasn’t all that sure blue was the right color choice. So I created a new gradient and a couple more exports led to:

right.png anddown.png Looking better.

In the process I learned how to export all four images with one button click.

export.png

The important thing is to select and export each object individually to a unique file name. Inkscape will remember this and use it for the bulk export. Yet another gradient / export iteration and

left.png up.png right.png down.png

And then I really went overboard.

Now to decide what ones to use. Next thing I want to learn is how to apply a stroke to a segment of a path so I can outline the tabs