3
<#@ 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

shamp00
  • 11,106
  • 4
  • 38
  • 81
Rad
  • 933
  • 1
  • 15
  • 32

3 Answers3

2

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;
    }
}
GarethJ
  • 6,496
  • 32
  • 42
  • Thanks, Gereth I tried this, but it returns COMException,` //object dteObject = GetCOMService(serviceProvider, typeof(EnvDTE80.DTE2)); object dteObject1 = GetCOMService(serviceProvider, typeof(EnvDTE.DTE)); EnvDTE80.DTE2 dte = (EnvDTE80.DTE2)dteObject1; 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? – Rad Oct 17 '11 at 08:00
  • That's a shame. In the past, when DTE commands have failed for me as synchronous calls, I've had luck with posting them to the dispatcher queue for later execution on the UI thread via IVsUIShell.PostExecCommand(). – GarethJ Oct 17 '11 at 23:03
  • Do I have to do something like shown here: [link]https://hg01.codeplex.com/forks/damianh/405commandlineversionrange/file/700b7e8b4b5c/src/VisualStudio/ProductUpdate/ProductUpdateService.cs inside ShowUpdatesTabInExtensionManager method. Do I have to use a timer and check its OnTimerTick event to figure out if it is finished before I continue with the remaining T4 generation code. I.e I have to split my code generation across multiple methods.Tx. – Rad Oct 18 '11 at 13:25
  • That shouldn't be necesssary - I was just suggesting replacing the synchronous call to the command with a call to Post it. – GarethJ Oct 18 '11 at 17:06
0

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.

shamp00
  • 11,106
  • 4
  • 38
  • 81
0

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;
    }
schlegel11
  • 363
  • 4
  • 8