.NET Forum / Visual Studio.NET / IDE / April 2006
Reflecting on Types in Project Assemblies
|
|
Thread rating:  |
Mark Olbert - 16 Apr 2006 23:55 GMT I'm in need of some leads/pointers on handling reflection of project assemblies in VS2005.
Here's an example of what I'd like to do: say I have a custom UITypeEditor which lets me pick Types descended from a class called BaseType. There might be many classes derived from BaseType in the current project. I want to expose those derived Types in the custom UITypeEditor that's referenced by a component in the same project.
If the derived Types were in an assembly referenced in the project this would be pretty straightforward. I could just do a GetTypes() against the referenced assembly and filter the returned values to find the classes derived from BaseType.
The situation is more complex if the derived classes are defined in the current project. All of those Types will be available at run-time, but at design-time they don't already exist. What I'd like to do is something equivalent to GetTypes(), but against the current project assembly.
I've written something that tries to do this, by backtracking through EnvDTE and locating the project assembly. However, if I load that assembly I run into a subtle problem: since the assembly is implicitly included in the project (it is the project's assembly, after all), the Framework does not consider the Types coming from my explicit load as being the same Types as in the project assembly (this, BTW, can be a great source of hair-pulling: if you ever find yourself in need of blowing hours trying to figure out why two Types, each with exactly the same AssemblyQualifiedName, are still different, this is your ticket). That's apparently because the Framework keeps separate lists of implicitly loaded and explicitly loaded assemblies.
Has anyone come up with a solution to this problem? Or know of some references on the Web where I can read up on the problem?
- Mark
Carlos J. Quintero [VB MVP] - 17 Apr 2006 10:17 GMT Hi Mark,
You can not get the Types from the assembly of the project since maybe it does not exist yet (before the first compilation) or maybe is out of date if you have added a new class to the project but you have not recompiled. For those reasons, design-time tools must deal with two kind of entities (I have done this extensively in my add-in for several features):
- Compiled, referenced assemblies, using Reflection.
- Source code of the current project, using the extensibility model of VS (EnvDTE assembly). So, you can get EnvDTE.Project.CodeModel.CodeElements and iterate, or for a given file you use EnvDTE.ProjectItem.FileCodeModel.CodeElements. You can get all the info for a type (fullname, base, etc.) but from an EnvDTE.CodeElement, not from a System.Type. You may need to create your own class that holds internally either a System.Type or a EnvDTE.CodeElement and you use it from your code transparently about its internal nature.
There is an article on my web site ("For add-in developers" section) about how to iterate the code elements of a project.
 Signature Best regards,
Carlos J. Quintero
MZ-Tools: Productivity add-ins for Visual Studio You can code, design and document much faster: http://www.mztools.com
> I'm in need of some leads/pointers on handling reflection of project > assemblies in VS2005. [quoted text clipped - 33 lines] > > - Mark Mark Olbert - 17 Apr 2006 15:42 GMT Thanks, Carlos. Great leads!
- Mark
Mark Olbert - 17 Apr 2006 19:58 GMT Carlos,
A follow-up question, if I may: once I've found the class information through the CodeElement interfaces, is there a way to create a Type at design-time? Calling Type.GetType(<fully qualified name>) doesn't work, but I didn't expect it to, since the assembly isn't built.
The reason I'm asking is that I often find myself needing to access the attributes associated with a class. That's easy to do if I can create a Type, but not so obvious (or easy) if I can't.
- Mark
Mark Olbert - 17 Apr 2006 21:09 GMT Never mind, I found a way to generate the Types: IDesignerHost.GetType(<typename>)
- Mark
Carlos J. Quintero [VB MVP] - 18 Apr 2006 10:24 GMT Just for the record, the EnvDTE.CodeElement has some properties common to all code elements, and for those specific, you can cast the CodeElement to the appropriate type EnvDTE.CodeClass, etc. and access them.
 Signature Best regards,
Carlos J. Quintero
MZ-Tools: Productivity add-ins for Visual Studio You can code, design and document much faster: http://www.mztools.com
> Never mind, I found a way to generate the Types: > IDesignerHost.GetType(<typename>) > > - Mark Steven Cheng[MSFT] - 17 Apr 2006 10:36 GMT Hi Mark,
Glad to see you again. From your description, I understand that you're currently developing a custom UIEditor for VS IDE, and the Editor will try reflecting some certain Types defined in the current Project. Currently you've tried locate the compiled assembly of the VS project and reflect types through the assembly, however, that didn't quite work, correct?
Based on my understanding, for those types (class, interfaces) which defined in the current VS IDE project, since they are still under constructing/editing and not like other referenced class library assemblies which have been fully compiled, we may consider some other approaches to reflect the types in project instead of directly use .net's reflection API on the compiled binary assembly.
Actually, the ENVDTE namespace provides the CodeModel property which expose the current editing source code as a collection of CodeElements. This is something like the CodeDOM namespace in .net framework(which used to dynamically construct .net source code), however, the CodeModel and CodeElements are a separate set of Code manipulation interfaces in VS IDE design-time component. And we can query the types defined in current project from the top Level CodeElement(CodeNamespace ) and loop the child codeElements(sub namespace, class, interfaces....) hierarchically. e.g:
============================ Public Sub TestRun()
Dim prj As Project
prj = DTE.Solution.Projects.Item(2)
Dim elm As CodeElement Dim ns As CodeNamespace
For Each elm In prj.CodeModel.CodeElements
If elm.Kind = vsCMElement.vsCMElementNamespace Then
ns = elm
If ns.Name = prj.Name Then
Dim elm1 As CodeElement
For Each elm1 In ns.Members
If elm1.Kind = vsCMElement.vsCMElementClass Then
Dim cls As CodeClass
cls = elm1
MsgBox(cls.Name & "<---<" & cls.Bases.Item(1).Name)
End If
Next
End If
End If
Next
End Sub =============================
When we get a reference to a certain CodeClass, we can inspect its base types and derives type info. Also, the CodeModel object also let us modify the project's source mode such as Adding new namespace, class..... I think this is a possible approach that we reflecting the types in current editing project. Actually, this is mainly used for VS IDE add-in or macros:
#Discovering Code with the Code Model (Visual Basic) http://msdn2.microsoft.com/en-us/library/tz746te4(vs.80).aspx
In addition, for your current testing through loading the project's compiled output assembly manually, are you using the Assembly.LoadFrom to load the assembly? If so, the type loaded through this context is a bit different from the assembly which get loaded by the application automatically(default context), this may cause the inconsistency behavior when you try comparing types get from the two context. So in such scenario, you can consider comparing or checking type through its Name or FullName.
http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx
http://blogs.gotdotnet.com/suzcook/archive/2003/09/19/57248.aspx
Hope this helps.
Regards,
Steven Cheng Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
Mark Olbert - 17 Apr 2006 15:44 GMT Steven,
Thanks for the thorough reply, and the leads. I look forward to some interesting reading!
- Mark
Steven Cheng[MSFT] - 18 Apr 2006 07:22 GMT Thanks for the response Mark,
Glad that those ones are of assistance. Frankly speaking, doing design-time work is much harder than developing runtime stuff since the document and resource are quite limited. Anyway, please feel free to post here when there is anything we can help.
Regards,
Steven Cheng Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
Mark Olbert - 19 Apr 2006 00:56 GMT A follow-up:
When I scan for CodeClass objects at design-time, the scan includes all the namespaces in the project, both the ones in the project source code and ones included through references to external assemblies. I can use the CodeClass objects to retrieve Type name information.
That's fine, but in order to produce assembly-qualified Type names (i.e., Type names that include the assembly name) I need to know the name of the assembly where a class is defined.
I haven't been able to find a way to do that. Looking at the CodeClass2.ProjectItem.ContainingProject property doesn't work, because it's not defined for external assembly references.
Is there some other property of a CodeClass object, or a related object in the CodeElements namespace, which contains this information? Maybe the CodeElements namespace focuses totally on just namespaces, and ignores the "assembly of origin" of the items it describes.
- Mark
Carlos J. Quintero [VB MVP] - 19 Apr 2006 09:36 GMT There is a CodeElement.InfoLocation property that you can use to know the origin of the code element (project, external, etc.).
Another safer way is instead of using Project.CodeModel, iterate recursively Project.ProjectItems and use ProjectItem.FileCodeModel.
 Signature Best regards,
Carlos J. Quintero
MZ-Tools: Productivity add-ins for Visual Studio You can code, design and document much faster: http://www.mztools.com
>A follow-up: > [quoted text clipped - 19 lines] > > - Mark
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|