Fortunately with the help of .NET 3.5, C# and LINQ to XML it's a piece of cake to find out. I was recently able to whip up this small little utility in less time then it'll take me to blog about it.
Basically it's a simple layout of all entries, used entries and orphaned entries. Let's look at how easy .NET makes it to figure out which is which:
1: private void Process( string documentPath )
3: var document = XDocument.Load(documentPath);
4: var entries = from row in document.Descendants("row")
5: where row.Parent.Attribute("name").Value == "ISString"
6: select row.Elements().First().Value;
8: List<string> stringEntries = entries.ToList();
9: document.Descendants("table").Where(w => w.Attribute("name").Value == "ISString").Remove();
10: string documentText = document.ToString();
12: foreach (var entry in stringEntries)
14: listBoxAll.Items.Add( entry );
15: if( documentText.Contains( entry ))
16: listBoxUsed.Items.Add( entry );
The process is fairly straight forward. Load the ISM into the DOM; Query a list of ISString entry names; remove the ISString table from the DOM and then finally iterate through the list of names to see if the document contains a reference. It wouldn't take but a few additional lines of code to let you select the entries to remove and automate the rest.
It takes a little time getting used to LINQ to XML but it really rocks once you get the hang of it.
Note: The above example doesn't take into consideration entries that might be used by InstallScript as I don't use InstallScript anymore.