简体   繁体   中英

T4 templates - refreshing solution explorer in Visual Studio 2010 from one application domain to another

<#@ template debug="true" hostspecific="true" language="C#" #>  
<#@ assembly name="EnvDTE80" #>  
<#@ include file="T4Toolbox.tt" #>  
<#  
IServiceProvider serviceProvider = (IServiceProvider)this.Host;  
EnvDTE80.DTE2 dte = (EnvDTE80.DTE2)serviceProvider.GetService(typeof(EnvDTE.DTE));  
//add a file to a project and add its dependupon build property. 
//I want to refresh teh solution explorer window to show the hierarchy between 2 files
//You will see this kind of relationship between Forms.cs and Form1.Designer.cs files.

EnvDTE.UIHierarchy solExplorer = dte.ToolWindows.SolutionExplorer;  
solExplorer.Parent.Activate();  
dte.ExecuteCommand("View.Refresh", string.Empty); 

I am trying to refresh the solution explorer's tool window so I can see the newly created files nested. I know that T4 templates are executed in one application domain and calls are made into Visual Studio Appdomain using remoting. I am getting this error about serialization. So is there a way I can refresh solution explorer tool window (solExplorer.Parent) by first activating it (I was told).

Type 'Microsoft.VisualStudio.Platform.WindowManagement.DTE.WindowBase' in Assembly 'Microsoft.VisualStudio.Platform.WindowManagement, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' is not marked as serializable.

Update : Based on Gereth's comment.
Thanks, Gereth I tried this, but it returns COMException,
I don't have an error about Serialization of Microsoft.VisualStudio.Platform.WindowManagement.DTE.WindowBase class and Activate method seems to have succeded. The error is now on dte.ExecuteCommand method .

//object dteObject = GetCOMService(serviceProvider, typeof(EnvDTE80.DTE2)); 
object dteObject1 = GetCOMService(serviceProvider, typeof(EnvDTE.DTE));
EnvDTE80.DTE2 dte = (EnvDTE80.DTE2)dteObject1;

COMException raised when executing this line:

dte.ExecuteCommand("View.Refresh", string.Empty);  

Message "Error HRESULT E_FAIL has been returned from a call to a COM component."
Source "EnvDTE80"
StackTrace "at EnvDTE80.DTE2.ExecuteCommand(String CommandName, String CommandArgs)
ErrorCode -2147467259

What to try next?

Thanks Rad

There's a section of DTE commands that don't stay with COM marshaling once the CLR notices that both ends of the remoting pipe are written in managed code. However, given these components are not actually set up to be .Net remotable, but ARE set up to be COM remotable, this type of error occurs.

In general, if the COM marshaling on the particular DTE object in question is set up correctly, you should be able to use the following code to get moving again. Call it instead of your vanilla service call to get the DTE.

public static Object GetCOMService(IServiceProvider provider, Type type)
{
    Object ret;
    ret = provider.GetService(type);
    if (ret == null)
    {
        return ret;
    }

    try
    {
        return Marshal.GetObjectForIUnknown(Marshal.GetIUnknownForObject(ret));
    }
    catch (Exception)
    {
        return ret;
    }
}

I finally got this to work by unloading and reloading the project. The project has to be selected in the Solution Explorer otherwise you'll get a COMException.

IServiceProvider hostServiceProvider = (IServiceProvider)Host;
// see @GarethJ's answer for the following
DTE2 dte2 = GetCOMService(hostServiceProvider, typeof(EnvDTE.DTE)) as DTE2;
object dteObject1 = GetCOMService(hostServiceProvider, typeof(EnvDTE.DTE));
EnvDTE80.DTE2 dte2 = (EnvDTE80.DTE2)dteObject1;

var solExplorer = dte2.ToolWindows.SolutionExplorer;  
solExplorer.Parent.Activate();
ProjectItem containingProjectItem = dte2.Solution.FindProjectItem(templateFile);
Project project = containingProjectItem.ContainingProject;

UIHierarchy solExplorerHierarchy = solExplorer.Parent.Object as UIHierarchy;
string projectSolutionPath = Path.Combine(dte2.Solution.Properties.Item("Name").Value.ToString(), project.Name);
var projectItem = solExplorerHierarchy.GetItem(projectSolutionPath);
projectItem.Select(vsUISelectionType.vsUISelectionTypeSelect);
dte2.ExecuteCommand("Project.UnloadProject", ""); 
dte2.ExecuteCommand("Project.ReloadProject", "");

And then any newly created nested items appear. I'm using VS2012 and T4Toolbox 11.7.

Thank you Gareth. Your solution works very well. I have extended my "GetService" method:

    private T GetService<T>(Type type) where T : class
    {
        IServiceProvider hostServiceProvider = (IServiceProvider)Host;
        if (hostServiceProvider == null)
        {
            throw new Exception("Host property returned unexpected value (null)");
        }
        object serviceObj = hostServiceProvider.GetService(type);
        try
        {
            serviceObj = Marshal.GetObjectForIUnknown(Marshal.GetIUnknownForObject(serviceObj));
        }
        catch (Exception) { }
        T service = serviceObj as T;
        if (service == null)
        {
            throw new Exception("Unable to retrieve service");
        }
        return service;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM