iPhone ringtone relief.

May 8th, 2008

I was really starting to get irked at Apple because it seems like they want me to pay $1.00 (ok, $.99) every time I want to turn one of my songs into a ringtone. I’ve already paid for these songs once and now every time I want to play 18 seconds of it I have to pay another $1? Crazy.

This guy is a genius. I just tried this Make Free Ringtones tutorial and it worked like a champ. Go Ed Zachary!

Windows hosting with GoDaddy is a royal PITA.

May 6th, 2008

At least the shared stuff is. I’ve spent at least four hours today trying to get my SQL 2005 database configured on their server. Since they don’t allow remote access through SQL Server Management Studio, I’ve been forced to use their not-so-powerful web-based administration tool. Compounding that is the fact that I used to have references between two different databases on my local server that I now need to merge into a single monolithic database, I’m not a happy camper.

As part of this process I’ve had to populate a bunch of tables with application-specific values. After wrestling with the crappy CSV import function that GoDaddy gave me I decided to write my own little tool to generate SQL INSERT statements for the data in my tables. Since WordPress won’t let me upload it, here’s the code:

/*
 * Created by SharpDevelop.
 * User: smeans
 * Date: 5/6/2008
 * Time: 11:58 AM
 *
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.IO;

namespace sql2insert
{
  class sql2insert
  {
    public static void Main(string[] args)
    {
      foreach (string dsn in args) {
        Console.WriteLine("writing files to " + System.Environment.CurrentDirectory);

        using (SqlConnection cn = new SqlConnection(args[0])) {
          cn.Open();

          DataTable dt = cn.GetSchema("Tables");

          foreach (DataRow row in dt.Rows) {
            for (int i = 0; i < dt.Columns.Count; i++) {
              if (((string)row[3]).Equals("BASE TABLE")) {
                dumpTable(cn, (string)row[2]);
              }
            }
          }
        }
      }
    }

   static void dumpTable(SqlConnection cn, string tableName) {
     SqlCommand cmd = new SqlCommand(String.Format("SELECT * FROM [{0}]", tableName), cn);

     SqlDataReader sdr = cmd.ExecuteReader();

     if (sdr.HasRows) {
       Console.WriteLine("writing data for table " + tableName);

       using (TextWriter tw = new StreamWriter(String.Format("{0}.sql", tableName))) {
         tw.WriteLine(String.Format("SET IDENTITY_INSERT [{0}] ON", tableName));
         tw.WriteLine("GO\r\n");

         StringBuilder sbCols = new StringBuilder();

         for (int i = 0; i < sdr.FieldCount; i++) {
          switch (sdr.GetFieldType(i).ToString()) {
             case "System.Byte[]": {
             } break;
             default: {
              if (sbCols.Length > 0) {
                sbCols.Append(", ");
              }

              sbCols.Append(String.Format("[{0}]", sdr.GetName(i)));
             } break;
          }
         }

         tw.WriteLine();

         while (sdr.Read()) {
           tw.Write(String.Format("INSERT INTO [{0}] ({1}) VALUES (", tableName, sbCols.ToString()));

           for (int i = 0; i < sdr.FieldCount; i++) {
            if (sdr.IsDBNull(i)) {
               if (i > 0) {
                tw.Write(',');
               }

               tw.Write("null");
            } else {
             switch (sdr.GetFieldType(i).ToString()) {
             case "System.Int32": {
               if (i > 0) {
                tw.Write(',');
               }

               tw.Write(sdr[i].ToString());
             } break;

             case "System.Decimal": {
               if (i > 0) {
                tw.Write(',');
               }

               tw.Write(sdr[i].ToString());
             } break;

             case "System.Byte[]": {
             } break;

             case "System.DateTime": {
               if (i > 0) {
                tw.Write(',');
               }

               tw.Write(String.Format("'{0}'", ((DateTime)sdr[i]).ToString("M/d/yyyy h:m:s tt")));
             } break;

             default: {
               if (i > 0) {
                tw.Write(',');
               }

              tw.Write(String.Format("'{0}'", sdr[i].ToString().Replace("\'", "\'\'")));
             } break;
             }
           }
           }

           tw.WriteLine(")\r\nGO");
         }

         tw.WriteLine(String.Format("SET IDENTITY_INSERT [{0}] OFF", tableName));
         tw.WriteLine("GO\r\n");

         tw.Close();
       }
     }

     sdr.Close();
   }
}
}

My daughter.

May 2nd, 2008

My daughter never speaks. Most weeks I spend two or three hours total driving her around in my car, and she never speaks, she just looks out the window. I often wonder what she’s thinking about, and today when I got the school newsletter I saw this poem she wrote:

Memories
Selene Means
Team 73

I am a piece of paper;
clean and blank,
now written all over
by others.

Words written in sharpie;
written in ink.
Staying there.
Forever.

Everything is written.
Everything.
Praises.
Compliments.
.Kindness.
Lies.
Hatefulness.
Misery.
Anger.

Sometimes it seems,
just seems,
kindness is outnumbered
by hurt.

But still,
I am a piece of a paper.
A home to these words.
Written in sharpie.
Staying with me.
Forever.
In memories.

It makes my heart ache, because I can’t give her any of my experiences, just watch her form her own. And hope.

The next Willy Mosconi…

April 28th, 2008
Skyler playing pool. Ok, so maybe not, but I can dream, can’t I? After watching the Smoky Mountain Shootout nine ball tournament this weekend, it’s more obvious than ever that I’m probably not cut out to be a top-flight billiards pro. But now that I have the table set up at home, maybe Skyler can be. I’m trying not to push him too hard, but then again, if it worked for Tiger Woods, maybe it’ll work for Skyler…

When will I be able to get Ethernet through my water pipes?

April 25th, 2008

I was exploring options for my friend (and mechanic) who wants to network his office and detached garage when I came across this Netgear product. If this really works, it will save me a huge headache in burying ethernet cable, etc. We’ll just have to see if it performs as advertised.

Windows Vista: making the formerly trivial nearly impossible every day since 2007.

April 18th, 2008

So yesterday my daughter got herself into trouble. Normally, she’s a really well-behaved girl, but last night she made up for a few months of good behaviour with one well-timed failure to obey her mother and some poor choices regarding a school orchestra recital. So to punish her, I’ve taken away her access to the computer for a week. Should be a snap, I think. In every version of Windows since NT I can just go in and disable her account. Child’s play. Wrong, sooo wrong.

I tried several approaches, some obvious, some not, but for some reason Microsoft decided that the account lock out feature is too dangerous for primitive Windows Vista home users. They don’t provide any access to it in the User Accounts applet through the Control Panel, and they’ve disabled access through the Computer Management MMC plug-in. After flailing around for about fifteen minutes (which for such a trivial thing felt like a lifetime), I suddenly remembered the old tried-and-true user account command line tool: NET USER.

Not to be confused with NET USE (which is for accessing shared network drives), NET USER lets you manage Windows user accounts from the command line. Feeling like I was only seconds away from my goal, I started a command prompt and got the command line help for the tool (NET USER /?). I get this output:

NET USER [username [password | *] [options]] [/DOMAIN]
username {password | *} /ADD [options] [/DOMAIN]
username [/DELETE] [/DOMAIN]
username [/TIMES:{times | ALL}]

Arrgh! Nothing remotely resembling the disable command I remember from 10 years ago. But, not willing to give up yet, I try NET HELP USER, and I see this:

(boring stuff elided)

Options Are as follows:

Options Description
----------------------------------------------
/ACTIVE:{YES | NO} Activates or deactivates the account. If
the account is not active, the user cannot
access the server. The default is YES.

(more boring stuff elided)

Victory! So I disabled her account, and it disappeared off of the login screen. She’ll think I deleted it, and I’ll go to sleep tonight satisfied that I have yet again managed to do something in 1/2 hour that could have been done with three mouse clicks a mere three years ago. Sigh.

I’ve seen the future, and the future is … COBOL?

April 15th, 2008

Ok, so in the process of migrating to my new laptop I’ve been forced to look at several of my old projects. I don’t remember where I found the time, but ten years ago I must have written a _lot_ of code. One of the more bizarre projects I did back then was a Y2K solution that involved cross-compiling COBOL programs to Java bytecode. I only implemented about 50% of the COBOL feature set back then, but it still turns out to be 600+ source files and who-knows-how-many thousands of LOCs.
So rather than let it sit on my hard drive gathering virtual dust, I’ve created a Sourceforge project for it. Check out the Universal COBOL Compiler project when you have a chance. There’s nothing out there but a one-paragraph synopsis and a bunch of code in the CVS repository, but at least it’s not hidden on my computer anymore!

Fun with SqlCommand.ExecuteXmlReader()

March 12th, 2008

So I’m coding along this morning, minding my own business, when I run up against a really annoying problem with the XML support in the .Net SQL support classes. What I was trying to do was fetch some values from a support table as a simple XML document with an element for each row. The SQL FOR XML AUTO clause was just the ticket, so I wrote the following code:

SqlCommand cmd = new SqlCommand();
cmd.Connection = cn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT * FROM "
  + "supportTable AS valueName FOR XML AUTO";

XmlReader res = cmd.ExecuteXmlReader();

XmlDocument doc = new XmlDocument();
doc.Load(res);

but when I tried to run the code I got the following error on the doc.Load() call:

This document already has a DocumentElement node.

It really had me irritated, because I’d already used the exact same code in another part of my application without a hitch. So after some research, it turns out that the problem (which is obvious in retrospect) is that if multiple rows are returned, the resulting XML is not a valid document, but a document fragment. Since there is no top-level element, the Load() method chokes when it encounters the second row. Not good.

I found several workarounds on the web, but none that I really cared for. So I resorted to some MS SQL funny business that I’ve used in the past that did the trick. The problem here is that there is no single top-level element to serve as a root element. One possible approach is to abandon the FOR XML AUTO in favor of FOR XML EXPLICIT, but the explicit support is so incredibly complex and unusable that I’d rather cut my own foot off with a rusty tin can lid. So, to save my foot, I resorted to some fun with joins.

The FOR XML AUTO clause will actually create nested elements in the case of joins between tables, so to create a single top-level element for my support table document I created a new table in my DB called dummy. It has a single column (dummy) and a single row with a single value (dummy). The single row part is important, because by changing the SQL statement above slightly, I end up with the nice, valid XML document I want:

SELECT * FROM dummy AS rootElementName,
  supportTable AS valueName FOR XML AUTO

Voila! Now my code works and I can move on to more interesting pursuits, like the development of a Comet service for the masses. Good stuff!

WTF, Steve.

March 4th, 2008

So last night I’m trying to add a number on my iPhone, and all of my contacts disappear! I called the helpful Apple support folks, and they had me resync with my PC and my last backup was restored to the phone. Very disconcerting. I probably lost 20 contacts in the deal.

Push it real good.

March 3rd, 2008

One of the big limitations of web-based programming is the pull nature of the HTTP protocol. Services like Google Talk, etc. get around this by using a concept that’s now called Comet, which allows the server to “push” content to the client, rather than forcing the client to continually ask for updates.

Since I tend to need this functionality more and more in my Ajax-style apps, I think there should be a web service to make this easier. I’ll keep you posted on my progress.