Carl’s Geek Notes

September 24, 2007

We Wish You A Merry Geek-mas!

Filed under: Just for Fun, LEGO, Video Games — Carl @ 11:38 pm

Red-letter day.

Halo 3 released at midnight.

Delivered today: LEGO Ultimate Collector’s Series Millennium Falcon.

Perfect.

September 21, 2007

C# string rears its ugly C++ head…

Filed under: C#/.NET, Computers, Microsoft Access, Programming — Carl @ 8:25 am

Here’s one that I won’t forget soon.  I was working on an application that reads data from a Microsoft Access database and builds a string:

string MyString = MyTable.Rows[i]["MyColumn"].ToString();
string NewString = "stuff " + MyString + " some other stuff";

Pretty basic stuff.  I was seeing a problem where NewString’s value was (assuming MyString = “MyStringData”) ”stuff MyStringData”–it cut off everything in the string after MyString.

The issue?  When MyString is assigned, it includes \0 at the end (the string terminator character from C).  .NET apparently stops marshalling at that point.  I corrected it by:

string MyString = MyTable.Rows[i]["MyColumn"].ToString().Replace("", "");

I don’t know whether Access returned it terminated, or the application that originally wrote the data added the string terminator (which I suspect, as I’ve written tomes of code that pull data from Access in my life).  I finally found it by breaking into the application, and when I hovered the cursor over MyString its data was displayed as “MyStringData\0″.  (FYI, it appeared as just “MyStringData” in the string visualizer and Watch window, it was only the hover-over that gave it away.)

September 19, 2007

Using BackgroundWorker to update status

Filed under: C#/.NET, Computers, Programming — Carl @ 8:27 am

I’ve been working on an application the past few days that iterates through directories and processes files within those directories.  I was looking for a good way to keep the user informed of progress, and I decided that using System.ComponentModel.BackgroundWorker was my best solution.

Here’s some relevant code for how I used UserState to keep the user updated of the progress:

namespace BackgroundWorkerSample
{
    public partial class Main : Form
    {
        #region Form Callbacks
        private void btnCancel_Click(object sender, EventArgs e)
        {
            // Let the user know the cancel is processing
            btnCancel.Text = "Cancelling...";
            btnCancel.Enabled = false;

            // Ask the background worker to cancel its process
            if ((null != _FileProcessingWorker) && (_FileProcessingWorker.IsBusy))
            {
                _FileProcessingWorker.CancelAsync();
            }
        }

        private void btnProcess_Click(object sender, EventArgs e)
        {
            // Set up the background worker
            _FileProcessingWorker = new BackgroundWorker();
            _FileProcessingWorker.WorkerReportsProgress = true;
            _FileProcessingWorker.WorkerSupportsCancellation = true;

            // Add the events needed for the background worker
            _FileProcessingWorker.DoWork += new DoWorkEventHandler(_FileProcessingWorker_DoWork);
            _FileProcessingWorker.ProgressChanged += new ProgressChangedEventHandler(_FileProcessingWorker_ProgressChanged);
            _FileProcessingWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_FileProcessingWorker_RunWorkerCompleted);

            // Get the directory to process
            string BasePath = lblDirectoryName.Text;
            DirectoryInfo MyDirectory = new DirectoryInfo(BasePath);
            DirectoryInfo[] MySubDirectories = MyDirectory.GetDirectories();

            // Collect statistics for the progress bar
            int SubDirectoryCount = MySubDirectories.GetLength(0);
            MyProgressBar.Minimum = 0;
            MyProgressBar.Step = 1;
            MyProgressBar.Maximum = SubDirectoryCount;
            MyProgressBar.Value = MyProgressBar.Minimum;
            _LastCounter = 0;

            // Instantiate the background worker and pass the file list
            _FileProcessingWorker.RunWorkerAsync(MySubDirectories);
        }
        #endregion

        #region File processing methods and events
        private void _FileProcessingWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            // Cast the EventArgs to the expected type
            DirectoryInfo[] MySubDirectories = (DirectoryInfo[])e.Argument;

            // Loop through the subdirectories
            for (int i = 0; i < MySubDirectories.GetLength(0); i++)
            {
                DirectoryInfo MySubDirectory = MySubDirectories[i];
                // Keep track of the current directory
                string CurrentDirectory = "Current Directory: " + MySubDirectory.Name;

                // Loop through the matching files
                foreach (FileInfo MyFile in MySubDirectory.GetFiles("*.txt"))
                {
                    // See if the user has requested cancellation
                    if (_FileProcessingWorker.CancellationPending)
                    {
                        e.Cancel = true;
                        return;
                    }
                    // Keep track of the current file
                    string CurrentFile = "Current File: " + MyFile.Name;
                    // Create an object to report the status via UserState
                    string[] CurrentStatus = new string[2];
                    CurrentStatus[0] = CurrentDirectory;
                    CurrentStatus[1] = CurrentFile;
                    // Raise the ProgressChanged event to allow reporting of status
                    _FileProcessingWorker.ReportProgress(i, CurrentStatus);

                    // Do the needed processing on the file
                    Thread.Sleep(500);
                }
            }
        }

        private void _FileProcessingWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Update the progress bar and status labels when the background worker reports progress
            // Cast the EventArgs to the expected type
            if (typeof(string[]) == e.UserState.GetType())
            {
                string[] StatusMsg = (string[])e.UserState;
                if (2 == StatusMsg.GetLength(0))
                {
                    // Update the status labels
                    lblCurrentDirectory.Text = StatusMsg[0];
                    lblStatus.Text = StatusMsg[1];
                }
            }

            // See if the percentage completion needs to be updated
            if (e.ProgressPercentage > _LastCounter)
            {
                _LastCounter = e.ProgressPercentage;
                // Increment the progress bar
                MyProgressBar.PerformStep();
            }
        }

        private void _FileProcessingWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // Keep the display of the last directory and file processed
            lblCurrentDirectory.Text = lblCurrentDirectory.Text.Replace("Current", "Last");
            lblStatus.Text = lblStatus.Text.Replace("Current", "Last");
        }
        #endregion
    }
}

You can download a complete project here.

September 11, 2007

Nerd Test Results

Filed under: Computers, Internet, Just for Fun — Carl @ 7:11 pm


NerdTests.com says I'm a Mega-Dorky Nerd God.  What are you?  Click here!

Woo-hoo! Mega-Dorky Nerd God!  (I really should have done better on history.  But the questions were ancient history, and I’m better at 20th century or American history…)

 Found at Michael “Cool Nerd King” McDaniel’s blog

Error CS0012: Unable to generate a temporary class

Filed under: C#/.NET, Computers, Programming, Web Services, XML — Carl @ 8:56 am

I received an error when serializing a class to XML:

Unable to generate a temporary class (result=1).
error CS0012: The type 'MyType' is defined in an assembly that is not referenced. You must add a reference to assembly 'MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=123412341234'.

Searching the web seemed to indicate that marking fields or properties of the class as non-serialized or adding a reference to the assembly containing the type would correct the problem. The odd thing is, MyType wasn’t part of the class being serialized; there were no fields or properties of MyType within the class!

Fortunately, I knew that it was working before I made some changes this morning. I had added the following:

public static implicit operator MyType(MySerializedClass parameterValue) {...}

to the class. When I changed it to an explicit operator, the error went away.  I’m not sure why this behavior is so, but it solved my problem.

September 4, 2007

Update file location through iTunes COM SDK

Filed under: C#/.NET, Computers, Gadgets, Programming, iPod — Carl @ 2:12 pm

I’m working on a little app to automate some management tasks in iTunes (primarily the ability to locate a “dead” file in iTunes and look for its new location).  I downloaded the Apple iTunes COM SDK documentation, and have built a little C# application to iterate through the library to find dead files.

What I’d like to be able to do is search the music directory for possible matches (artist, album, file size, etc.) and update the iTunes library with the new location.  The code to loop through the library is pretty straightforward:

public static Hashtable GetInvalidFileList(iTunesAppClass iTunesAppObject)
{
bool CloseWhenDone = (null == iTunesAppObject);
if (CloseWhenDone)
{
iTunesAppObject = new iTunesAppClass();
}

Hashtable InvalidFileList = new Hashtable();

// Get the full library
IITLibraryPlaylist MyLibrary = iTunesAppObject.LibraryPlaylist;

// Get the track collection
IITTrackCollection MyTrackCollection = MyLibrary.Tracks;

for (int i = 0; i < MyTrackCollection.Count; i++)
{
// Get the track
IITTrack MyTrack = MyTrackCollection[i + 1];

// See if it's a file track
if (ITTrackKind.ITTrackKindFile == MyTrack.Kind)
{
// Cast it as a file track
IITFileOrCDTrack MyFileTrack = (IITFileOrCDTrack)MyTrack;

// Inspect the location
if (String.IsNullOrEmpty(MyFileTrack.Location))
{
int TrackDatabaseID = MyFileTrack.TrackDatabaseID;
string TrackName = "Artist: " + MyFileTrack.Artist + " Album: " + MyFileTrack.Album + " Song: " + MyFileTrack.Name;
InvalidFileList.Add(TrackDatabaseID, TrackName);
}
}
}

if (CloseWhenDone)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(iTunesAppObject);
iTunesAppObject = null;
GC.Collect();
}

return InvalidFileList;
}

However, updating the location appears to be problematic, as IITFileOrCDTrack.Location is read-only.  It seems that the only way to do this through the COM interface is to add a new track, copy the meta from the old track, add the new track to all playlists that the old track was a member of, and delete the old track.  That will set DateAdded to the time the application was run, and it’s a little kludgey as well.  Is there a better way to do this?

Blog at WordPress.com.