CustomTool dla Visual Studio
Ostatnio, w kontekście
zine #2, dużo pisaliśmy o tym jak generować kod. Mnie ostatnio zainteresowała
kwestia integracji generatora z Visual Studio. Wiedziałem, że jest to
możliwe patrząc chociażby na generator zasobów, czy typowanych datasetów. Wiedziałem,
że VS dla danego pliku w solucji udostępnia właściwość CustomTool, w której możemy podać nazwę generatora, który na
podstawie tego pliku wygeneruje nowy plik i podłączy go jako potomka do pliku
źródłowego w drzewie projektów. Trzeba było się tylko dowiedzieć jak taki
custom tool napisać. Okazało się, że nie jest to takie skomplikowane i da się
napisać w postaci kodu zarządzanego.
Visual Studio do komunikacji z
custom toolem wykorzystuje COMa. Jak się dowiedziałem kod opakowujący
odpowiednie interfejsy był w SDK do Visual Studio 2002 dostępny w pliku Microsoft.VSDesigner.dll
natomiast w późniejszych wydaniach został schowany. Można go natomiast ściągnąć
ze strony [1]. Ponadto wymagane jest Visual Studio SDK, które dostarcza kilku
wymaganych bibliotek. Mając te wszystkie elementy zadanie implementacji jest
banalnie proste.
Custom tool to klasa, która
dziedziczy po klasie BaseCodeGeneratorWithSite. Dodatkowo klasa ta musi być
oznaczona dwoma atrybutami: Guid oraz ComVisible. Poniżej znajduje się
przykładowa implementacjia:
[Guid("408ABA1A-9AD7-41a9-8ECE-4B35C4D5208D")]
[ComVisible(true)]
public class Testowy : BaseCodeGeneratorWithSite
{
public override string GetDefaultExtension()
{
return ".xml";
}
protected override byte[] GenerateCode(string inputFileName, string inputFileContent)
{
string generatedCode = "Wygenerowałeś mnie :)";
return Encoding.ASCII.GetBytes(generatedCode);
}
}
Najważniejsza metoda to
GenerateCode. Jest to metoda, którą wywołuje Visual Studio generując plik.
Zwracana jest tablica bajtów, która zapisywana jest do generowanego pliku.
Nazwa generowanego pliku jest zawsze taka sama jak nazwa pliku źródłowego.
Możemy wpłynąć na rozszerzenie nadpisując metodę GetDefaultExtension. To
wszystko co trzeba zrobić, aby zaimplementować custom toola.
Drugim krokiem jest jego
rejestracja. Visual Studio wczytuje informacje o dostępnych custom toolach z
rejestru i tam musimy umieścić swój wpis. Oto zawartość pliku, który tworzy
odpowiedni wpis w rejestrze:
Windows Registry Editor Version
5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Generators\<identyfikator
projektu>\<nazwa custom toola>]
@="<nazwa custom toola>"
"CLSID"="<id toola>”
"GeneratesDesignTimeSource"=dword:00000001
Należy w tym miejscu wytłumaczyć
kilka elementów:
<identyfikator projektu> to
guid wskazujący na typ projektu. {fae04ec1-301f-11d3-bf4b-00c04f79efbc} to guid
odnoszący się do projektów w języku C#. Gdybyśmy chcieli dodać generator dla
VB.Net to musielibyśmy umieścić nasz wpis w gałęzi {164b10b9-b200-11d0-8c61-00a0c91e29d5}
<nazwa custom toola> to
nazwa którą będziemy wpisywali w VS w pole CustomTool.
<id toola> to guid, którym
opatrzona jest klasa, którą zaimplementowaliśmy, czyli
{408ABA1A-9AD7-41a9-8ECE-4B35C4D5208D}
Ostatni parametr wskazuje na to,
czy VS ma wygenerować plik i dołączyć go do projektu.
Ostatnim krokiem, który musimy
wykonać jest zarejestrowanie naszej klasy w systemie, aby można go było używać
jako obiektu COM. Wykorzystujemy do tego narzędzie tlbexp.exe do
wyekstrahowania interfejsu z naszego assembly oraz regasm.exe do rejestracji w
systemie. Docelowo assembly z custom toolem powinno posiadać silną nazwę, ale
na potrzeby eksperymentalne możemy ten krok pominąć.
tlbexp Testowy.dll
regasm /codebase Testowy.dll
Teraz możemy już ponownie
uruchomić Visual Studio i wypróbować generator.
Przedstawione tutaj rozwiązanie
pozwala generować tylko jeden plik. Czasami chcielibyśmy mieć możliwość
wygenerowania kilku plików. Nie jest to niemożliwe :) Implementację klas
bazowych umożliwiających generowanie wielu plików znalazłem na CodeProject [2].
[1] http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=4AA14341-24D5-45AB-AB18-B72351D0371C
[2] http://www.codeproject.com/useritems/VsMultipleFileGenerator.asp