Using Visual Studio 2012, the manifest signed, but the assembly is not - deployment

I am using ClickOnce application deployment, and I just got my code certificate from Verisign. I am using this certificate to sign the manifest.
When I download and install the application, the smartscreen comes up with my name on it (lame, but I think this is what is supposed to happen). When the ClickOnce installer completes, the smartscreen comes up again for the execution of the actual application, here it says 'Unknown Publisher'.
Does ClickOnce not sign the assemblies by default? How do I do this?
Edit: Currently I am letting VS sign my manifest (installer) for the ClickOnce, and I am setting a Post-build event to sign my assembly. But still when I install the application it says 'unknown publisher' when I go to actually run it.

That does not sound right to me. I have used exactly the same workflow for multiple applications, and it works fine. Most likely there is an issue with your postbuild step. Make sure that you sign EXE file inside the OBJ folder (because that's where ClickOnce takes all the files from) - not the BIN one.
Do ClickOnce publishing, go to the OBJ folder, right click on your application.exe file, and select properties. It should have six tabs - the last one being "Digital Signature":
If you don't have it, you don't sign your application properly.
And here is my postbuild step - note that I sign "RELEASE" configuration only:
<Target Name="SignOutput" AfterTargets="CoreCompile" Condition="'$(ConfigurationName)'=='Release'">
<PropertyGroup>
<TimestampServerUrl>http://timestamp.verisign.com/scripts/timestamp.dll</TimestampServerUrl>
<ApplicationDescription>my app</ApplicationDescription>
<SigningCertificateCriteria>/n "my company."</SigningCertificateCriteria>
</PropertyGroup>
<ItemGroup>
<SignableFiles Include="$(ProjectDir)obj\$(ConfigurationName)\$(TargetName)$(TargetExt)" />
</ItemGroup>
<Exec Condition=" '$(ConfigurationName)'=='Release'" Command=""c:\Program Files (x86)\Windows Kits\8.0\bin\x64\signtool.exe" sign $(SigningCertificateCriteria) /d "$(ApplicationDescription)" /t "$(TimestampServerUrl)" "%(SignableFiles.Identity)"" />
</Target>

Related

Configuring ClickOnce Package on Release

I'm working on a pipeline migration from an old manually implemented ci/cd solution to Azure DevOps.
There are some prebuilt functions/processes that I'm still reusing.
For example. Like how they package all their solution as artifacts.
I'm trying to keep the code changes as minimal as possible.
The build pipeline creates a ClickOnce package .zip.
Then on the release stage, the myapp.exe.config in the Application Files gets transformed via XML-Document-Transform. Also the application manifest <ApplicationName>.application gets manually edited through Powershell. The <deploymentProvider codebase="http://1.1.1.1/samplefolder/myapp.application" /> gets changed on release depending on the environment/path it is going to be deployed to.
Application Manifest
<asmv1:assembly ...>
<deployment ...>
<subscription>
<update>
<beforeApplicationStartup />
</update>
</subscription>
<deploymentProvider codebase="http://1.1.1.1/samplefolder/myapp.application" />
</deployment>
</asmv1:assembly>
Now I understand that this method requires Re-Signing of the whole package. They have a custom .exe file to re-sign the whole package (it's not mage.exe). Unfortunately, I cant reuse the said executable to re-sign it.
All I have is their Certificate Thumbprint. But I don't know what to do with it.
Questions:
What are my other options to re-sign the package?
Is there a better way to do this? Do I have to make another build step for this solution?
I have managed to sign the ClickOnce Appmanifest (*.application) and *.exe.manifest files on release by using dotnet mage. I've done this by adding the certificate (.pfx or .p12) file in the Secure Files and the certificate password in the pipeline variables.
Use the .NET Core task specify to use version 5.x.
optional step Re-install via dotnet tool update --global microsoft.dotnet.mage --version 5.0.0
Run the following in powershell
## Signing the exe.manifest file
dotnet mage -update "<folder>/Application Files/<assembly folder name>/<assemblyname>.exe.manifest" -fd "<folder>/Application Files/<folder>" -CertFile "$(SignKey.secureFilePath)" -Password "$(SignKeyPassword)"
## Signing the .Application file
dotnet mage -update "<the .Application full path>" -pu "$publisherURL" -pub "$(PublisherDetails)" -appmanifest "Application Files/<assembly folder name>/<assemblyname>.exe.manifest" -CertFile "$(SignKey.secureFilePath)" -Password "$(SignKeyPassword)"

Why does MSBuild only work properly in the developer command prompt?

I'm building a project locally using msbuild.exe like:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe MyProject.csproj
When I execute it through the Developer Command Prompt, everything works as expected.
However, when I execute it through the standard Command Prompt, I get an error saying:
The imported project "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\WebApplications\Microsoft.WebApplication.targets" was not found.
If I look up that folder path, I can indeed verify it's invalid (I only have VS 2015 installed on the machine).
So why isn't it working in Command Prompt, or conversely: why is it working in Developer Command Prompt?
Edit: The .csproj file is pretty much the Visual Studio 2015 default for an ASP.NET 4 website, and it specifies:
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
To me it seems it would default to 10.0 if VisualStudioVersion isn't set, but judging from the error message it's assuming VS version 12.0.
I realize I could just modify the .csproj file, replacing 12 with 14, but I'd rather not go for a workaround, but instead understand why it's working in the Developer Command Prompt, but not the standard one.
I'm guessing it potentially has to do with different environment variables, or something along those lines?
When you run Developer Command Prompt you basically run VsDevCmd.bat from VisualStudio's Tools folder and sets some environment variables for the Console that you will be working with. Without those msbuild can't find correct file.
For example it sets VisualStudioVersion environment variable
#rem VisualStudioVersion
#rem -------------------
#set VisualStudioVersion=14.0
It depends on the .csproj but I suspect you might have something similar in it
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
So if the $(VisualStudioVersion) is not defined (and it won't in standard Command Prompt) it will use the version 12. And when you run it through Developer Command Prompt this gets set to 14 and you're good to go.
Hans Passant pointed me in the right direction in the comments, and typing where msbuild.exe in the Developer Command Prompt showed that it had two paths to MSBuild:
C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe
The first one works in both the standard Command Prompt, and the Developer Command Prompt.
The second one (which my build script had retrieved from the registry) only works in Developer Command Prompt, probably explained by what Pawel said in his answer (essentially due to missing/different environment variables).
In my build script, I changed the registry path from...
HKLM\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0\
to...
HKLM\SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0\
...and that produces the proper (current) MSBuild path in the build script.

VS2015: recursively adding external content directories to AppX

I try to add a folder and its subfolders (~4000 files) as content to a C++ windows store app (in VS2015).
Heres the scenario:
G:\Game -> is the build directory
D:\data -> holds the original content
I've read there are some methods to declare external content in the .vxcproj file like that:
<ItemGroup>
<Content Include="D:\**">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<DeploymentContent>true</DeploymentContent>
</Content>
</ItemGroup>
This actually copies the contents of D:\data into the build-directory (G:\Game). This is great since the program can now be run & debugged. BUT: as soon as i deploy the project to the AppX Folder (G:\Game\AppX) the data-folder doesnt get deployed there.
G:\Game\game.exe
G:\Game\data\...
G:\Game\AppX
G:\Game\AppX\game.exe
(G:\Game\AppX\data\... - missing)
Any clues ?
After fiddling around for days, as of now i can state there is no way to do this properly in the Visual C++ - IDE (2012 / 2015) (it seemed to work with C# projects though).
The only way to achieve what i wanted to do is
a post-build-event using robocopy to copy/synch the data over to the AppX folder
Writing a script for the packaging / signing using MakeAppX.exe, SignTool.exe and 7-zip.

How to modify the csdef defined in a cspkg

To deploy to different azure environments I modify the csdef as part of the compilation step to change the host headers. Doing so requires building the cspkg once for each environment instead of being able to reuse the cspkg and specify different configs for deployment.
I would like to instead modify the csdef file of a cspkg after it has been created, without recompiling. Is that possible, and if so how?
I've done something similar to what you're after to differentiate between test and live environments. First of all you need to create a new .csdef file that you want to use for your alternate settings. This needs to be the complete file as we're just going to swap it out with the original one. Now we need to add this to the cloud project. Right click on the cloud project and select unload project. Right click on it again and select Edit [Name of project]. There's a section that looks a bit like this:
<ItemGroup>
<ServiceConfiguration Include="ServiceConfiguration.Test.cscfg" />
<ServiceDefinition Include="ServiceDefinition.csdef" />
<ServiceConfiguration Include="ServiceConfiguration.cscfg" />
</ItemGroup>
Add a new ServiceDefinition item that points to your newly created file. Now find the following line:
<Import Project="$(CloudExtensionsDir)Microsoft.WindowsAzure.targets" />
Then add this code block, editing the TargeProfile check to be the build configuration you're wanting to use for your alternate and ensuring that it points to your new .csdef file
<Target Name="AfterResolveServiceModel">
<!-- This should be run after it has figured out which definition file to use
but before it's done anything with it. This is all a bit hard coded, but
basically it should remove everything from the SourceServiceDefinition
item and replace it with the one we want if this is a build for test-->
<ItemGroup>
<!-- This is an interesting way of saying remove everything that is in me from me-->
<SourceServiceDefinition Remove="#(SourceServiceDefinition)" />
<TargetServiceDefinition Remove="#(TargetServiceDefinition)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetProfile)' == 'Test'">
<SourceServiceDefinition Include="ServiceDefinition.Test.csdef" />
</ItemGroup>
<ItemGroup Condition="'$(TargetProfile)' != 'Test'">
<SourceServiceDefinition Include="ServiceDefinition.csdef" />
</ItemGroup>
<ItemGroup>
<TargetServiceDefinition Include="#(SourceServiceDefinition->'%(RecursiveDirectory)%(Filename).build%(Extension)')" />
</ItemGroup>
<Message Text="Source Service Definition Changed To Be: #(SourceServiceDefinition)" />
</Target>
To go back to normal, right click on the project and select Reload Project. Now when you build your project, depending on which configuration you use, it will use different .csdef files. It's worth noting that the settings editor in is not aware of your second .csdef file so if you add any new settings through the GUI you will need to add them manually to this alternate version.
If you would want to just have a different CSDEF then you can do it easily by using CSPACK command prompt directly as below:
Open command windows and locate the folder where you have your CSDEF/CSCFG and CSX folder related to your Windows Azure Project
Create multiple CSDEF depend on your minor changes
Be sure to have Windows Azure SDK in path to launch CS* commands
USE CSPACK command and pass parameters to use different CSDEF and Output CSPKG file something similar to as below:
cspack <ProjectName>\ServiceDefinitionOne.csdef /out:ProjectNameSame.csx /out:ProjectOne.cspkg /_AddMoreParams
cspack <ProjectName>\ServiceDefinitionTwo.csdef /out:ProjectNameSame.csx /out:ProjectTwo.cspkg /_AddMoreParams
More about CSPACK: http://msdn.microsoft.com/en-us/library/windowsazure/gg432988.aspx
As far as I know, you can't easily modify the .cspkg after it is created. I guess you probably technically could as the .cspkg is a zip file that follows a certain structure.
The question I'd ask is why? If it is to modify settings like VM role size (since that's defined in the .csdef file), then I think you have a couple of alternative approaches:
Create a seperate Windows Azure deployment project (.csproj) for each variation. Yes, I realize this can be a pain, but it does allow the Visual Studio tooling to work well. The minor pain may be worth it to have the easier to use tool support.
Run a configuration file transformation as part of the build process. Similiar to a web.config transform.
Personally, I go with the different .csproj approach. Mostly because I'm not a config file transformation ninja . . . yet. ;) This was the path of least resistance and it worked pretty well so far.

MSTest.exe not copying all needed project DLLs?

I'm trying to get MSTest.exe to run, and it seems like testcontainer isn't being read properly; while my tests all run successfully in all config environments within Visual Studio.
the command I'm using is:
"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe" /nologo /usestderr /testSettings:"C:\temp\MyProject\Sources\MyProject\Local.testsettings" /searchpathroot:"C:\temp\MyProject\Binaries" /resultsfileroot:"C:\temp\MyProject\TestResults" /testcontainer:"C:\temp\MyProject\Binaries\MyProject.Services.Server.UnitTests.dll"
The project references within testcontainer project look like this:
<ItemGroup>
<ProjectReference Include="..\..\Services\MyProject.Services.Server\MyProject.Services.Server.csproj">
<Project>{92EC1999-CC0C-47DD-A4D6-17C3B1233C50}</Project>
<Name>MyProject.Services.Server</Name>
</ProjectReference>
<ProjectReference Include="..\..\SvcConfiguration\MyProject.ServiceConfiguration.Interfaces\MyProject.ServiceConfiguration.Interfaces.csproj">
<Project>{8E2E7BA9-75DB-458E-A184-AC1030EAD581}</Project>
<Name>MyProject.ServiceConfiguration.Interfaces</Name>
</ProjectReference>
<ProjectReference Include="..\..\SvcConfiguration\MyProject.ServiceConfiguration.Services\MyProject.ServiceConfiguration.Services.csproj">
<Project>{39514766-23A8-45DB-96EA-B6B4D9C8B086}</Project>
<Name>MyProject.ServiceConfiguration.Services</Name>
</ProjectReference>
</ItemGroup>
Neither the ServiceConfiguration.Interfaces nor the ServiceConfiguration.Services DLL is placed into the Out folder in TestResults.
The project GUIDs do match between the references and the referenced projects.
Is there something that I'm missing in the command line?
mstest.exe will not coy all referenced dll's.
See a blog post on this at https://web.archive.org/web/20111221110459/http://www.dotnetthoughts.net/2011/11/22/mstest-exe-does-not-deploy-all-items/
You can specify exactly what files are copied to the test directory using a test settings file. You can create multiple test settings files in Visual Studio, so you can have one for running from VS, another for running from MSTest, another for server CI builds, and so on. See here for more information: Create Test Settings to Run Automated Tests from Visual Studio
Use the /testsettings:<filename> option to specify it on the command line.
What seems to confuse people at first is that, by default, MSTest's "current directory" is not the MSTest launch directory, but the Out folder of the test results.
As mentioned previously, MSTest does not correctly infer all used assemblies, if you don't have a direct reference, it will not copy the assembly. That said, Visual Studio has similar behaviour in its build too, so a lot of people work around this by adding bogus code references - a terrible solution - I don't recommend it.
However, native DLLs are even more problematic, and I have found that explicitly copying them in the test configuration (test settings) works for them, just as for managed assemblies.
Whether it goes to Out or the build area depends on different factors, however, for the situations where it still doesn't work, you can use a DeploymentItem "hack", or, tweak your runsettings file.
Try looking at this answer: https://stackoverflow.com/a/33344573/2537017