#c# #visual-studio-2010 #t4 #t4-toolbox
#c# #visual-studio-2010 #t4 #t4-toolbox
Вопрос:
<#@ 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);
Я пытаюсь обновить окно инструмента Solution Explorer, чтобы я мог видеть вложенные вновь созданные файлы. Я знаю, что шаблоны T4 выполняются в одном домене приложения, а вызовы выполняются в Appdomain Visual Studio с использованием удаленного взаимодействия. Я получаю эту ошибку о сериализации.
Итак, есть ли способ обновить окно инструмента обозревателя решений (solExplorer.Родительский), сначала активировав его (мне сказали).
Введите ‘Microsoft.VisualStudio.Платформа.WindowManagement.DTE.WindowBase’ в сборке ‘Microsoft.VisualStudio.Платформа.WindowManagement, Version = 10.0.0.0, Culture = нейтральный, PublicKeyToken =b03f5f7f11d50a3a’ не помечен как сериализуемый.
Обновление: основано на комментарии Герета.
Спасибо, Герет, я пробовал это, но он возвращает COMException,
у меня нет ошибки о сериализации Microsoft.VisualStudio.Платформа.Класс WindowManagement.DTE.WindowBase и метод Activate, похоже, завершились успешно. Теперь ошибка связана с dte.Метод ExecuteCommand.
//object dteObject = GetCOMService(serviceProvider, typeof(EnvDTE80.DTE2));
object dteObject1 = GetCOMService(serviceProvider, typeof(EnvDTE.DTE));
EnvDTE80.DTE2 dte = (EnvDTE80.DTE2)dteObject1;
При выполнении этой строки возникает исключение COMException:
dte.ExecuteCommand("View.Refresh", string.Empty);
Сообщение «Ошибка HRESULT E_FAIL была возвращена из вызова COM-компонента».
Источник «EnvDTE80»
StackTrace «в EnvDTE80.DTE2.ExecuteCommand(строка CommandName, Строка commandArgs)
Код ошибки -2147467259
Что попробовать дальше?
Спасибо Рад
Ответ №1:
Есть раздел команд DTE, которые не остаются при маршалировании COM, как только среда CLR замечает, что оба конца канала удаленного взаимодействия записаны в управляемом коде. Однако, учитывая, что эти компоненты фактически не настроены быть .Net remotable, но настроены на COM remotable, возникает ошибка такого типа.
В общем, если маршалинг COM для конкретного объекта DTE, о котором идет речь, настроен правильно, вы сможете использовать следующий код, чтобы снова начать движение. Вызовите его вместо вашего ванильного вызова службы, чтобы получить 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;
}
}
Комментарии:
1. Спасибо, Герет, я пробовал это, но он возвращает COMException,` // объект dteObject = GetCOMService(ServiceProvider, typeof(EnvDTE 80.DTE2)); объект dteObject1 = GetCOMService(ServiceProvider, typeof(EnvDTE.DTE)); EnvDTE80.DTE2 dte = (EnvDTE80.DTE2) dteObject1; Сообщение «ОшибкаВозвращен результат HRESULT E_FAIL из вызова COM-компонента «. Источник «EnvDTE80» StackTrace «в EnvDTE80.DTE2.ExecuteCommand(String CommandName, String commandArgs) Код ошибки -2147467259 ` Что попробовать дальше?
2. Это позор. В прошлом, когда команды DTE не выполнялись для меня как синхронные вызовы, мне повезло с отправкой их в очередь диспетчера для последующего выполнения в потоке пользовательского интерфейса через IVsUIShell. PostExecCommand() .
3. Должен ли я делать что-то вроде показанного здесь: [ссылка] hg01.codeplex.com/forks/damianh/405commandlineversionrange/file/… внутри метода ShowUpdatesTabInExtensionManager. Должен ли я использовать таймер и проверять его событие OnTimerTick, чтобы выяснить, завершено ли оно, прежде чем продолжить работу с оставшимся кодом генерации T4. Т.е. мне нужно разделить генерацию кода на несколько methods.Tx.
4. Это не должно быть необходимым — я просто предлагал заменить синхронный вызов команды вызовом для ее публикации.
Ответ №2:
Я, наконец, заставил это работать, выгрузив и перезагрузив проект. Проект должен быть выбран в обозревателе решений, иначе вы получите 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", "");
А затем появляются все вновь созданные вложенные элементы. Я использую VS2012 и T4Toolbox 11.7.
Ответ №3:
Спасибо, Гарет. Ваше решение работает очень хорошо. Я расширил свой метод «GetService»:
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;
}