The official source of product insight from the Visual Studio Engineering Team
For those using the Visual Studio 2010 SDK to create extensions for Visual Studio, you should find the creation and use of .pkgdef files to be mostly automatic (see What's a PkgDef and Why?). However, like anything involving computers and software, there are occasions that require manual intervention because they aren’t covered by the automatic settings or things can go wrong. This article illustrates some possible issues with .pkgdef files and provides techniques for discovering what’s wrong and correcting them.
Please note that these techniques apply not only to the Visual Studio 2010 IDE, but also to applications built using the Visual Studio 2010 Shell (Isolated) (or Integrated). All of the examples assume the case of VS 2010 running on 32-bit Windows 7. For other cases, substitute the appropriate file locations and names and registry root key.
First of all, how do you know if something goes wrong? A problem with a .pkgdef file usually manifests itself in one of two ways:
Either of these symptoms can be caused by one or more of the issues listed below. The list is by no means intended to be exhaustive.
Pkgdef files that are installed as part of a user extension deployed using the new VSIX format are copied to a sub-folder of %LocalAppData%\Microsoft\VisualStudio\10.0\Extensions. The pkgdef loader does not automatically search for pkgdef files here. Rather, it loads pkgdef files for user extensions that have been enabled, which the Extension Manager and the stand-alone VSIX installer both do automatically. If you choose to unpack a VSIX file yourself, or otherwise do a manual xcopy-style deployment, it’s easy to forget the step that enables the extension. This is easily fixed.
It’s entirely possible that if you manually deployed the contents of a VSIX file, you simply need to enable it in the Extension Manager dialog and restart Visual Studio before the pkgdef loader will pick it up.
On a related note, there is an option to disable loading of all per-user extensions when running ‘as administrator’ (elevated), regardless of being enabled or not. This is controlled under Tools-Options-Extension Manager as Load per user extensions when running as administrator. Make sure this checkbox is checked (when appropriate).
For all other extensions (those that are not per user), the pkgdef loader searches the locations listed in the PkgDefSearchPath of the master pkgdef file. The value of PkgDefSearchPath has three elements:
$RootFolder$ is the location where Visual Studio is installed (C:\Program Files\Microsoft Visual Studio 10.0). The first two search elements are folders, which are recursively scanned looking for pkgdef files. The third element is a specific pkgdef file (which does not typically exist, but is kept in the list as a placeholder for system administrators).
An easy mistake to make is to drop a pkgdef file into either $RootFolder$ or $RootFolder$\Common7\IDE or some other sub-folder and expect the pkgdef loader to just find it. However, these locations are not in the search path.
You can find out exactly which pkgdef files the loader is finding, using the /log command line option for Visual Studio. Among the items the pkgdef loader logs are the full expanded value for the PkgDefSearchPath, a list of all of the pkgdef files found on the search path, and a list of the pkgdef files found for enabled extensions.
Run: devenv.exe /log
When Visual Studio is finished loading (status bar reads “Ready”), File - Exit, start Windows Explorer, browse to C:\Users\User\AppData\Roaming\Microsoft\VisualStudio\10.0, and open the file ActivityLog.xml. It should open in Internet Explorer using the associated XSL to format it in an table.
Near the top of the file, Visual Studio will list the PkgDefSearchPath:
This is followed by the number of files found and then the list of those files, for example:
Armed with the knowledge of which folders are being scanned and which pkgdef files were discovered, you should now be able to make sure your pkgdef file is deployed to an appropriate location.
Any extension that is not installed per-user should be installed in a sub-folder of $RootFolder$\Common7\IDE\Extensions or $RootFolder$\Common7\IDE\CommonExtensions.
This issue is most likely to crop up if you run into a situation where you must create or edit a pkgdef file manually. Like any language, it’s easy to forget a closing quote or insert an extra one.
The syntax of a pkgdef file is actually quite simple. Each line can be one of:
Common syntax mistakes include:
The pkgdef loader is not very tolerant of syntax errors and will simply refuse to load a pkgdef file that contains even a single error, rather than risk corruption of the pkgdef cache. When it does reject a pkgdef, this information is written to the activity log.
Use /log to generate ActivityLog.xml. Any syntax errors will be highlighted in red, along with a warning showing the name of the offending file (which is not loaded).
In this example, the value of “foo” is missing the closing quote expected for strings and the value of bar is given as word, rather than dword or qword. The name for “bax” is not put in quotes. Once you edit the file to make these corrections (and save it and deploy it to the correct location), the pkgdef loader will see that the file has been updated and rebuild the cache automatically (you do not have to force this rebuild by deleting the registry value from the cache again).
Another way to find and fix syntax errors is to download and install the new editor extension we’ve created to give syntax coloring and error output for pkgdef files. It can be found in the Visual Studio gallery, either through the Extension Manager or directly in the gallery. Just search for “pkgdef” under the Tools category and download the PkgDef Editor extension.
In this example, the same errors as above are flagged, along with the pop-up message describing the problem with the value of “foo”.
pkgdef files are made portable by tokenizing system-specific and user-specific information. These are known as substitution strings. Examples are $RootKey$, which defines the root of the Visual Studio configuration registry, the starting point for all key values, and $PackageFolder$, which is the full path in the file system to the location from where the current pkgdef file is being loaded.
Substitution strings are case-sensitive and must be enclosed between two dollar-signs (‘$’). Common mistakes related to tokens are:
When the pkgdef loader cannot recognize a substitution string, it simply does nothing and uses it ‘as is’. When this happens in a registry key or value name, it means the key or value will not be put in the correct location or get the correct name.
This can be very difficult to debug, except by knowing what to look for. When this happens for a string value, it is a little easier to detect, simply by looking in the registry using regedit.exe:
In this example, the value for “bax” was specified with “$PkgFolder$”, rather than “$PackageFolder$”.
As seen above, the PkgDef Editor extension can make it much easier to see problems with pkgdef files. In particular, it knows the valid tokens for string substitution and can flag the ones it does not recognized:
In this example, all of the string substitution tokens (anything starting and ending with the dollar-sign) are highlighted (in blue) and the unrecognized tokens are flagged as errors. One string contains a value that was intended to be a string substitution, but is missing the closing dollar-sign, so it is not highlighted. Also in this example, it is easy to see that $RegRoot$ is not a recognized string substitution, but using regedit it was simply missing-in-action.
The pkgdef loader will not write to arbitrary registry locations. It only writes registry entries within the configuration hive of Visual Studio (or an Visual Studio Shell (Isolated) application). This rule is easily followed by starting all key/section names with $RootKey$.
The pkgdef loader will log attempts to write to locations outside of the application’s configuration hive:
In this example, using the file from the previous example, $RegRoot$ was not recognized as a substitution string, so it did not get replaced with the appropriate root key for the application. And the result is not a valid location for a registry key for the application.
Sometimes the same registry value is being set by more than one pkgdef file. In other cases, a registry value is being set, but it’s not clear which pkgdef file is doing it. You can either scan all of the pkgdef files and discover where the value is being set. Or use /log again.
To discover all pkgdef files that are setting a value, use a simple trick: explicitly set that same value by temporarily changing the master pkgdef (C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\devenv.pkgdef) to explicitly set the value before all of the other pkgdef files are processed. This will require Administrator rights to edit the file, but is otherwise straightforward. Make a backup copy of devenv.pkgdef, then bring it up in an editor. Below the [$Initialization$] section, add a new section for the parent key of the value. Then add the key value below it, save it, and run devenv.exe with /log. The pkgdef loader will log all of the additional writes to that value, along with the path to the offending pkgdef.
In this example, we caught the blog.pkgdef file writing to the value “foo” under “$RootKey$\MyExtension”. The next step is to determine why and whether this needs to change (be sure to put devenv.pkgdef back the way you found when you’re done).
This post has described several tactics for troubleshooting pkgdef issues. The most basic of these is to use /log, along with a trick for forcing PkgDef Management to rebuild the pkgdef cache. You may also find that the PkgDef Editor extension is helpful when editing pkgdef files.
Bill Weinberger: Developer, Visual Studio Platform
Bill has been with the VS Platform team for about two years, and has more than a few previous years of software industry experience working on IDEs and vertical applications. For Visual Studio 2010, he was a key contributor to PkgDef Management.