Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Visual Studio.NET / IDE / April 2006

Tip: Looking for answers? Try searching our database.

Reflecting on Types in Project Assemblies

Thread view: 
Enable EMail Alerts  Start New Thread
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 Magazines

Get 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 ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.