documentation for developers


Crossroads is a dotnet core commandline tool packager for developers. This is a generic solution to host any application within Crossroads package executable and further launches application’s executable. Developers will specify arguments such as name, icon, version etc for branding during the package generation. The specified argument name will be used to rebrand the internal application.

Why Use Crossroads

Crossroads allows you to:

  • create an executable package.
  • customise your package with a name, icon, version and other attributes
  • run applications through crossroads generated package

How it Works

Crossroads, a commandline packager, generates an executable package by going through the following process:

Before Package Run
  • Takes all the required parameters(like name, version, icon) and a path to target an internal application(and its resources) to be packaged.
  • Bundles all the targeted resources as unit and embed it into the custom package(having the given name, version and icon) that was emitted.
  • Creates a metadata inside the package that can be used to inspect the package.
After Package Run
  • Initializes an internal launcher that
  • Unloads resource files that were inside the generated package into a temporary location.
  • Reads references and assembly files and
  • Spins the application as a child process.

Given sample basic command crossroads --name isaac --input app.exe --location C:\destination rebrands app.exe to isaac.exe and keeps its dependendecies in the destination directory.

When isaac.exe starts, it unzips and extracts all dependencies into a temp file and launches the packaged app.exe internally.


unzip -> temp_directory
launch app.exe


Project Solution Architecture

Solution-folder (ie. Crossroads)
|_ src
| |
_ project-folder (ie. Crossroads)
|_ test
| |
_ project-test-folder (ie. Crossroads.UnitTest)
|___ .gitignore (with this content)


  • You need to have a x64 bit of Windows 10/8 platform
  • You need to be running .NET Core SDK v3.0 or newer
  • Clone this repository
$ git clone
  • Navigate into the directory, crossroads
$ cd /crossroads

Package Generation

  • Build the project and navigate to \bin\Debug\netcoreapp3.1\ folder with:
$ cd \src\Crossroads\bin\Debug\netcoreapp3.1
  • Generate a package with this command below. For help, run .\crossroads --help:
$ .\crossroads package --name PACKAGE_NAME --icon ICON_PATH --version VERSION --location DESTINATION_OUTPUT_PATH --include APP_RESOURCE_PATH --command APP_EXECUTABLE_TO_BE_PACKAGED --args "APP_ARGUMENTS"


$ dotnet myms.exe --target "C:\Users\Mr. Erbynn\Desktop\packagedTarget" --packagedappArgs ""
Package Inspection

Inspect the metadata of the generated package by navigating to bin\Debug\netcoreapp3.1 and use the command below:

$ crossroads inspect --package PACKAGE_NAME

This displays a detail information of the pakaged app in json format.

How the build pipeline was setup to publish code test coverage on Codecov using Coverlet

  • Install coverlet.msbuild package to the test project dotnet add package coverlet.msbuild. Verify that it’s added to the .cproj file of the test project. Link to package here.

  • Activate coverlet by adding an extra property to the test run command:

    dotnet test /p:CollectCoverage=true -p:CoverletOutputFormat=opencover -p:CoverletOutput=”./report/”

    This command produces a coverage result in an opencover format file in a “report” folder in the root of the test project. Also, a summary of the result is displayed in the terminal. Refer here for more information.

Codecov Remote Setup

Now, let’s setup our repository up and running on codecov by installing the Codecov app and verify from the Github Settings option that it’s part of the Github Integrations

Publishing Test Coverage

Run the test suite to generate code coverage reports in opencover format to be published to the Codecov dashboard on PR raised. Add this snippet within the CI build workflow pipeline.

• • •

  - name: Add Coverlet package
    run: dotnet add package coverlet.msbuild
    working-directory: ./<TEST_PROJECT_PATH>
  - name: Run Test and Generate Coverage Report
    run: dotnet test-p:CollectCoverage=true -p:CoverletOutputFormat=opencover -p:CoverletOutput='./report/'
    working-directory: ./<TEST_PROJECT_PATH>
  - name: Upload Test Report to Codecov
    uses: codecov/codecov-action@v1
      token: $
      file: ./TEST_PROJECT/report/coverage.opencover.xml
      flags: unittests
      fail_ci_if_error: true

• • •

Navigate back to the repository on Codecov, and you should see coverage result.

Runing Test Locally before pushing

This can be achieved by

  • running Coverlet tool to display console UI summary report to generate an opencover file and
  • running ReportGenerator tool to convert the OpenCover coverage report into human readable reports. Find more on this here

Navigate to the test project and run test coverage locally with command:

$ dotnet test -p:CollectCoverage=true -p:CoverletOutputFormat=opencover

Install Report Generator package using:

$ dotnet tool install --global dotnet-reportgenerator-globaltool --version 4.6.5

To generate the UI Coverage report using the opencover report file emitted by the coverlet run:

$ reportgenerator "-reports:result/coverage.opencover.xml" "-targetdir:path/to/result-folder"

Now, locate the path and browse the index.html file to see the result.

Embedding a metadata resource in a created assembly

Given Crossroads.Metadata.package the resource result name and “resourcepath” as a path to a JSON file, to be embedded. The resource file can be embedded inside an assembly with this code sample snippet:

var resourceDescription = new ResourceDescription<("",
                        ()=>_fileSytem.File.OpenRead(resourcePath), isPublic: true);

compilation.Emit(outputAssembly, win32Resources: win32Resources,
                        manifestResources: new[] { resourceDescription, resourceDescription });

A dnlib library was employed from the Nuget package manager to read .NET assemblies and modules, windows PDBs and portable PDBs. It can be installed from the CLI

$ dotnet add package dnlib --version 3.3.2

The code snippet below demonstrates the reading of the embedded resource in the assembly module from the path specified.

var resourceName = "";
var assemblyDef = dnlib.DotNet.AssemblyDef.Load(path);
var manifest =   assemblyDef.ManifestModule.Resources.FindEmbeddedResource(resourceName);

using(StreamReader reader = new StreamReader(manifest.CreateReader().AsStream()))
    string result = reader.ReadToEnd();

Now this should query, read and display the embedded resource content.