locked
Getting a list of controls with a Text Template RRS feed

  • Question

  • Setup: I am trying to create a Text Template in Visual Studio 2010. I am running Windows 7 and the language is C#.

    Problem: I am trying to create a Text Template to loop through all projects in a solution and get a list of all controls that are on any form within the solution. I have written a template that I think should get the components using the IDesignerHost, but I am getting the following error: Running transformation: System.InvalidCastException: Unable to cast object of type 'System.__ComObject' to type 'System.ComponentModelCollection'. The line of code where the error occurs in indicated below. I have just included the function that throws the error to keep it brief. But the other function just loop through all projects in a solution and then all ProjectItems for that solution.

    Question: Am I doing something wrong here to get this error? Or is there a better way to achieve my goal?

    Code:

    <#+ void ParseDesigner(ProjectItem projectItem)
    
    {
    
    	System.ComponentModel.ComponentCollection colComponents;
    
    	if (projectItem.FileCodeModel != null)
    
    	{
    
    		EnvDTE.Window window = projectItem.Open(EnvDTE.Constants.vsViewKindDesigner);
    
    		if (window.Object != null)
    
    		{
    
    			this.WriteLine("//Caption: " + window.Caption + " Object Kind:" + window.Object.GetType().ToString());
    
    			System.ComponentModel.Design.IDesignerHost designHost = (System.ComponentModel.Design.IDesignerHost)window.Object;
    
    
    
    			if (designHost != null && designHost.RootComponentClassName != null)
    
    			{
    
    				this.WriteLine("//" + designHost.RootComponentClassName + " " + projectItem.Name);
    
    				System.ComponentModel.IContainer container = designHost.Container;
    
    				if (container != null)
    
    				{
    
    //ERROR: System.InvalidCastException
    
    					colComponents = (System.ComponentModel.ComponentCollection)container.Components;
    
    				}
    
    			}
    
    		}
    
    		window.Close(vsSaveChanges.vsSaveChangesNo);
    
    	}
    
    } #>

     

    Tuesday, November 30, 2010 4:28 PM

Answers

  • In that case, it's very likely that the marshalling of one of the DTE interfaces is not quite working over T4's remoting, so COM needs to be forced:

    Can you try wrapping your DTE objects in the followig call pair:

        foo= Marshal.GetObjectForIUnknown(Marshal.GetIUnknownForObject(<foo>));

    You could proobably try the container that's failing first and move up the call chain if that isn't enough.


    Gareth Jones - Developer Architect - T4, UML Designer Extensibility [MSFT]
    Friday, February 11, 2011 10:02 PM

All replies

  • Hi Sniph,

     

    Thanks for your post.

    I went through the code but cannot reproduce your issue.

    I suspect the exception doesn't throw from these lines.

    Could you please provide more detail or share the project via SkyDrive? Which can help us figure this out easier.

    Looking forward to your reply.

     

    Best Regards,

    Ziwei Chen


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Thursday, December 2, 2010 9:48 AM
  • Hi Sniph,

     

    Is it resolved?

     

    Best Regards,

    Ziwei Chen

     

    MSDN Subscriber Support in Forum
    If you have any feedback on our support, please contact msdnmg @ microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Monday, December 6, 2010 8:46 AM
  • Hi,

    Thank you for the reply and sorry it took so long to get back to you. I was expecting an email when someone replied to the post... Anyway, it is not resolved. I need to strip some things out of my project since this is for work and make sure I am clear to share it. I also have not used SkyDrive before, so I will need to check that out as well. I will see what I can work up over the next few days to share in order to see if you can reproduce. Thanks for helping me with this.

    Brian Sneed

    Monday, December 6, 2010 9:37 PM
  • I have included the entire Text Template here so you can try to reproduce. Here are the steps that I have taken to create the project and reproduce the error. I am trying to get the name and type of control on the form. I appreciate any guidance on this.

    1. Create a new project in Visual Studio 2010
    2. Add a new form to the project.
    3. Add a text box control on the form
    4. Add a new text template
    5. Use the code below in the text template.
    6. Click save to run the template.
    7. I get this error message on line 58 -

                    colComponents = (System.ComponentModel.ComponentCollection)container.Components;

    Error Message

    Error 1 Running transformation: System.InvalidCastException: Unable to cast object of type 'System.__ComObject' to type 'System.ComponentModel.ComponentCollection'.
       at System.StubHelpers.InterfaceMarshaler.ConvertToManaged(IntPtr pUnk, IntPtr itfMT, IntPtr classMT, Int32 flags)
       at System.ComponentModel.IContainer.get_Components()

    <#@ template debug="true" hostspecific="true" language="C#" #>
    <#@ output extension=".txt" #>
    
    <#@ assembly name="EnvDTE" #>
    <#@ import namespace="EnvDTE" #>
    <#@ import namespace="System.Diagnostics" #>
    <#@ assembly name="System.Core" #>
    <#@ import namespace="System" #>
    
    class MyTestClass {
    
    
    
    <#
    	IServiceProvider hostServiceProvider = (IServiceProvider)Host;
    	EnvDTE.DTE dte = (DTE)hostServiceProvider.GetService(typeof(DTE));
    	Solution sol = dte.Solution;
    	foreach (Project project in sol.Projects)
    	{
    		ParseProjectItems(project.ProjectItems);
    	}
    #>
    
    }
    
    
    <#+ void ParseProjectItems(ProjectItems projectItems)
    	{
    		if (projectItems != null)
    		{
    			foreach (ProjectItem item in projectItems)
    			{
    				ParseDesigner(item);
    			}
    		}
    
      } #>
    
    
    <#+ void ParseDesigner(ProjectItem projectItem)
    {
    	System.ComponentModel.ComponentCollection colComponents;
    	if (projectItem.FileCodeModel != null)
    	{
    		EnvDTE.Window window = projectItem.Open(EnvDTE.Constants.vsViewKindDesigner);
    		if (window.Object != null)
    		{
    			this.WriteLine("Caption: " + window.Caption + " Object Kind:" + window.Object.GetType().ToString());
    			System.ComponentModel.Design.IDesignerHost designHost = (System.ComponentModel.Design.IDesignerHost)window.Object;
    
    			if (designHost != null && designHost.RootComponentClassName != null)
    			{
    				this.WriteLine(designHost.RootComponentClassName + "  " + projectItem.Name);
    				System.ComponentModel.IContainer container = designHost.Container;
    				if (container != null)
    				{
    					colComponents = (System.ComponentModel.ComponentCollection)container.Components;
    				}
    			}
    		}
    		window.Close(vsSaveChanges.vsSaveChangesNo);
    	}
    } #>
    
    Thursday, December 9, 2010 9:42 PM
  • Help anyone? Thanks for your time.

     

    Tuesday, January 4, 2011 5:44 PM
  • Can anyone help with this?

     

    Tuesday, February 8, 2011 8:37 PM
  • Sniph, does the same basic traversal code run correctly in a macro or a VS add-in?  I'm trying to isolate whether it's a T4 issue or an issue with the use of the APIs more generally.
    Gareth Jones - Developer Architect - T4, UML Designer Extensibility [MSFT]
    Wednesday, February 9, 2011 3:07 AM
  • This seems to work fine as a macro.

     

    Friday, February 11, 2011 3:06 PM
  • In that case, it's very likely that the marshalling of one of the DTE interfaces is not quite working over T4's remoting, so COM needs to be forced:

    Can you try wrapping your DTE objects in the followig call pair:

        foo= Marshal.GetObjectForIUnknown(Marshal.GetIUnknownForObject(<foo>));

    You could proobably try the container that's failing first and move up the call chain if that isn't enough.


    Gareth Jones - Developer Architect - T4, UML Designer Extensibility [MSFT]
    Friday, February 11, 2011 10:02 PM