Установка самописных компонентов (part III)

Jan 20, 2010 18:02

Создание новых компонентов
Теперь, когда созданы панели для новых компонентов, время создать реальные объекты. Для этого надо активировать соответствующую панель и создать элемент. Тоже не тяжело. Добавляем этот код после создания панелей.

copy to clipboardподсветка кода
  1. foreach (var control in controls) {  
  2.     var tab = GetToolBoxTab (tabs, control.TabName);  
  3.   
  4.     if (tab != null && !control.IsToolBoxTab) {  
  5.         tab.Activate();  
  6.         tab.ToolBoxItems.Add("anyName", control.Control, vsToolBoxItemFormat.vsToolBoxItemFormatDotNETComponent);  
  7.     }  
  8. }  
Здесь есть небольшие нюансы: когда добавляете новый элемент с помощью кода tab.ToolBoxItems.Add, второй параметр - объект. Если передать строку, то она будет распознана как путь до сборки и все публичные компоненты оттуда будут добавлены. Если передать тип, то только этот объект будет добавлен. Когда программа закончит добавлять компоненты к панели(ям), надо будет закрыть соединение c DTE2.

copy to clipboardподсветка кода
  1. dte.Quit();  
Вот и всё, для случая запущенной студии. Установка при незапущенной студии
Думаю, что вам не захочется просить пользователя запустить Visual Studio и создать проект WinForm, перед тем как запустить установщик. И далее будет описано, как сделать это все автоматически. Для начала нам надо создать новый экземпляр студии, для того чтобы продолжить работу. Думаю, что лучшим местом для этого куска кода будет метод GetDesignTimeEnvironment.

copy to clipboardподсветка кода
  1. if (result == null) {  
  2.     //Open a new VS.Net  
  3.     var type = Type.GetTypeFromProgID (progID);  
  4.   
  5.     result = (DTE2) Activator.CreateInstance(type, true);  
  6. }  
Как я уже ранее говорил, необходимо создать WinForm проект для того, чтобы все нормально установить. И это возможно сделать кодом тоже. Открываем метод RegisterControls и добавляем в начало.

copy to clipboardподсветка кода
  1. if (!alreadyCreatedDTE) {  
  2.      var tmpFile = Path.GetFileNameWithoutExtension(Path.GetTempFileName ());  
  3.      var tmpDir = string.Format("{0}{1}", Path.GetTempPath(), tmpFile);  
  4.      var solution = dte.Solution as Solution2;  
  5.      var templatePath = solution.GetProjectTemplate("WindowsApplication.zip", "CSharp");  
  6.   
  7.      solution.AddFromTemplate(templatePath, tmpDir, "dummyproj", false);  
  8. }  
На мой взгляд код тут самодокументирующийся и в комментариях не нуждается. Но не забываем закрыть солюшен перед концом программы.

copy to clipboardподсветка кода
  1. dte.Solution.Close(false);  
Параметр является ответом на вопрос, сохранять проект или нет. Теперь новые компоненты будут установлены независимо от того, запущена студия или нет. К сожалению СОМ ошибки могут нарушить всю идиллию. Во время разработки я получал тонны ошибок, которые сообщали, что СОМ-объект все еще занят и не может быть вызван. Поиски в интернете дали ответ на то, как это избежать. Как избежать СОМ ошибок
Основная идея в том, чтобы отфильтровывать сообщения от СОМ-объектов и получать только те, на которые мы подпишемся. Необходимо объявить интерфейс IOleMessageFilter

copy to clipboardподсветка кода
  1. [ComImport, Guid("00000016-0000-0000-C000-000000000046"),  
  2. InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]  
  3. internal interface IOleMessageFilter {  
  4.     [PreserveSig]  
  5.     int HandleInComingCall(  
  6.             int dwCallType,  
  7.             IntPtr hTaskCaller,  
  8.             int dwTickCount,  
  9.             IntPtr lpInterfaceInfo);  
  10.   
  11.     [PreserveSig]  
  12.     int RetryRejectedCall(  
  13.             IntPtr hTaskCallee,  
  14.             int dwTickCount,  
  15.             int dwRejectType);  
  16.   
  17.     [PreserveSig]  
  18.     int MessagePending(  
  19.            IntPtr hTaskCallee,  
  20.            int dwTickCount,  
  21.            int dwPendingType);  
  22. }  
И сделать реализацию к нему.

copy to clipboardподсветка кода
  1. public class MessageFilter : IOleMessageFilter {  
  2.     //  
  3.     // Class containing the IOleMessageFilter  
  4.     // thread error-handling functions.  
  5.     // Start the filter.  
  6.   
  7.     public static void Register()  {  
  8.         IOleMessageFilter newFilter = new MessageFilter();  
  9.         IOleMessageFilter oldFilter = null;  
  10.         CoRegisterMessageFilter (newFilter, out oldFilter);  
  11.     }  
  12.   
  13.     // Done with the filter, close it.  
  14.   
  15.     public static void Revoke()  {  
  16.         IOleMessageFilter oldFilter = null;  
  17.         CoRegisterMessageFilter(null, out oldFilter);  
  18.     }  
  19.   
  20.     //  
  21.     // IOleMessageFilter functions.  
  22.     // Handle incoming thread requests.  
  23.     int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo) {  
  24.         //Return the flag SERVERCALL_ISHANDLED.  
  25.         return 0;  
  26.     }  
  27.   
  28.     // Thread call was rejected, so try again.  
  29.     int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType) {  
  30.         if (dwRejectType == 2) {  
  31.             // flag = SERVERCALL_RETRYLATER.  
  32.             // Retry the thread call immediately if return >=0 &  
  33.             // <100.  
  34.             return 99;  
  35.         }  
  36.   
  37.        // Too busy; cancel call.  
  38.        return -1;  
  39.     }  
  40.   
  41.     int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType) {  
  42.         //Return the flag PENDINGMSG_WAITDEFPROCESS.  
  43.         return 2;  
  44.     }  
  45.   
  46.     // Implement the IOleMessageFilter interface.  
  47.     [DllImport("Ole32.dll")]  
  48.     private static extern int  
  49.          CoRegisterMessageFilter (IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);  
  50.     }  
В итоге должно получиться что-то в духе:

copy to clipboardподсветка кода
  1. MessageFilter.Register();  
  2. RegisterControls(dte, alreadyCreated, controls);  
  3. MessageFilter.Revoke();  
С этим кодом все у вас будет в ажуре! =) Я тестировал на семерке, ХР и все работало замечательно. Я надеюсь получить ваши комментарии, вопросы, предложения. Кстати да, в процессе написания я заметил места, где можно улучшить код, но оставил его до лучших времен.

Source code


Hard’n’heavy!

visual studio, custom control

Previous post Next post
Up