Mastering Visual Studio .NET 1002003 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Mastering Visual Studio .NET 1002003 [Electronic resources] - نسخه متنی

Jon Flanders, Ian Griffiths, Chris Sells

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








2.7 Custom Build Tools


In C#, J#, and VB.NET projects, all
source files have a Custom Tool property. This can be used to process
a file at design time, optionally generating another file to be
compiled into the project. The most common application of this in
VS.NET projects is to generate a type-safe wrapper for the
DataSet class from an XML schema file
(.xsd). (See Chapter 5 for
more information on type-safe DataSet wrappers.)
However, this system is extensible, allowing you to add your own
custom tools to generate code.

A custom tool
is a COM component that VS.NET will run every time the source file
changes and is saved. It must implement the
IVsSingleFileGenerator COM interface. The main
interesting method on this interface is Generate.
VS.NET will call this each time the source file is saved, passing in
the filename and the contents of the input file. The
Generate method returns an array of bytes that
will contain either C#, J#, or Visual Basic .NET source code,
depending on the type of project. VS.NET saves these bytes to a file,
which it compiles when the project is next built. (You can see this
file in the Solution Explorer by pressing the Show All Files button.)
Because the generated file is compiled as part of the project,
IntelliSense will be available during development time for all of the
types it defines.

While you could implement the
IVsSingleFileGenerator COM interface directly, a
managed base class provided in Visual Studio .NET
2002Microsoft.VSDesigner.CodeGenerator.BaseCodeGeneratorWithSiteis
much easier to use. To use it, just import the
Microsoft.VSDesigner.dll assembly in the
Common7\IDE directory of the VS.NET program
directory. Your class must be decorated with the
Guid attribute to determine its CLSID, but apart
from that, the only thing you have to do is write the
Generate method itself. The following code shows
the implementation of a simple code generator.

[Guid("A0B5E5E9-3DF8-48bc-A6BA-E0DFD35C6237")]
public class MyGenerator : BaseCodeGeneratorWithSite
{
public override byte[ ] GenerateCode(string file, string contents)
{
string code = "public class Foo { }";
return System.Text.Encoding.ASCII.GetBytes(code);
}
}

This particular example isn't very
interestingit always generates the same code and
doesn't bother to examine its input. A more useful
tool would generate code based on the input provided.

Once you've built your custom tool, it must be
registered as a COM class. (You can do this by running the
regasm command-line tool.) You must add certain
keys to the registry to let Visual Studio .NET know about your custom
tool. Figure 2-14 shows a typical example.


Figure 2-14. Custom tool registry entries


As you can see, you must add entries under this key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.1\Generators

(For VS.NET 2002, use 7.0 instead of 7.1.) Underneath here you will
find several GUIDs. These are package IDs, which are listed in Table 2-1, and they determine which languages the custom
tool will be available with. (See Chapter 10 for
more information about VS.NET packages.) The example in Figure 2-14 shows a generator registered for C#.






























Table 2-1. Package IDs used with custom tools

Package ID


Package


{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}


C#


{164B10B9-B200-11D0-8C61-00A0C91E29D5}


VB.NET


{E6FDF8B0-F3D1-11D4-8576-0002A516ECE8}


J#


{20D4826B-C6FA-45db-90F4-C717570B9F32}


Embedded C#


{54307750-4C48-4d2d-B523-A3B42F5C3837}


Embedded VB.NET

To add your own tool, create a new key underneath the relevant
package. (So if your tool generates C#, place it under the C# package
ID.) The name of the key will be the name the user types into the
Custom Tool property in Visual Studio .NET. Set the
key's default property to a string describing the
tool. Next, add a string value called
CLSIDthis must contain the CLSID of your
tool (as specified in its Guid attribute; you can
generate a new GUID with Tools Create GUID).
Finally, add a DWORD value called
GeneratesDesignTimeSource, and set it to
1this tells VS.NET that the tool generates source code at
design time and that it should be given the opportunity to do so
every time the user saves the input file.

Once your custom tool has been registered, using it is just a matter
of setting the relevant file's Custom Tool property.
You can either set this manually or create a wizard that will do it
for you programmatically. (See Chapter 9 for more
information on Wizards.)

Unfortunately, with the release of Visual Studio .NET 2003, all of
the types in
Microsoft.VSDesigner.dll
were made private. Not only does this mean that you can no longer
derive from BaseCodeGeneratorWithSite, it also
hides the implementation of the
IVsSingleFileGenerator COM interface. (This is not
defined in any type libraries that ship with VS.NETthe only
definition for it is the one inside
Microsoft.VSDesigner.dll.) This makes it tricky
to write a custom tool in VS.NET 2003, as the documentation states
that you must implement this interface despite not providing a
definition. Fortunately, it doesn't make it
impossiblethe COM interface definitions you require are
simple, and are shown in Example 2-1.

Example 2-1. Custom tool COM interface definitions

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("3634494C-492F-4F91-8009-4541234E4E99")]
public interface IVsSingleFileGenerator
{
[return:MarshalAs(UnmanagedType.BStr)]
string GetDefaultExtension( );
void Generate([In, MarshalAs(UnmanagedType.LPWStr)] string wszInputFilePath,
[In, MarshalAs(UnmanagedType.BStr)] string bstrInputFileContents,
[In, MarshalAs(UnmanagedType.LPWStr)] string wszDefaultNamespace,
out IntPtr pbstrOutputFileContents,
[MarshalAs(UnmanagedType.U4)] out int pbstrOutputFileContentsSize,
[In, MarshalAs(UnmanagedType.Interface)]
IVsGeneratorProgress pGenerateProgress);
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("BED89B98-6EC9-43CB-B0A8-41D6E2D6669D")]
public interface IVsGeneratorProgress
{
[return:MarshalAs(UnmanagedType.U4)]
void GeneratorError(
[In, MarshalAs(UnmanagedType.Bool)] bool fWarning,
[In, MarshalAs(UnmanagedType.U4)] int dwLevel,
[In, MarshalAs(UnmanagedType.BStr)] string bstrError,
[In, MarshalAs(UnmanagedType.U4)] int dwLine,
[In, MarshalAs(UnmanagedType.U4)] int dwColumn);
[return:MarshalAs(UnmanagedType.U4)]
void Progress(
[In, MarshalAs(UnmanagedType.U4)] int nComplete,
[In, MarshalAs(UnmanagedType.U4)] int nTotal);
}

You can then implement the IVsSingleFileGenerator
directly. This is slightly more work than it was under VS.NET 2002,
because we must now deal with the interop issues that were previously
handled by the BaseCodeGeneratorWithSite base
class. But this it not too onerous, as shown in Example 2-2.

Example 2-2. Implementing IVsSingleFileGenerator by hand

[Guid("A0B5E5E9-3DF8-48bc-A6BA-E0DFD35C6237")]
public class MyCustomTool : IVsSingleFileGenerator
{
public byte[ ] GenerateCode(string file, string contents)
{
string code = "public class Foo { }";
return System.Text.Encoding.ASCII.GetBytes(code);
}
public void Generate(string wszInputFilePath,
string bstrInputFileContents, string wszDefaultNamespace,
out IntPtr pbstrOutputFileContents, out int pbstrOutputFileContentsSize,
IVsGeneratorProgress pGenerateProgress)
{
pbstrOutputFileContents = new IntPtr ( );
pbstrOutputFileContentsSize = 0;
if (bstrInputFileContents = = null)
throw new ArgumentNullException( );
byte[ ] codeBytes = GenerateCode(wszInputFilePath, bstrInputFileContents);
int len = codeBytes.Length;
pbstrOutputFileContents = Marshal.AllocCoTaskMem(len);
pbstrOutputFileContentsSize = len;
Marshal.Copy(codeBytes, 0, pbstrOutputFileContents, len);
}
public string GetDefaultExtension( )
{
return ".cs";
}
}

As you can see, the GenerateCode method here looks
exactly the same as beforewe have simply had to supply our own
implementation of IVsSingleFileGenerator. This
custom tool will work in both VS.NET 2002 and VS.NET 2003.

Although the BaseCodeGeneratorWithSite class was
made private with the release of VS.NET 2003, you can still use this
class if you want to, instead of using the code in Example 2-1 and Example 2-2. Microsoft
has made the source code for this class available for download at
http://www.gotdotnet.com/userarea/keywordsrch.aspx?keyword=BaseCodeGeneratorWithSite.

/ 148