-SOURCE, -OUTPUT, -KEY, -WRITELOG, -ALLPRIVATE, -CONCEALSTRINGS, -FLOW), set parallel builds to 1, run your tests against the protected binary, then package. CLI automation requires the Pro or Enterprise edition.
The objective is straightforward: the binary that leaves the developer machine (or CI agent) is already protected. No manual GUI step is needed for each release.
The problem with manual obfuscation
Manual obfuscation doesn’t scale
Using the Skater GUI for a one-off demo works fine. But it breaks down when you have:
- Multiple developers and frequent Release builds
- CI/CD pipelines that must produce protected artifacts without human interaction
- Setup/Deployment projects that package whatever is currently in the output folder
If obfuscation is optional or forgotten, an unprotected assembly reaches customers. Integrating the CLI into the Visual Studio build event makes protection automatic and repeatable.
Small C# example (context)
Assume a simple class-library or executable project that produces MyApp.dll (or MyApp.exe) under bin\Release\net8.0\. The project may be strong-named with KeyFile.snk in the project root. After a normal build the assembly is fully readable by ILSpy. The post-build step below replaces that assembly with an obfuscated version before any further packaging occurs.
What happens without build integration
- Developers must remember to open the Skater GUI after every Release build.
- CI agents produce unprotected binaries unless a separate manual (or ad-hoc script) step is added.
- Setup projects that reference “Primary Output” can package the original, unobfuscated file even when a developer ran Skater once by hand.
- Inconsistent protection settings appear across machines because there is no single, version-controlled command line.
Step-by-step Skater configuration in Visual Studio
-
Create (or confirm) settings in the Skater GUI
Launch Skater, load the Release assembly once, choose the desired protection options (private/public renaming, string encryption, control-flow intensity, WPF/BAML if applicable), and save a named settings profile. The CLI can later reuse that profile or accept explicit flags. -
Open Project Properties
In Solution Explorer right-click the project → Properties. -
Go to Build Events
Select the Build Events tab. -
Paste the Post-Build command
In the Post-build event command line box insert the command shown below. Restrict it to Release configurations if you prefer (see the conditional example later). -
Disable parallel project builds
Tools → Options → Projects and Solutions → Build and Run → set Maximum number of parallel project builds to 1. This prevents file-lock races while Skater rewrites the assembly. -
Build the project
Perform a Release build. The Output window should show Skater running after the compiler finishes. Check for a zero exit code and the presence of the log file. -
Test, then package
Run your test suite against the protected binary, then proceed to packaging / installer / publish steps.
Exact Post-Build command
The documentation provides the following command (line continuation with ^ is for readability inside the Visual Studio dialog; you may also place it on a single line):
"C:\Program Files (x86)\RustemSoft\Skater\Skater.exe" ^
-SOURCE="$(TargetPath)" ^
-OUTPUT="$(TargetPath)" ^
-KEY="$(ProjectDir)KeyFile.snk" ^
-WRITELOG="$(TargetDir)Skater.log" ^
-ALLPRIVATE ^
-CONCEALSTRINGS ^
-FLOW
Skater.exe if you installed to a different location (for example C:\Program Files\Rustemsoft LLC\Skater\Skater.exe or a custom folder). Always test the command from a plain Command Prompt first.
Parameter breakdown
| Parameter | Purpose |
|---|---|
-SOURCE="$(TargetPath)" |
Input assembly. $(TargetPath) expands to the full path of the file the compiler just produced (e.g. …\bin\Release\net8.0\MyApp.dll). |
-OUTPUT="$(TargetPath)" |
Output path. Writing back to the same location overwrites the original with the protected version so every subsequent step (tests, packaging) automatically uses the obfuscated binary. |
-KEY="$(ProjectDir)KeyFile.snk" |
Strong-name key used to re-sign the assembly after modification. Omit this flag if the project is not strong-named. |
-WRITELOG="$(TargetDir)Skater.log" |
Writes a diagnostic log next to the output assembly. Useful for diagnosing failures on build servers. |
-ALLPRIVATE |
Obfuscates private (and typically internal) members for deeper name protection. |
-CONCEALSTRINGS |
Enables string encryption so literals are not visible in plain text inside the assembly. |
-FLOW |
Applies control-flow obfuscation (Lightweight by default in most configurations). |
You can also invoke a previously saved settings profile by name (created in the GUI) instead of listing every flag:
"C:\Program Files (x86)\RustemSoft\Skater\Skater.exe" MySavedSettingsProfile
The explicit-flag form shown above is preferred when you want the command itself to be self-documenting and version-controlled.
Run only on Release builds
To avoid slowing Debug builds, wrap the command with a configuration check:
if "$(ConfigurationName)" == "Release" (
"C:\Program Files (x86)\RustemSoft\Skater\Skater.exe" ^
-SOURCE="$(TargetPath)" ^
-OUTPUT="$(TargetPath)" ^
-KEY="$(ProjectDir)KeyFile.snk" ^
-WRITELOG="$(TargetDir)Skater.log" ^
-ALLPRIVATE ^
-CONCEALSTRINGS ^
-FLOW
)
.bat script example
For local testing or for use from a CI agent that does not expand Visual Studio macros, a small batch file is convenient:
@echo off
setlocal
REM Adjust these paths to match your machine / pipeline
set SKATER="C:\Program Files (x86)\RustemSoft\Skater\Skater.exe"
set ASSEMBLY=%~dp0bin\Release\net8.0\MyApp.dll
set KEYFILE=%~dp0KeyFile.snk
set LOGFILE=%~dp0bin\Release\net8.0\Skater.log
echo Obfuscating %ASSEMBLY% ...
%SKATER% -SOURCE="%ASSEMBLY%" -OUTPUT="%ASSEMBLY%" -KEY="%KEYFILE%" -WRITELOG="%LOGFILE%" -ALLPRIVATE -CONCEALSTRINGS -FLOW
if %ERRORLEVEL% neq 0 (
echo Skater failed with exit code %ERRORLEVEL%
exit /b %ERRORLEVEL%
)
echo Done. Log written to %LOGFILE%
endlocal
Save as obfuscate.bat next to the project (or on the build agent), then call it from a post-build event or from a pipeline task:
call "$(ProjectDir)obfuscate.bat"
Resulting output
When the command succeeds you typically see console output similar to:
The file at $(TargetPath) is now the protected assembly. Opening it in ILSpy shows renamed identifiers, encrypted strings, and (when control-flow is enabled) hard-to-follow method bodies. The log file contains timestamps, selected options, and any warnings.
Testing the obfuscated application
- Build in Release configuration and confirm the post-build event runs without errors.
- Inspect
Skater.logfor warnings (missing key, excluded members, etc.). - Run the full automated test suite against the binary that now sits in the output folder.
- Manually exercise critical paths (licensing, payments, UI) on the protected build.
- Open the protected assembly in ILSpy / dnSpy and verify that names and strings are no longer readable in plain form.
- Only after the above steps succeed, proceed to packaging or publishing.
Common failures and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| File is locked / access denied | Parallel project builds or another process still has the DLL open | Set parallel project builds to 1; ensure no other tool is locking the output file |
| “Skater.exe is not recognized” | Wrong path or CLI not installed (Basic edition) | Verify the full path to Skater.exe; upgrade to Pro/Enterprise for CLI support |
| Strong-name validation fails at runtime | Assembly was modified but not re-signed | Supply a valid -KEY path or disable strong-naming if not required |
| Setup project ships unobfuscated binary | “Primary Output” captured the file before Skater ran, or PreBuildEvent was not used on the setup project | Obfuscate the application project first; add the compiled (already protected) file manually to the setup project, or use PreBuildEvent on the deployment project as described in the official docs |
| Post-build runs on every Debug build and slows iteration | No configuration filter | Wrap the command in if "$(ConfigurationName)" == "Release" |
| Exit code non-zero, log shows missing settings | Flags conflict with a saved profile or required options are absent | Test the exact command line from a Command Prompt; simplify flags or rely on a known-good saved profile |
Production recommendations
- Protect only Release (and any official staging) configurations. Keep Debug builds fast and easy to debug.
- Version-control the post-build command (or the .bat wrapper) so every developer and the CI agent use identical flags.
- Always re-sign strong-named assemblies with
-KEYafter obfuscation. - Treat the protected binary as the only shippable artifact. Never package from an intermediate unobfuscated folder.
- Disable parallel project builds (or carefully orchestrate project order) to avoid file-lock races.
- Store the log and rename map as build artifacts for later diagnostics.
- For CI/CD (Azure DevOps, GitHub Actions, etc.) use the same CLI command inside a pipeline task after the compile step; the Visual Studio post-build event and the pipeline script should stay in sync.
- Test the protected binary on a clean machine that matches the target runtime before releasing.
FAQ
Which edition do I need for post-build automation?
Can I call a saved settings profile instead of listing flags?
Skater.exe MyProfileName. Explicit flags remain useful when you want the command line to be self-contained and easy to review in source control.