MSBuild is the build system used by Visual Studio, though it can also be invoked from the command line. You can integrate Dotfuscator into your Visual Studio projects using Dotfuscator's MSBuild targets. Dotfuscator also provides MSBuild tasks that can be called from your own custom build targets.
This page gives a functional overview of these components and explains best practices for using them. For a complete technical reference, see the MSBuild Reference page.
- MSBuild Targets
- Choosing What to Protect
- Controlling Which Assemblies Are Protected
- Importing the Targets
- Setting Properties for the Targets
- Initial Build
- About the Config File
- About the Report Files
- Subsequent Builds
- MSBuild Tasks
- Legacy MSBuild Targets
Dotfuscator provides MSBuild targets that can be imported into Visual Studio projects to automatically apply protection during builds. The advantages of using these MSBuild targets include:
Dotfuscator applies its protection as part of your normal build process in Visual Studio and MSBuild, instead of requiring you to apply the protection as a separate build step.
Dotfuscator runs before the packaging steps of your build, making it easier to apply protection to apps distributed through a packaged format, such as Universal Windows (UWP) apps.
The integration automatically generates and maintains a Dotfuscator config file based on your project's references, ensuring that all of your code is protected, even those in different projects of your solution.
The Protect Your App page illustrates how easy it is to get started using these targets to protect a typical project. This section contains additional details useful for more advanced cases, such as how to choose which projects and configurations to protect.
PreEmptive.Dotfuscator.Common.targetsfile. For the deprecated MSBuild targets defined in
PreEmptive.Dotfuscator.targets, see Legacy MSBuild Targets.
Once imported and enabled, these MSBuild targets run during the build automatically, after compilation but before packaging steps. If you want tighter control over how and when in the build process Dotfuscator runs, you can call the MSBuild tasks from your own MSBuild targets instead.
For technical details of the MSBuild targets, see Targets Reference.
Choosing What to Protect
Adding Dotfuscator to a build will increase your app's security, but it also adds additional time to your builds, introduces the risk of misconfiguration or runtime issues, and can make it harder to debug your app.
Given this, you should only use Dotfuscator for projects and build configurations that need it, such as the
Release configuration of an executable project.
Choosing Which Projects to Protect
When Dotfuscator is integrated into a Visual Studio project, it protects all assemblies in the project's output directory (e.g.,
ProjectDir\bin\Release) that originated from the project's solution by default.
Because of this, you do not need to integrate Dotfuscator into all projects in your solution in order to protect all the assemblies you distribute.
DotfuscatorIncludeAsInputMSBuild Property to the desired project file (e.g.
As an example, consider the following solution,
MySolution.sln, and its dependencies.
We want to distribute
MyApp.exe and its dependencies to untrusted users, and we want Dotfuscator to protect all of the code we own and distribute.
LibB.dll are never distributed except alongside
If we integrate Dotfuscator into just the
MyApp project and enable protection for the
Release configuration, Dotfuscator will protect three assemblies in the
Two assemblies will also appear in the
MyApp\bin\Release directory but not be protected:
Assemblies in output directories of other projects (e.g.,
LibA\bin\Release) will also be unprotected.
There are three important points to learn from this example:
Dotfuscator protects only the assemblies in the integrated project's output directory.
While the copy of
MyApp\bin\Releaseis protected, copies of the same assembly in
LibB\bin\Releaseare not protected. A similar rule applies for
Dotfuscator only needs to be integrated into final output projects, i.e. those whose output you intend to directly distribute.
In this example, integrating Dotfuscator into the
LibBprojects is unnecessary. The only way an untrusted user would receive the
LibB.dllassemblies would be as dependencies of
MyApp, and Dotfuscator protects the copies of these assemblies in the
If your solution has multiple output projects, you will need to integrate Dotfuscator into each. In an alternate scenario, where
LibAis also distributed to untrusted users (such as an API library for developers outside of your organization to reference), we would need to integrate Dotfuscator into both the
MyAppproject and the
LibAproject, ensuring Library Mode is enabled for the latter.
Dotfuscator will protect only your code (by default).
Assemblies external to your solution, such as from NuGet (
ExtLib1.dll) or a path on the filesystem (
ExtLib2.dll), are not protected by default. You can protect external assemblies on disk with the
DotfuscatorIncludeAsInputitem metadata as described in "Controlling Which Assemblies Are Protected". This version of Dotfuscator does not support protecting assemblies that have already been packaged into a NuGet Package.
ProjectReferenceItems recursively from the integrated project. If one of your projects references another project's output assembly through a
ReferenceItem, the latter assembly will only be protected if it is manually included with the
DotfuscatorIncludeAsInputitem metadata. See Controlling Which Assemblies Are Protected for more information.
Choosing Which Configurations to Protect
Builds in Visual Studio solutions and projects are parameterized by configuration.
By default, most solutions have two configurations,
Each of the projects has matching configurations, so that when we build the solution in
Release, all of the projects themselves also build in their respective
Once Dotfuscator is integrated into a project, you will need to know which of these configurations should be protected. Here are some guidelines for choosing what to protect:
You should apply Dotfuscator to all releasable build configurations. You want all builds given to untrusted users to be protected.
You should apply Dotfuscator to build configurations that will be tested. As with any static analysis tool, Dotfuscator cannot determine everything about your app's behavior. You will need to test the protected app to discover and address runtime issues.
You should NOT apply Dotfuscator to build configurations meant exclusively for debugging. Compared to debugging unprotected code, debugging obfuscated code is a much more challenging and limited experience. Even when you have the source code present and Dotfuscator is configured to automatically emit updated debugging symbols, some debugging features will be unavailable. Because of this, we don't recommend running Dotfuscator as part of your local development configurations.
Following these guidelines usually means you will protect the
Release configuration but not
If your project has more configurations, you may also need to protect some of those; for example, in a Xamarin.iOS project, you should protect
Controlling Which Assemblies Are Protected
When the Dotfuscator MSBuild targets are integrated into a project and enabled, Dotfuscator's default behavior is to protect the integrated project's assembly, as well as every assembly copied to the integrated project's output directory from other projects in the same solution. However, assemblies external to the solution will not be protected by default.
To exclude a referenced project from Dotfuscator, add the
DotfuscatorIncludeAsInput tag to the project file (ex:
.csproj) of the project you want to exclude, and set the value to
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <!-- Additional Property Tags --> <DotfuscatorIncludeAsInput>false</DotfuscatorIncludeAsInput> <!-- Additional Property Tags --> </PropertyGroup> </Project>
To re-enable protection on the assembly, you can either delete the tag entirely or set its value to
Including External Assemblies
To add an external assembly as an input to Dotfuscator, add the
DotfuscatorIncludeAsInput tag to the
Reference item for that assembly and set the value to
You can add the
DotfuscatorIncludeAsInput tag to any input project that references the external assembly, not just the project configured with Dotfuscator.
<Project Sdk="Microsoft.NET.Sdk"> <ItemGroup> <Reference Include="ExternalAssembly"> <HintPath>..\path\to\the\assembly\ExternalAssembly.dll</HintPath> <DotfuscatorIncludeAsInput>true</DotfuscatorIncludeAsInput> </Reference> </ItemGroup> </Project>
To disable protection on the assembly, change the
DotfuscatorIncludeAsInput tag's value to
false or remove it entirely.
DotfuscatorIncludeAsInputtag will only work with the
Referenceitem, and is not supported on any
DotfuscatorIncludeAsInput tags used in the examples above may have the same syntax, the first is a MSBuild Project Property, while the second is MSBuild Item Metadata.
The usage for both is essentially the same, although the Project Property must be added to a project file which does not directly integrate Dotfuscator.
The metadata, however, can be added to a
Reference item in any file.
DotfuscatorIncludeAsInput Project Property is applied recursively.
This means that any in-solution project referenced by a protected project will also be protected, while an in-solution project referenced by an excluded project will be excluded.
In the event that an in-solution project is referenced by both protected and excluded projects, the project in question will be protected by default.
DotfuscatorIncludeAsInput metadata on
Reference items in excluded projects.
Importing the Targets
Dotfuscator's MSBuild targets file,
PreEmptive.Dotfuscator.Common.targets, is installed in Dotfuscator's MSBuild extensions directory.
Thus these targets can be imported into a chosen project by editing the project file and adding the following line to the end of the file, right before the closing
If your project uses the new SDK-based format introduced by .NET Core and .NET Standard, you will also need to replace the root
Sdk attribute with explicit imports to ensure Dotfuscator's targets are imported after the SDK's targets.
<Project> <!-- ORIGINALLY WAS: <Project Sdk="Microsoft.NET.Sdk"> --> <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" /> <!-- ...existing MSBuild definitions... --> <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" /> <Import Project="$(MSBuildExtensionsPath)\PreEmptive\Dotfuscator\4\PreEmptive.Dotfuscator.Common.targets"/> </Project>
If your build agents are using the Dotfuscator NuGet package, the directory containing the targets file will differ between the build agents and your developer environment.
To have your project accommodate both locations, define a
DotfuscatorMSBuildDir property to contain the currently-used directory's path.
Default the property to the developer machine directory's path, as seen below:
<!-- Some time before the PreEmptive.Dotfuscator.Common.targets import --> <PropertyGroup> <DotfuscatorMSBuildDir Condition="'$(DotfuscatorMSBuildDir)' == ''">$(MSBuildExtensionsPath)\PreEmptive\Dotfuscator\4</DotfuscatorMSBuildDir> </PropertyGroup> <!-- ... --> <!-- REPLACES the PreEmptive.Dotfuscator.Common.targets import --> <Import Project="$(DotfuscatorMSBuildDir)\PreEmptive.Dotfuscator.Common.targets"/>
On developer machines,
DotfuscatorMSBuildDir will default to the MSBuild extensions directory.
You will need to override this on build agents by setting the
DotfuscatorMSBuildDir property explicitly when calling MSBuild, e.g.:
msbuild YourSolution.sln /p:DotfuscatorMSBuildDir="C:\nuget_install_location\PreEmptive.Protection.Dotfuscator.Pro\tools\msbuilddir"
Setting Properties for the Targets
The targets are controlled by MSBuild properties, which must be declared earlier in the project file than the import statement for the targets. This section explains commonly-used properties; for a full list, see Available Properties.
By default, Dotfuscator does not run during the build.
To opt-in, set the
DotfuscatorEnabled property to
<PropertyGroup> <DotfuscatorEnabled>true</DotfuscatorEnabled> </PropertyGroup>
However, you'll probably only want Dotfuscator to protect certain configurations, like
To only protect one configuration, specify a
<PropertyGroup> <DotfuscatorEnabled Condition="'$(Configuration)' == 'Release'">true</DotfuscatorEnabled> </PropertyGroup>
To protect multiple configurations (e.g., for Xamarin.iOS apps), repeat the definition with a different
Condition for each configuration:
<PropertyGroup> <DotfuscatorEnabled Condition="'$(Configuration)' == 'Release'">true</DotfuscatorEnabled> <DotfuscatorEnabled Condition="'$(Configuration)' == 'Ad-Hoc'">true</DotfuscatorEnabled> <DotfuscatorEnabled Condition="'$(Configuration)' == 'AppStore'">true</DotfuscatorEnabled> </PropertyGroup>
Alternatively, instead of declaring
DotfuscatorEnabled in its own
<PropertyGroup>, you can declare it in pre-existing groups that are already selected based on the configuration and target platform.
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release\</OutputPath> <!-- ...other properties... --> <!-- Enable Dotfuscator for Release|AnyCPU --> <DotfuscatorEnabled>true</DotfuscatorEnabled> </PropertyGroup>
If you go this route, don't forget to set
DotfuscatorEnabled for all of a configuration's target platforms.
For instance, if you want to protect the
Release configuration, check for
<PropertyGroup> sections for
To perform protection, Dotfuscator requires a config file (named
DotfuscatorConfig.xml by default).
When setting up Dotfuscator's MSBuild targets, you should configure the targets to generate this file.
To do so, set the
DotfuscatorGenerateConfigFileIfMissing property to
<PropertyGroup> <DotfuscatorGenerateConfigFileIfMissing>true</DotfuscatorGenerateConfigFileIfMissing> </PropertyGroup>
After importing the targets and setting the necessary properties, you can build the project in Visual Studio or through the MSBuild command line.
Be sure to select a configuration for which you have set
The first time you build, assuming you set
true, Dotfuscator will produce a new config file and emit a warning to this effect.
You can ignore the warning on the initial build; it's intended to signal possible build issues in subsequent builds, where the config file is expected to already exist.
Every time you build, Dotfuscator will apply protection to the assemblies in the project's output directory (e.g.,
For a detailed example of which assemblies are protected, see Choosing Which Projects to Protect.
Dotfuscator also produces report files as part of every build.
When the build completes, you can run the app locally to ensure it continues to behave as expected.
About the Config File
Dotfuscator uses a config file to control its various protection settings.
By default, this config file is saved as
DotfuscatorConfig.xml in your project directory.
You can change the path of the config file by setting the
DotfuscatorConfigPath property in your project file.
(Note that if the config file has already been generated at the old path, you will need to move it to the new path to retain your settings.)
During the initial build, Dotfuscator generates a config file which offers a default level of protection. As part of subsequent builds, Dotfuscator will keep the config file up-to-date with any changes you make to project references in the solution.
The config file is an input to your build process. You should:
Add this file to version control. This allows build agents and fellow team members to build the project with the same protection settings. You can also track your team's changes to the file, and thus the protection settings, over time.
Disable config file generation. If Dotfuscator can't find the config file during a build, it will automatically generate one. While this was helpful for the initial build, leaving this feature enabled for subsequent builds will cause these builds to pass, even if the config file is missing. If the config file is removed by accident, this could lead to builds that are only protected with the default settings, rather than your customized protection configuration.
To disable the config file generation feature and have the build error if the config file is missing, edit your project file to change the following property to
Never distribute this file outside of your organization. Sensitive information, such as the names of source code elements, can reside in the config file.
Finally, note that the config file appears under your project's node in Visual Studio's Solution Explorer. This is because the MSBuild targets add the config file as an MSBuild item to your project, which ensures Visual Studio will consider changes to the config file when deciding whether to build the project.
About the Report Files
Dotfuscator generates various report files whenever it runs as part of a build. The following report files can be generated, depending on the protection settings:
Renaming.xml: the renaming report file, also known as the renaming map file.
Removal.xml: the removal report file.
SmartObfuscation.xml: the Smart Obfuscation report file.
platform, corresponding to the current build configuration and platform. You can use these values in your custom report file paths to ensure reports from different build configurations and platforms stay separate.
Report files are outputs of a build, much like the assemblies themselves (in the
Ignore these files in version control. These files are updated every time Dotfuscator runs as part of the build, so your version control should ignore the
Archive these files to a secure, versioned location when releasing the app. When Dotfuscator's Renaming obfuscation is enabled, stack traces from the app will contain obfuscated type and method names. This can make it difficult to diagnose issues reported by customers.
To solve this, one of the reports Dotfuscator produces, the renaming map file, associates each renamed code element with the element's original name. When you release your app, you should copy this file (and the other reports) to a secure location. Then, if a customer gives you an obfuscated stack trace, you can pass it and the archived map file to Dotfuscator's tool for decoding obfuscated stack traces, which will produce a stack trace matching the original source code.
Never distribute these files outside of your organization. The information in these reports can be used to undo parts of Dotfuscator's protection, even by those who do not have Dotfuscator.
Once you've built the project once and then configured the config file and report files as recommended, you can continue building your project locally. If you push your changes to your team's version control, you will also start getting protected builds from your automated build system.
You can continue to develop and test your app with Dotfuscator's protection. You can also enhance this protection.
Dotfuscator includes MSBuild tasks that can be called from a customized MSBuild project.
The most commonly-used task is the
Dotfuscate task, which calls Dotfuscator to perform protection specified by an existing Dotfuscator config file.
For a full list of tasks and their associated parameters, see MSBuild Task Reference.
Referencing the Dotfuscate Task
The tasks are declared in the
PreEmptive.Dotfuscator.Tasks.dll assembly in Dotfuscator's MSBuild extensions directory.
Tasks are located in the namespace
So to reference the
Dotfuscate task, you can add the following to your custom MSBuild project file:
<UsingTask TaskName="PreEmptive.Tasks.Dotfuscate" AssemblyFile="$(MSBuildExtensionsPath)\PreEmptive\Dotfuscator\4\PreEmptive.Dotfuscator.Tasks.dll" />
Calling the Dotfuscate Task
Once referenced, you can then call the
Dotfuscate task from a custom MSBuild target.
As a simple example, consider the following
AfterBuild target in a C# project:
<Target Name="AfterBuild"> <Dotfuscate ConfigPath="MyDotfuscatorConfig.xml"> <Output TaskParameter="OutputAssemblies" ItemName="MyDotfuscatorOutputAssemblies" /> </Dotfuscate> <Message Importance="high" Text="Dotfuscator wrote: @(MyDotfuscatorOutputAssemblies)" /> </Target>
AfterBuild declaration overrides a target predefined by MSBuild.
As a result, MSBuild will run the target after running the normal build steps.
We start by having the target call the
We provide two task parameters, one input and one output:
We set the required
ConfigPathinput parameter to
MyDotfuscatorConfig.xml. Dotfuscator will perform the protection specified by that Dotfuscator config file, located in the project's directory.
We specify that MSBuild items from the
OutputAssembliesoutput parameter will be stored as a set of MSBuild items named
MyDotfuscatorOutputAssemblies. This output parameter exposes the full paths to assemblies Dotfuscator wrote.
We then log the paths Dotfuscator wrote by having the target call the
For a full list of parameters, see Dotfuscate Task.
Legacy MSBuild Targets
For compatibility with existing projects, Dotfuscator includes a separate set of MSBuild targets defined in the
Because these targets are not designed for easy integration into an existing Visual Studio or MSBuild project, we recommend either using the newer MSBuild targets to integrate Dotfuscator into your project or calling the appropriate MSBuild tasks from your own target.