I'm trying to write a PowerShell script to write a Visual Studio extension which will simply add a project template. Here is a trimmed down version of the script to demonstrate the problem:
# Add the assemblies
Add-Type -Assembly System.IO.Compression.FileSystem
# Create temporary directories for the zip archives
[System.IO.Directory]::CreateDirectory("Extension")
[System.IO.Directory]::CreateDirectory("Template")
# Build up the contents of the template file
$templateContent = "<?xml version=`"1.0`" encoding=`"utf-8`"?>`r`n"
$templateContent += "<VSTemplate Version=`"3.0.0`" Type=`"Project`" xmlns=`"http://schemas.microsoft.com/developer/vstemplate/2005`" xmlns:sdk=`"http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010`">`r`n"
$templateContent += " <TemplateData>`r`n"
$templateContent += " <Name>MyExtension</Name>`r`n"
$templateContent += " <Description>MyExtension</Description>`r`n"
$templateContent += " <Icon>MyExtension.ico</Icon>`r`n"
$templateContent += " <ProjectType>CSharp</ProjectType>`r`n"
$templateContent += " <ProjectSubType></ProjectSubType>`r`n"
$templateContent += " <RequiredFrameworkVersion>2.0</RequiredFrameworkVersion>`r`n"
$templateContent += " <SortOrder>1000</SortOrder>`r`n"
$templateContent += " <TemplateID>61251892-9605-4816-846b-858352383c38</TemplateID>`r`n"
$templateContent += " <CreateNewFolder>true</CreateNewFolder>`r`n"
$templateContent += " <DefaultName>MyExtension</DefaultName>`r`n"
$templateContent += " <ProvideDefaultName>true</ProvideDefaultName>`r`n"
$templateContent += " </TemplateData>`r`n"
$templateContent += " <TemplateContent>`r`n"
$templateContent += " <Project File=`"MyExtension.csproj`" ReplaceParameters=`"true`"></Project>`r`n"
$templateContent += " </TemplateContent>`r`n"
$templateContent += "</VSTemplate>"
# Save the template file
$templateContent | Out-File ([System.IO.Path]::Combine("Template", "MyExtension.vstemplate")) -Encoding "UTF8" -NoNewline
# Build up the contents of the proj file
$projContent = "<?xml version=`"1.0`" encoding=`"utf-8`"?>`r`n"
$projContent += "<Project ToolsVersion=`"4.0`" DefaultTargets=`"Build`" xmlns=`"http://schemas.microsoft.com/developer/msbuild/2003`">`r`n"
$projContent += " <Import Project=`"`$(MSBuildExtensionsPath)\`$(MSBuildToolsVersion)\Microsoft.Common.props`" Condition=`"Exists('`$(MSBuildExtensionsPath)\`$(MSBuildToolsVersion)\Microsoft.Common.props')`" />`r`n"
$projContent += " <PropertyGroup>`r`n"
$projContent += " <Configuration Condition=`" '`$(Configuration)' == '' `">Debug</Configuration>`r`n"
$projContent += " <Platform Condition=`" '`$(Platform)' == '' `">AnyCPU</Platform>`r`n"
$projContent += " <ProductVersion>`r`n"
$projContent += " </ProductVersion>`r`n"
$projContent += " <SchemaVersion>2.0</SchemaVersion>`r`n"
$projContent += " <ProjectGuid>{403C08FA-9E44-4A8A-A757-1662142E1334}</ProjectGuid>`r`n"
$projContent += " <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>`r`n"
$projContent += " <OutputType>Library</OutputType>`r`n"
$projContent += " <AppDesignerFolder>Properties</AppDesignerFolder>`r`n"
$projContent += " <RootNamespace>`$safeprojectname`$</RootNamespace>`r`n"
$projContent += " <AssemblyName>`$safeprojectname`$</AssemblyName>`r`n"
$projContent += " <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>`r`n"
$projContent += " <UseIISExpress>false</UseIISExpress>`r`n"
$projContent += " <IISExpressSSLPort />`r`n"
$projContent += " <IISExpressAnonymousAuthentication />`r`n"
$projContent += " <IISExpressWindowsAuthentication />`r`n"
$projContent += " <IISExpressUseClassicPipelineMode />`r`n"
$projContent += " </PropertyGroup>`r`n"
$projContent += " <PropertyGroup Condition=`" '`$(Configuration)|`$(Platform)' == 'Debug|AnyCPU' `">`r`n"
$projContent += " <DebugSymbols>true</DebugSymbols>`r`n"
$projContent += " <DebugType>full</DebugType>`r`n"
$projContent += " <Optimize>false</Optimize>`r`n"
$projContent += " <OutputPath>bin\</OutputPath>`r`n"
$projContent += " <DefineConstants>DEBUG;TRACE</DefineConstants>`r`n"
$projContent += " <ErrorReport>prompt</ErrorReport>`r`n"
$projContent += " <WarningLevel>4</WarningLevel>`r`n"
$projContent += " </PropertyGroup>`r`n"
$projContent += " <PropertyGroup Condition=`" '`$(Configuration)|`$(Platform)' == 'Release|AnyCPU' `">`r`n"
$projContent += " <DebugType>pdbonly</DebugType>`r`n"
$projContent += " <Optimize>true</Optimize>`r`n"
$projContent += " <OutputPath>bin\</OutputPath>`r`n"
$projContent += " <DefineConstants>TRACE</DefineConstants>`r`n"
$projContent += " <ErrorReport>prompt</ErrorReport>`r`n"
$projContent += " <WarningLevel>4</WarningLevel>`r`n"
$projContent += " </PropertyGroup>`r`n"
$projContent += " <ItemGroup>`r`n"
$projContent += " <Reference Include=`"Microsoft.CSharp`" />`r`n"
$projContent += " <Reference Include=`"System.ServiceModel`" />`r`n"
$projContent += " <Reference Include=`"System.Transactions`" />`r`n"
$projContent += " <Reference Include=`"System.Web.DynamicData`" />`r`n"
$projContent += " <Reference Include=`"System.Web.Entity`" />`r`n"
$projContent += " <Reference Include=`"System.Web.ApplicationServices`" />`r`n"
$projContent += " <Reference Include=`"System.ComponentModel.DataAnnotations`" />`r`n"
$projContent += " <Reference Include=`"System`" />`r`n"
$projContent += " <Reference Include=`"System.Data`" />`r`n"
$projContent += " <Reference Include=`"System.Core`" />`r`n"
$projContent += " <Reference Include=`"System.Data.DataSetExtensions`" />`r`n"
$projContent += " <Reference Include=`"System.Web.Extensions`" />`r`n"
$projContent += " <Reference Include=`"System.Xml.Linq`" />`r`n"
$projContent += " <Reference Include=`"System.Drawing`" />`r`n"
$projContent += " <Reference Include=`"System.Web`" />`r`n"
$projContent += " <Reference Include=`"System.Xml`" />`r`n"
$projContent += " <Reference Include=`"System.Configuration`" />`r`n"
$projContent += " <Reference Include=`"System.Web.Services`" />`r`n"
$projContent += " <Reference Include=`"System.EnterpriseServices`" />`r`n"
$projContent += " </ItemGroup>`r`n"
$projContent += " <PropertyGroup>`r`n"
$projContent += " <VisualStudioVersion Condition=`"'`$(VisualStudioVersion)' == ''`">10.0</VisualStudioVersion>`r`n"
$projContent += " <VSToolsPath Condition=`"'`$(VSToolsPath)' == ''`">`$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v`$(VisualStudioVersion)</VSToolsPath>`r`n"
$projContent += " </PropertyGroup>`r`n"
$projContent += " <Import Project=`"`$(MSBuildBinPath)\Microsoft.CSharp.targets`" />`r`n"
$projContent += " <Import Project=`"`$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets`" Condition=`"'`$(VSToolsPath)' != ''`" />`r`n"
$projContent += " <Import Project=`"`$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets`" Condition=`"false`" />`r`n"
$projContent += " <ProjectExtensions>`r`n"
$projContent += " <VisualStudio>`r`n"
$projContent += " <FlavorProperties GUID=`"{349c5851-65df-11da-9384-00065b846f21}`">`r`n"
$projContent += " <WebProjectProperties>`r`n"
$projContent += " <UseIIS>False</UseIIS>`r`n"
$projContent += " <AutoAssignPort>True</AutoAssignPort>`r`n"
$projContent += " <DevelopmentServerPort>58060</DevelopmentServerPort>`r`n"
$projContent += " <DevelopmentServerVPath>/</DevelopmentServerVPath>`r`n"
$projContent += " <IISUrl>`r`n"
$projContent += " </IISUrl>`r`n"
$projContent += " <NTLMAuthentication>False</NTLMAuthentication>`r`n"
$projContent += " <UseCustomServer>True</UseCustomServer>`r`n"
$projContent += " <CustomServerUrl>http://localhost/</CustomServerUrl>`r`n"
$projContent += " <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>`r`n"
$projContent += " </WebProjectProperties>`r`n"
$projContent += " </FlavorProperties>`r`n"
$projContent += " </VisualStudio>`r`n"
$projContent += " </ProjectExtensions>`r`n"
$projContent += " <!-- To modify your build process, add your task inside one of the targets below and uncomment it. `r`n"
$projContent += " Other similar extension points exist, see Microsoft.Common.targets.`r`n"
$projContent += " <Target Name=`"BeforeBuild`">`r`n"
$projContent += " </Target>`r`n"
$projContent += " <Target Name=`"AfterBuild`">`r`n"
$projContent += " </Target>`r`n"
$projContent += " -->`r`n"
$projContent += "</Project>"
# Save the proj file
$projContent | Out-File ([System.IO.Path]::Combine("Template", "MyExtension.csproj")) -Encoding "UTF8" -NoNewline
# Create the template zip file
[System.IO.Directory]::CreateDirectory("Extension\ProjectTemplates\CSharp\Web\1033")
[System.IO.Compression.ZipFile]::CreateFromDirectory("Template", "Extension\ProjectTemplates\CSharp\Web\1033\MyExtension.zip")
# Create a content types xml file (an error will be thrown if this does not exist)
$conentTypesContent = "<?xml version=`"1.0`" encoding=`"utf-8`"?><Types xmlns=`"http://schemas.openxmlformats.org/package/2006/content-types`"><Default Extension=`"vsixmanifest`" ContentType=`"text/xml`" /><Default Extension=`"zip`" ContentType=`"application/zip`" /></Types>"
# Save the content types file
$conentTypesContent | Out-File -literalPath "Extension\[Content_Types].xml" -Encoding "UTF8" -NoNewline
# Now create an extension manifest for the visual studio template
$extensionContent = "<PackageManifest Version=`"2.0.0`" xmlns=`"http://schemas.microsoft.com/developer/vsx-schema/2011`">`r`n"
$extensionContent += " <Metadata>`r`n"
$extensionContent += " <Identity Id=`"MyExtension - 1`" Version=`"0.1.0`" Language=`"en-US`" Publisher=`"MyExtension.net Ltd`" />`r`n"
$extensionContent += " <DisplayName>MyExtension Project Template</DisplayName>`r`n"
$extensionContent += " <Description xml:space=`"preserve`">MyExtension Project Template Extension</Description>`r`n"
$extensionContent += " </Metadata>`r`n"
$extensionContent += " <Installation>`r`n"
$extensionContent += " <InstallationTarget Id=`"Microsoft.VisualStudio.Community`" Version=`"[14.0]`" />`r`n"
$extensionContent += " </Installation>`r`n"
$extensionContent += " <Dependencies>`r`n"
$extensionContent += " <Dependency Id=`"Microsoft.Framework.NDP`" DisplayName=`"Microsoft .NET Framework`" Version=`"[4.5,)`" />`r`n"
$extensionContent += " </Dependencies>`r`n"
$extensionContent += " <Assets>`r`n"
$extensionContent += " <Asset Type=`"Microsoft.VisualStudio.ProjectTemplate`" Path=`"ProjectTemplates`" />`r`n"
$extensionContent += " </Assets>`r`n"
$extensionContent += "</PackageManifest>"
# Save the extension file
$extensionContent | Out-File "Extension\extension.vsixmanifest" -Encoding "UTF8" -NoNewline
# Create the extension zip file
[System.IO.Compression.ZipFile]::CreateFromDirectory("Extension", "MyExtension.vsix")
# Delete the temporary directories
[System.IO.Directory]::Delete("Extension", $true)
[System.IO.Directory]::Delete("Template", $true)
When I run the script it successfully produces a MyExtension.vsix file. When I execute that file it installs the extension but if I now open Visual Studio 2015 and create a new project the project template (C# -> Web) doesn't exist.
However if you do the following steps then it works fine (make sure you have uninstalled the old extension first):
- Rename the MyExtension.vsix file to MyExtension.zip and then extract to a directory
- Go into that directory and select all the files, right click and select Send to Compressed Folder
- Rename the generated zip file to MyExtension.vsix
- Execute this file to install the extension
It's taken me hours to even notice this scenario fixes the issue. However this is a hack and in the long run it's going to become quite tedious. I was wondering if anyone knew how my PowerShell script can be changed to fix this.
Thanks