Bruce,
Sorry for the delay. I ended up using another method entirely. Your code helped me on the way but was not the solution I ended up using. One problem with the code you gave is that the currently executing assembly is not my project - it is the package in which my designers are hosted.
I ended ended up solving the problem by breaking it into two steps:
- Finding .xsd's in the project
- Determine the type generated by the XSD.
I solved #1 using the DTE object. I enumerated all ProjectItem objects in the current project, searching for those with the "Extension" and "CustomTool" properties. This is the LINQ query I used to search the current project for XSDs:
public static IEnumerable<ProjectItem> FindDataSetSchemas(this Project currProject)
{
// Find all XSDs that will generate strongly typed datasets.
var datasets = from dataset in currProject.ProjectItems.AllItems()
let properties = dataset.Properties.OfType<Property>()
// Only select project items that have Extension = .xsd
// and CustomTool = "MSDataSetGenerator". This will
// give us the project items which will generate
// strongly-typed data sets.
let hasExtension = properties.Any((p) => p.Name == "Extension" &&
String.Compare(".xsd", p.Value.IfNotNull((v) => v.ToString(), String.Empty), true) == 0)
let hasCustomTool = properties.Any((p) => p.Name == "CustomTool" &&
String.Compare("MSDataSetGenerator", p.Value.IfNotNull((v) => v.ToString(), String.Empty), true) == 0)
where hasExtension && hasCustomTool
select dataset;
return datasets;
}
I solved the second problem with the BuildManager property on the VSProject2 interface. BuilderManager has a "BuildDesignTimeOutput" method which will create a temporary assembly for inspection. This was trickier as I had 3 sub-problems to solve:
a. Find the .cs file which implements the strongly typed data set.
b. Find the type defined in that file.
c. Build the assembly and extract the right type.
I solved a) by iterating over the ProjectItems elements of the XSD found above. The "CustomToolOutput" property on that XSD indicates which .cs file implements the data set, so I just search the ProjectItem elements until I find the right one.
I solved b) through the FileCodeModel property on the ProjectItem that I foudn in a). Fortunately, I just needed to look for a class that derived from DataSet, which the CodeClass2 interface has support for.
c) was solved by using BuildDesignTimeOutput to build the file found in a). This method returns an XML string that indicates where the file is found. I couldn't find an example of its use anywhere else, but it was easy to parse. I used this LINQ query to build the path to the assembly generated (where results is the string returned:
string results = mgr.BuildDesignTimeOutput(xsdDesigner.Name);
XElement element = XElement.Parse(results);
string assemblyPath = (from e1 in element.Descendants("Application")
from e2 in element.Descendants("Assembly")
select Path.Combine(e1.Attribute("private_binpath").Value,
e2.Attribute("codebase").Value)).FirstOrDefault();
Finally, I searched the generated assembly for the type I discovered in b). By repeating this process for each XSD in the project, I was able to discover all the types exposed.
If there is a better or less convoluted way to do it, I'd love to know!
Justin