Inside Microsoft® Visual Studio® .NET 2003 [Electronic resources] نسخه متنی

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

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

Inside Microsoft® Visual Studio® .NET 2003 [Electronic resources] - نسخه متنی

Brian Johnson

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

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

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










Add-in Registry Named Values


As you learned earlier in this chapter, an add-in makes itself known to Visual Studio .NET by creating a ProgID-named subkey under the AddIns registry key. This subkey can contain a number of named values that allow you to fine-tune the behavior of an add-in. The following several sections cover the named values you can apply to an add-in and describe the effects they produce.



CommandPreload and the PreloadAddinState Key


Many add-ins expose their functionality through menu items and toolbar buttons in the IDEwhen selected or clicked, these user interface items load the add-in and pass along the appropriate command for processing. Of course, the user interface items don't appear magically; an add-in creates them and adds them to the IDE the first time that the add-in loads. But without user intervention, how does an add-in first get loaded in order to create the user interface item to load it? The solution to this "chicken or egg" problem begins with the CommandPreload named value and ends with the PreloadAddinState key. A newly installed add-in sets its CommandPreload value to 0x1 to tell Visual Studio .NET that it wants to be loaded once, the next time the IDE starts up, for the purpose of adding its user interface items to the IDE (a process known as preloading). Figure 6-5 illustrates how Visual Studio .NET preloads add-ins.


Figure 6-5. How add-ins are preloaded



Note

Actually, preloaded add-ins aren't required to create user interface items; they're free to perform any kind of "first load" initialization they need, such as creating data files, adding custom registry entries, and so forth.

At startup, Visual Studio .NET preloads each add-in that has a CommandPreload value of 0x1, but only if the add-in hasn't yet been preloaded. The way Visual Studio .NET determines that an add-in hasn't been preloaded will differ a little depending on whether the add-in is an administrator add-in. As you can see from Figure 6-5, a user add-in's CommandPreload value alone determines whether it has been preloaded because Visual Studio .NET changes the value to 0x2 after preloading the add-in. An administrator add-in's CommandPreload value never changes, however, which means that Visual Studio .NET needs some other mechanism to keep track of whether the add-in has been preloaded. That's what the PreloadAddinState key is for: it holds a list of administrator add-ins and their preload statuses. Before Visual Studio .NET preloads an administrator add-in, it checks PreloadAddinStateif this key is missing, the add-in isn't in the list, or the add-in is in the list and its value is set to 0x1, Visual Studio .NET knows that the add-in hasn't yet been preloaded.

For both administrator and user add-ins, Visual Studio .NET preloads the add-ins silently, passing them the ext_cm_UISetup value in their OnConnection events, and then unloads them immediately after OnConnection returns, passing them the ext_dm_UISetupComplete value in their OnDisconnection events. After preloading an administrator add-in, Visual Studio .NET creates the PreloadAddinState key, if necessary, and sets the add-in's value to 0x0; after preloading a user add-in, Visual Studio .NET sets the add-in's CommandPreload value to 0x2.

Warning

When we say that Visual Studio .NET preloads add-ins silently, we mean silently. Under normal circumstances, Visual Studio .NET tells you when your add-in fails to load, and it even offers you the choice of removing the offending add-in from the AddIns registry key. When preloading, however, Visual Studio .NET won't give you the slightest hint that your add-in bombed.

The PreloadAddinState key lives in the HKEY_CURRENT_USER branch of the registry at Software\Microsoft\VisualStudio\7.1\AddIns\PreloadAddinState. This key isn't created when you install Visual Studio .NET; instead, the key is created on demand the first time Visual Studio .NET preloads an administrator add-in for a particular user. In this way, an administrator add-in that sets CommandPreload to 0x1 gets preloaded automatically for each userincluding users who are added after the add-in is installed.

Tip

If you need to restore an add-in's user interface items, set its PreloadAddinState value to 0x1 if it's an administrator add-in, or set its CommandPreload value to 0x1 if it's a user add-in, and it will get preloaded the next time Visual Studio .NET runs. If you prefer the shotgun approach, you can restore the user interface items of every add-in by executing the following command at the command prompt:


devenv /setup

This command resets the IDE to its original state (thereby removing every add-in user interface item, along with any other customizations to the IDE), changes the CommandPreload values of user add-ins from 0x2 to 0x1, and deletes the PreloadAddinState key; the result is that Visual Studio .NET preloads each and every one of these add-ins the next time it runs. A bit crude, but effective.



LoadBehavior and CommandLineSafe


The LoadBehavior named value controls how an add-in is loaded and also reflects the add-in's current load state. Table 6-3 lists the possible LoadBehavior values. These values are bit flags, so in theory you can combine them to create your own custom load settings; in reality, with one exception, combinations of flags behave no differently from individual flags.

Table 6-3.
LoadBehavior Values

Flag


Value


Description


ID_UNLOADED


0x0


Add-in currently is unloaded


ID_STARTUP


0x1


Add-in loads at startup


ID_LOADED


0x2


Add-in currently is loaded


ID_COMMAND_LINE


0x4


Add-in loads during command-line builds

The ID_UNLOADED and ID_LOADED values no longer serve a useful purpose. Use the AddIn.Connected property instead to discover the load state of an add-in.

The ID_STARTUP flag tells Visual Studio .NET to load the add-in when the IDE starts up. Add-ins that monitor IDE events, in particular, need to be up and running from the beginningotherwise, they might miss some of the action. Add-ins that don't care about IDE events can omit this flag and wait to be loaded on demand.

The ID_COMMAND_LINE flag signals that an add-in should be loaded during command-line builds. Be aware that in Visual Studio .NET 2002 this flag doesn't work as advertised. In version 2002, the only effect this flag has is to disallow an add-in from being loaded by the Add-in Manager (and even this effect is overridden when the ID_STARTUP flag is present).

The optional CommandLineSafe named value is supposed to work hand-in-hand with the ID_STARTUP and ID_COMMAND_LINE flags to ensure the success of unattended builds. A CommandLineSafe value of 0x1 indicates that the add-in won't display a user interface that requires human interactionat least, not when a build is started from the command line; a missing CommandLineSafe entry or a CommandLineSafe value of 0x0 marks the add-in as unsuited for command-line builds. Currently, the CommandLineSafe value doesn't affect whether Visual Studio .NET loads the add-inthe value is for informational purposes only.


Is It Safe?


Clearly, the Visual Studio .NET designers had something special in mind when they created the command linerelated registry values. Long builds and overnight builds are staples of the software industry, so any feature that improves the odds for successful, unattended builds is worth a sys admin's weight in gold. As far as add-ins go, Visual Studio .NET 2002 includes all the components for the perfect command-line buildbut the components just haven't been wired up yet. Here's how command-line builds work in Visual Studio .NET 2003:

A developer initiates a command-line build.

Visual Studio .NET loads the add-ins marked as ID_COMMAND_LINE and passes in an ext_cm_CommandLine value to each add-in's OnConnection event.

The add-ins use the ext_cm_CommandLine value as a signal to disable their user interfaces.


But command-line builds don't work this way in Visual Studio .NET 2002. Instead, ID_COMMAND_LINE doesn't affect whether an add-in is loaded; add-ins marked as ID_STARTUP are loaded by mistake; and Visual Studio .NET 2002 doesn't pass in ext_cm_CommandLine under any circumstances.

Not to worry, thoughall these problems are fixed in Visual Studio .NET 2003. And you can work around these problems in Visual Studio .NET 2002 by marking the add-in as ID_STARTUP and using code like the following, which sifts through the System.Environment.CommandLine property looking for evidence of a command-line build:

[View full width]

if (Regex.IsMatch(System.Environment.CommandLine, "/build"))
{
// Command-line build
System.Console.WriteLine("Command-line build--phooey on GUI!");

}
else
{
// Not a command-line build
MessageBox.Show("GUI for you!");
}


By placing the previous code in your add-in's OnConnection event handler, your add-in can decide at startup whether to display its user interface.



SatelliteDLLPath and SatelliteDLLName


If you want to distribute your add-in internationally, you need to pay attention to the problem of localization. A worldly add-in doesn't force a particular language on its usersinstead, it communicates with each user in his or her native tongue. Of course, a standalone add-in can't possibly accommodate every language; instead, an add-in that supports localization stores its localizable resources in satellite DLLs. At run time, the add-in searches for the satellite DLL that corresponds to the current locale and uses the localized resources from that DLL to populate its user interface. In this way, the same add-in can support any number of languages simply by providing a localized satellite DLL for each locale.

An add-in advertises the location of its satellite DLLs through the Satellite­DLLPath and SatelliteDLLName named values. SatelliteDLLName stores the satellite DLL name (which implies that all localized satellite DLLs share the same file name), and SatelliteDLLPath stores the satellite DLL root folder. Each localized satellite DLL lives in its own folder under the root, and the name of the folder is the locale identifier of the language that the DLL supports. For example, the locale identifier for U.S. English is 1033, so the U.S. English satellite DLL is found at <SatelliteDLLPath>\1033\<SatelliteDLLName>. At run time, you can locate an add-in's satellite DLL for the current locale by using either the AddIn.SatelliteDllPath property or the DTE.SatelliteDllPath method.


Lab: Probing for Satellite DLLs


The documentation claims that Visual Studio .NET locates an add-in's satellite DLL by looking in the following places, in the order listed:


<SatelliteDLLPath>\<Default system locale>\<SatelliteDLLName>
<SatelliteDLLPath>\<Default user locale>\<SatelliteDLLName>
<SatelliteDLLPath>\<OS setup locale>\<SatelliteDLLName>
<SatelliteDLLPath>\1033\<SatelliteDLLName>
<SatelliteDLLPath>\<SatelliteDLLName>

Problem is, the documentation is wrongat least in part. So, how does Visual Studio .NET find satellite DLLs? See for yourself. The Satellite­DLLProbing macro creates a directory named satellites that contains subdirectories for 133 of your favorite localesfrom Afrikaans to Zuluand copies a satellite DLL into each locale's directory. The macro's main loop calls the DTE.SatelliteDllPath method repeatedly: after each call, the macro displays the satellite DLL path and corresponding locale in the Output window, and then it deletes the current satellite DLL, forcing Satellite­DllPath to search in a new location the next time through the loop.

If you run the SatelliteDLLProbing macro a couple of times, changing the user locale between runs, you'll notice that the user locale doesn't count for much. The system locale always overrides the user locale, which means the current user won't necessarily see his preferred locale. You'll also notice that SatelliteDllPath never finds the satellite DLL in <Satellite­DLLPath>, contrary to the claims made by the documentation. The most interesting result, however, is the stubborn determination with which SatelliteDllPath hunts down a stray satellite DLL. Instead of stopping after checking the system locale subdirectory and finding nothing, SatelliteDllPath starts rummaging through the other locale subdirectories like a raccoon digging through a trash canthe logic being, we suppose, that any language is better than no language at all. (Can't find English? Try Catalan instead!) In fact, SatelliteDllPath goes so far as to search through nonlocale subdirectories, and if it finds a satellite DLL there, it'll use it.

Understand that localization is a complex issue and that Visual Studio .NET's satellite DLL search algorithm reflects that complexitywhen the locales are set up correctly, the algorithm works just fine, but if a locale gets misplaced, the algorithm blows up spectacularly. The good news is that Visual Studio .NET uses the same mechanism for its own localization, which means that it will always find your add-in if your add-in uses the same locale as the Visual Studio .NET installation.



FriendlyName and Description


The FriendlyName and Description named values allow you to apply a meaningful name and a short description to your add-in. An example of an application that uses these values is the Add-in Manager, which populates its add-in list with FriendlyName values and displays the Description value of the selected add-in in its Description box.

FriendlyName and Description each store either a human-readable string or the ID of a string resource in the add-in's satellite DLL (in the form "#<resource ID>"). Pulling string resources out of satellite DLLs is fairly straightforward; you can find code for doing so in the Add-in Manager Plus sample add-in.



AboutBoxDetails and AboutBoxIcon


The AboutBoxDetails and AboutBoxIcon named values buy your add-in some real estate on the Visual Studio .NET About dialog box, as shown in Figure 6-6. The About dialog box displays FriendlyName values in the Installed Products list; when a user selects an add-in from this list, the dialog box displays the add-in's AboutBoxIcon under the Product Details label and its AboutBoxDetails information in the text box next to its icon.

Note

Both the AboutBoxDetails and AboutBoxIcon entries need to be in the registry before the About dialog box will display your add-in's information; however, either or both of the values can be bogus.


Figure 6-6. Add-in information displayed in the Visual Studio .NET About dialog box



The format of the AboutBoxDetails value is the same as that of the FriendlyName and Description valuesa string that holds either a short description or the ID of a string resource in the add-in's satellite DLL (in the form "#<resource ID>").

The AboutBoxIcon value offers a bit more flexibility than its counterpart. An add-in can store its icon information in one of four formats:

String data representing the ID of an icon resource in the add-in's satellite DLL (in the form "#<resource ID>").

String data representing a path to an icon file.

String data representing a path to an executable file and the ID of an icon resource within that file (in the form "<path>,<resource ID>"). For example, "C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\devenv.exe,1200" refers to the Visual Studio .NET application icon.

Binary data of an icon.


The last format gives an add-in the option of storing all its "personal" informationincluding FriendlyName, Description, and AboutBoxDetailswithin the registry rather than distributed across separate files. Storing icon data in the registry is as easy as the following macro procedure, which takes the add-in's registry key and the pathname of the icon file as parameters:


Sub SetAboutBoxIcon(ByVal addInKey As RegistryKey, _
ByVal iconPath As String)
Dim iconFile As FileStream = File.OpenRead(iconPath)
Dim iconData(iconFile.Length) As Byte
Dim i As Integer
For i = 0 To iconFile.Length - 1
iconData(i) = iconFile.ReadByte
Next
addInKey.SetValue("AboutBoxIcon", iconData)
End Sub

Note

Hats off to Kenny Kerr for his icon resource management classes! We're hard-pressed to think of a more thankless task than writing managed code to pull icons from Win32 executable files, but Kenny did it and saved us the trouble, so we're happy to give him all the thanks that can fit into one of these notes. (We put his classes to work extracting icons for the Add-in Manager Plus add-in and couldn't be more pleased with the results.) You can find his Icon Browser utility, for which the icon resource management classes were originally written, and lots more cool stuff by Kenny at his Web site: http://www.kennyandkarin.com/kenny.


/ 118