Thursday, November 22, 2007 - Posts
To deploy a VS Shell application we first need to obtain a Shell Load Key (SLK), a Package Load Key (PLK) for the shell resource package and a Package Load Key (PLK) for each VS Package that makes up the application behavior.
All these keys are obtained via the VSIP members web site.
The information needed to obtain a SLK is the following:
- ThisVersionDTECLSID
- AppName
- CompanyName
- Productversion
- ID
[$RootKey$]
"ThisVersionDTECLSID"="{65a1a6a1-0b2a-461f-8dc2-1a3ebc299584}"
"AppName"="MyVSShellApp"
"CompanyName"="MyCompany"
"Productversion"="1.0"
"ID"=dword:00000069
The information needed to obtain a PLK for the shell resource package is the following:
- Resource package GUID
- CompanyName
- ProductName
- ProductVersion
- ID
- MinEdition
"AppLocalizationPackage"="{209c95cd-4012-43e5-845a-f09f5f3a13ed}"
"CompanyName"="MyCompany"
"ProductName"="MyVSShellApp"
"Productversion"="1.0"
"ID"=dword:00000065
"MinEdition"="Standard"
The information needed to obtain a PLK for the VS Package behavior is here
Links:
http://msdn2.microsoft.com/en-us/library/bb932468(VS.90).aspx
http://msdn2.microsoft.com/en-us/library/bb932503(VS.90).aspx
Pablo
After we create a VS Shell application using the new VS solution template shipped with the VS 2008 SDK 1.0,

The following solution structure is created:
Now we can customize the VS Shell Application and hit F5 to see it running.
So far so good, but up to this point we only have the shell and we need to add the behavior of the application.
For this post I am going to assume that the behavior of our VS Shell application is going to be implemented by a VS Package.
So we add a new VS package project using the existing VS SDK project template,
We implement the behavior of our application and finally we integrate the package with the VS Shell application.

After adding the VS Package as a Shell Dependency and compiling the application, the package assembly and the pkgdef (automatically generated) are copied under the \PackagesToLoad directory:
The last thing is to hit F5 and see how it works!!!
Pablo
A bootstrapper is a program used to get the system prepared to install an application. Some applications have requirements in order to run properly.
This is the case for a VS Shell application that has the Visual Studio 2008 Shell Isolated Mode Redistributable package as a requirement.
Basically we need a bootstrapper that installs the Visual Studio 2008 Shell Isolated Mode Redistributable package first and if everything went fine, it redirects the call to our VS Shell application installer.
.Net Framework 2.0 includes the GenerateBootstrapper class to support the ClickOnce scenario. Of course we can use it in other scenarios, since this class is a msbuild task that can be easily added to our msbuild project.
The bootstrapper infrastructure that comes with .Net Fx 2.0 is generic and configurable by the notion of bootstrapper packages.
Bootstrapper packages are composed of one xml file (metadata that describes the package), a xml localization file and the msi/exe for the package itself.
These packages need to be copied to a certain location to be found by Visual Studio and the msbuild infrastructure.
We can see the installed packages on our system from Visual Studio with the Prerequisites... button:
This list is being populated from the %Program Files%\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages directory.
The first thing that we need to do is create the package for the VS Shell Redist.
These are two sample xml files for the bootstrapper package:
Product.xml
<?xml version="1.0" encoding="utf-8"?>
<Product ProductCode="Vs.Shell" xmlns="http://schemas.microsoft.com/developer/2004/01/bootstrapper">
<PackageFiles CopyAllPackageFiles="false">
<PackageFile Name="vs_shell_isolated.enu.exe" />
</PackageFiles>
<InstallChecks>
<RegistryCheck Property="VisualStudio" Key="HKLM\Software\Microsoft\VisualStudio\9.0" Value="ApplicationId" />
</InstallChecks>
<Commands Reboot="Immediate">
<Command PackageFile="vs_shell_isolated.enu.exe" Arguments="/q" EstimatedInstallSeconds="2800">
<InstallConditions>
<BypassIf Property="VisualStudio" Compare="ValueEqualTo" Value="VisualStudio" />
</InstallConditions>
<ExitCodes>
<ExitCode Value="0" Result="Success"/>
<ExitCode Value="1641" Result="SuccessReboot"/>
<ExitCode Value="3010" Result="SuccessReboot"/>
<DefaultExitCode Result="Fail" String="GeneralFailure" FormatMessageFromSystem="true" />
</ExitCodes>
</Command>
</Commands>
</Product>
Package.xml
<?xml version="1.0" encoding="utf-8"?>
<Package Name="Vs.Shell" Culture="Culture" xmlns="http://schemas.microsoft.com/developer/2004/01/bootstrapper">
<Strings>
<String Name="Culture">en</String>
<String Name="DisplayName">Vs Shell</String>
<String Name="GeneralFailure">A fatal error occurred. The installation failed.</String>
</Strings>
</Package>
Once we have the package created and correctly installed we can create a simple msbuild proj file that includes the GenerateBootstrapper task:
<Project DefaultTargets="Bootstrapper" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<BootstrapperFile Include="Vs.Shell">
<ProductName>Vs Shell</ProductName>
</BootstrapperFile>
</ItemGroup>
<Target Name="Bootstrapper">
<GenerateBootstrapper
ApplicationFile="MyVSShellApplication.msi"
ApplicationName="My Application"
BootstrapperItems="@(BootstrapperFile)"
OutputPath=".\"
CopyComponents="true"
ComponentsLocation="Relative"
Culture="en"
/>
</Target>
<Import Project="$(MSBuildBinPath)\Microsoft.Common.targets" />
</Project>
So far so good, but now we have three files (bootstrapper (setup.exe), Vs Shell redist exe and our application msi).
We need to package these three files on single setup exe file that the user will download.
To package the files we can use the Microsoft IExpress technology.
And here is where we need to do some *ugly* hacks because of a directory structure problem with the GenerateBootstrapper task and IExpress (it doesn't support adding directory structures)
After we compile our msbuild proj file a directory structure is created similar to:
- Vs Shell
- vs_shell_isolated.enu.exe
- Setup.exe
- MyVSShellApplication.msi
Unfortunately, the MSBuild task doesn't provide the option to have the configuration resource use prerequisite installers found in the target directory, so you must manually update the appropriate resource file to remove the hard-coded path that looks for prerequisites in a sub-directory of the same name.
- Open the Setup.exe program in Visual Studio's resource editor
- Double-click the resource named, SETUPCFG in the 41 folder
- Search for the "Vs Shell\" string and delete the two occurrences that appear
- Save the resource file and the Setup.exe executable will be updated automatically
- Run iexpress
- Create a new package by following the IExpress wizard's steps and make sure to include the following files:
- The MyVSShellApplication.msi file
- The Setup.exe bootstrapper file
- The vs_shell_isolated.enu.exe file
Links:
Bootstrapper manifest generator:
http://www.codeplex.com/bmg
Packages documentation:
http://msdn2.microsoft.com/en-us/library/aa730839(vs.80).aspx
http://blogs.msdn.com/chrsmith/archive/2005/09/02/Rebooting-in-the-Bootstrapper.aspx
IExpress documentation:
http://www.microsoft.com/technet/prodtechnol/ie/ieak/techinfo/deploy/60/en/iexpress.mspx?mfr=true
Happy VS Shell bootstrapping,
Pablo
As we know Visual Studio depends on the registry. Every VS component is registered in the registry and Visual Studio reads this information to configure and load the components at runtime.
This is also the case for a VS Shell Application. The only difference here is the main registry hive used. And this main hive is "Isolated" from the Visual Studio main hive.
The root hive for VS Shell applications is HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AppEnv\9.0\Apps\
Every VS Shell application has its own main hive and the name of this hive is composed of the stub's name and GUID.
We can see that name on main cpp file for our stub:
nRetVal = Setup(W2A(lpCmdLine), L"MyVSShellApp_490526d0-3a2e-4a41-8bce-d01f8c1df6b7", NULL);
This means that for this specific example the main hive for MyVSShellApp is:
- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AppEnv\9.0\Apps\MyVSShellApp_490526d0-3a2e-4a41-8bce-d01f8c1df6b7
Now lets talk about the /Setup switch. For the other available switches please refer to my previous post.
When we do a MyShellApp.exe /Setup a couple of things happens:
- The registry main hive is created
- The menus cache is refreshed
- VS templates are installed
- ...
In this post I will go into the details of the registry information created.
The /Setup switch tells appenvstub.dll to create the registry information based on the pkgdef files found under the \PackagesToLoad directory, and these are the steps:
- The VS Shell application hive is created
- All pkgdef files found are parsed and every single line is added to the registry (with support of parameter replacement variables)
- VS Shell application pkgdef file
- Any other VS package pkgdef file
- The %DevEnvDir%\Common7\IDE\AppEnv\AppEnvConfig.pkgdef file is parsed and every single line is added to the registry
- The %DevEnvDir%\Common7\IDE\AppEnv\BaseConfig.pkgdef file is parsed and every single line is added to the registry
- The VS Shell application pkgundef file is parsed and every single line is removed from the registry
Now when we do a MyVSShellApp.exe /Remove, the main registry hive for our VS Shell application is removed from the registry.
Pablo
The customization points for our VS Shell application are available in three separate files.
The pkgdef file:
In this file we can customize things like the title bar, splash screen, icon graphics, start page and more.
[$RootKey$]
"ThisVersionDTECLSID"="{65a1a6a1-0b2a-461f-8dc2-1a3ebc299584}"
"ThisVersionSolutionCLSID"="{72636818-5927-4b0d-8c56-8c145875fa4f}"
"SplashScreenBitmap"="$RootFolder$\Splash.bmp"
"AppName"="MyVSShellApp"
"AppIcon"="$RootFolder$\MyVSShellApp.ico"
"CommandLineLogo"=" MyVSShellApp Version 1.0"
"UserFilesSubFolderName"="MyVSShellApp"
"NewProjDlgSlnTreeNodeTitle"="MyVSShellApp installed templates"
"NewProjDlgInstalledTemplatesHdr"="MyVSShellApp installed templates"
"HideMiscellaneousFilesByDefault"=dword:00000000
"AddinsAllowed"=dword:00000001
"DisableOutputWindow"=dword:00000000
"AllowsDroppedFilesOnMainWindow"=dword:00000001
"DefaultSearchPage"="http://search.live.com"
"DefaultHomePage"="http://www.myapp.com"
"HideSolutionConcept"=dword:00000000
"DefaultDebugEngine"="{00000000-0000-0000-0000-000000000000}"
"UserOptsFileExt"="MyVSShellApp_suo"
"SolutionFileExt"="MyVSShellApp_sln"
"DefaultUserFilesFolderRoot"="MyVSShellApp"
"SolutionFileCreatorIdentifier"="MyVSShellApp Solution File, Format Version 10.00"
"PredefinedAliasesString"=""
"DefaultProjectsLocation"="$MyDocuments$\MyVSShellApp"
"AppLocalizationPackage"="{209c95cd-4012-43e5-845a-f09f5f3a13ed}"
The pkgundef file:
In this file we can remove VS components by deleting the associated registry entries for our VS Shell application
For example if we want to remove the Server Explorer toolwindow from our VS Shell application:
//Server Explorer
[$RootKey$\ToolWindows\{74946827-37a0-11d2-a273-00c04f8ef4ff}]
The vsct file:
In this file we can turn on/off Visual Studio feature sets and menu command groups.
The rule is:
- To turn on a menu we need to comment the menu definition line
- To turn off a menu we need to un-comment the menu definition line
<Define name="No_TechSupportCommand"/>
<!-- <Define name="No_F1HelpCommand"/> -->
Links:
http://msdn2.microsoft.com/en-us/library/bb685692(VS.90).aspx
Pablo
If you want to know what happens behind the scenes when we execute a VS Shell application you can read Craig's post about the VS Shell architecture.
To summarize Craig's post when we execute a VS Shell application, it will find and load the appenvstub.dll (shipped with the VS SDK) and call one of the three exported methods of this dll (Start, Setup or Remove). Then the appenvstub.dll will act as a translator between the VS Shell Application and msenv.dll who is responsible for doing all the work.
The important thing (and purpose of this post) is that the VS Shell application calls the appenvstub.dll and passes the name of the Application (SKU), the command line switch (Start/Setup/Remove) and the rest of the command line buffer.
We can take a look at the main .cpp of the VS Shell application to see how it works:
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
...
if(fDoSetup)
{
...
nRetVal = Setup(W2A(lpCmdLine), L"MyAppName_MyAppGUID", NULL);
...
}
else if(fDoRemove)
{
...
nRetVal = Remove(W2A(lpCmdLine), L"MyAppName_MyAppGUID");
...
}
else
{
...
nRetVal = Start(W2A(lpCmdLine), L"MyAppName_MyAppGUID", nCmdShow, NULL, NULL);
}
...
}
The first parameter for these three methods is lpCmdLine and this is the entire command line buffer.
The Start method in particular is the one that we are interested in. Because appenvstub.dll ends up calling msenv.dll, this means that we can use the same command line switches that when we execute devenv.exe, in fact our VS Shell application is at the end another msenv.dll caller. (same as devenv.exe)
So basically we can use:
- MyApp.exe /Splash (to show the splash screen)
- MyApp.exe /Log (to enable the application log)
- MyApp.exe /NOVSIP (to test the SLK and PLKs)
Pablo