Feeling like Homer Simpson…
Sorry for being out of touch for so long. We had to get ready for beta in a foreign country and we’re rocking and rolling toward full production. (I now have less hair!) It’s all fun, but I had an issue that came up and the MSDN forums indicated it could be a duplicate Project GUID. Since we create our own project files (we have special needs J), I thought that was very possible. So in 15 minutes, I threw together a utility that would walk every directory and read every project file and look for duplicates. Seemed simple enough, however, my
DocumentElement.SelectSingleNode("//Project/PropertyGroup/ProjectGuid")
was always returning null!
The project file was simple enough:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="2.0">
<PropertyGroup>
<BinplaceToBinariesFolder>true</BinplaceToBinariesFolder>
<BinplaceToSymbolsFolder>true</BinplaceToSymbolsFolder>
</PropertyGroup>
<Import Project="$(NTMAKEENV)My.Build.settings" />
<PropertyGroup>
<ProjectGuid>{71440C14-ED89-4FF0-B1EF-3A217CC6887D}</ProjectGuid>
<OutputType>library</OutputType>
<RootNamespace>My.Namespace.MyAssembly</RootNamespace>
<AssemblyName>MyAssembly</AssemblyName>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>….My.snk</AssemblyOriginatorKeyFile>
<StyleCopEnabled>true</StyleCopEnabled>
<DocumentationFile>$(IntermediateOutputPath)$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
… rest goes here …
</Project>
And the code to retrieve the ProjectGuid was simple enough as well:
// Load the project file
XmlDocument doc = new XmlDocument();
doc.Load(fileName);
// retrieve the Project Guid
XmlNode node = doc.DocumentElement.SelectSingleNode("//Project/PropertyGroup/ProjectGuid");
// do something here . . .
OK, I thought, I’m a smart guy, I know it’s a namespace problem. I can see in the project that there is a default namespace and according to the MSDN documentation for XmlNamespaceManager, to make a default namespace you use:
String.Empty |
The empty namespace. This value can be assigned to a prefix. For example, xmlns="" defines the default namespace to be the empty namespace. |
So I updated the code as follows:
// Load the project file
XmlDocument doc = new XmlDocument();
doc.Load(fileName);
// create a default namespace
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("", "http://schemas.microsoft.com/developer/msbuild/2003");
// get the Project Guid
XmlNode node = doc.DocumentElement.SelectSingleNode("//Project/PropertyGroup/ProjectGuid", nsmgr);
// do something here . . .
But still nothing! Thanks to a co-worker who whacked me in the back of the head saying “We dealt with this before” (to be honest, he had forgotten at first too!). The docs are incorrect when using a default namespace. You do have to give it a prefix. So the correct code was:
// Load the project file
XmlDocument doc = new XmlDocument();
doc.Load(fileName);
// create a default namespace
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("a", "http://schemas.microsoft.com/developer/msbuild/2003");
// get the Project Guid
XmlNode node = doc.DocumentElement.SelectSingleNode("//a:Project/a:PropertyGroup/a:ProjectGuid", nsmgr);
Now this is all fine and dandy and I’m glad I had it re-implanted into my brain cells. However, it’s not all that performant. Now, for what I was doing (building a utility), it didn’t matter as much, but for production quality code, performance does matter. One of the reasons it’s not too speedy is that it loads the XML into the DOM. To use an XmlReader, we could use the code below:
// create a reader
XmlTextReader xmlReader = new XmlTextReader(fileName);
// read until…
while (xmlReader.Read())
{
switch (xmlReader.NodeType)
{
// use the NodeType to look for an element
case XmlNodeType.Element:
// we find an element named "ProjectGuid"
if (xmlReader.Name == "ProjectGuid")
{
// use xmlReader.ReadString() to get the value of the element
// do something here
}
break;
}
}
}
Pretty simple stuff, but yet can trip you up and make you waste time (which none of us have enough of!).
Hope that helps and happy coding!