ProcessSandbox.Runner 1.0.6

There is a newer version of this package available.
See the version list below for details.
dotnet add package ProcessSandbox.Runner --version 1.0.6
                    
NuGet\Install-Package ProcessSandbox.Runner -Version 1.0.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ProcessSandbox.Runner" Version="1.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ProcessSandbox.Runner" Version="1.0.6" />
                    
Directory.Packages.props
<PackageReference Include="ProcessSandbox.Runner" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ProcessSandbox.Runner --version 1.0.6
                    
#r "nuget: ProcessSandbox.Runner, 1.0.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ProcessSandbox.Runner@1.0.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ProcessSandbox.Runner&version=1.0.6
                    
Install as a Cake Addin
#tool nuget:?package=ProcessSandbox.Runner&version=1.0.6
                    
Install as a Cake Tool

ProcessSandbox

License: MIT .NET

Process isolation library for .NET that protects your application from legacy, unmanaged, or problematic code by running it in separate sandboxed processes.

The Problem

You have legacy code that:

  • Calls into unmanaged COM objects or native DLLs
  • Has resource leaks (memory, handles, GDI objects)
  • Is single-threaded but your app is multi-threaded
  • Occasionally crashes or hangs
  • You can't easily modify or replace

ProcessSandbox solves this by running the problematic code in isolated worker processes with automatic resource monitoring, lifecycle management, and transparent proxying.

Jump straight to the live demo

<a href="https://com-sandbox-demo-app.azurewebsites.net" target="_blank" rel="noopener noreferrer">live demo</a>

Calculator showing it running via the C Com Object

Features

  • ๐Ÿ›ก๏ธ Process Isolation: Crashes and leaks don't affect your main application
  • ๐Ÿ”„ Automatic Recycling: Workers recycled based on memory, handles, or call count
  • ๐ŸŽฏ Interface-Based Proxy: Use your interfaces naturally, calls routed transparently
  • โšก High Performance: Named pipes + MessagePack for fast IPC
  • ๐Ÿ”ง 32/64-bit Support: Run 32-bit workers from 64-bit apps (COM interop)
  • ๐Ÿšซ No Orphans: Job Objects ensure workers never leak
  • ๐Ÿ“Š Resource Monitoring: Track memory, GDI/USER handles, call counts
  • ๐Ÿงต Thread-Safe: Multiple threads can call concurrently
  • ๐ŸŽš๏ธ Configurable: Extensive options for pool size, limits, timeouts

Quick Start

See the tutorial for an easy to follow example of how ProcessSandbox.Runner works.

See the com tutorial to see an example of how to call a 32 bit com object in an Azure App Service.

Installation

dotnet add package ProcessSandbox.Runner

Basic Usage

using ProcessSandbox;
using Microsoft.Extensions.Logging;

// 1. Define your interface
public interface ILegacyService
{
    string ProcessData(string input);
}

// 2. Create implementation (in separate assembly)
public class LegacyServiceImpl : ILegacyService
{
    public string ProcessData(string input)
    {
        // Your legacy/unmanaged code here
        return LegacyComObject.DoWork(input);
    }
}

// 3. Configuration
var config = new ProcessPoolConfiguration
{
    MaxPoolSize = 5,
    MaxMemoryMB = 1024,
    WorkerAssembly = "MyLegacy.dll",
    WorkerType = "LegacyServiceImpl"
};

// 4. Create a logger factory and configure it to use the Console provider
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());

// 5. Create the proxy factory
using var factory = await ProcessProxyFactory<ILegacyService>.CreateAsync(config, loggerFactory);

// 6. Use the factory to give you a scoped proxy
string overallResult = await factory.UseProxyAsync<string>(async proxy =>
{
    // 7. Use it like any interface
    //    Proxy methods will run in seperate process that has loaded MyLegacy.dll
    //    Proxy object is stateful whist inside this code block
    string result = proxy.ProcessData("test data");
    string result2 = proxy.ProcessData("Some more test data");
    return $"{result} {result2}";
});

Console.WriteLine(overallResult);

Configuration

var config = new ProcessPoolConfiguration
{
    // Pool sizing
    MinPoolSize = 1,              // Start with 1 worker
    MaxPoolSize = 5,              // Scale up to 5 workers
    
    // Worker process
    DotNetVersion = DotNetVersion.Net48_32Bit,        // Use a 32-bit framework dll for COM
    WorkerAssembly = "MyLegacy.dll",
    WorkerType = "MyLegacy.ServiceImpl",
    
    // Resource limits
    MaxMemoryMB = 1024,           // Recycle at 1GB
    MaxGdiHandles = 10000,        // GDI handle limit
    ProcessRecycleThreshold = 100,// Recycle after 100 calls
    
    // Timeouts
    MethodCallTimeout = TimeSpan.FromSeconds(90),
};

Use Cases

COM Interop (32-bit)

Supply the ComClsid to call directly in to a com object without the need to register it.

Use extra com dependencies if you have dependant com object you need to call into. Look in src/tutorials/ComSandboxDemo for an example of how this works.

Very useful for running in web apps / app services when you don't have access to the registry

var config = new ProcessPoolConfiguration
{
    DotNetVersion = DotNetVersion.Net48_32Bit,
    ComClsid = new Guid("11111111-2222-3333-4444-555555555555"),
    ImplementationAssemblyPath = Path.Combine(AppContext.BaseDirectory, "workers", "SimpleComDelphi32.dll"),
    ExtraComDependencies = [
        new ComDependency
        {
            Clsid = new Guid("B1E9D2C4-8A6F-4E2B-9D3D-1234567890AB"),
            DllPath = Path.Combine(AppContext.BaseDirectory, "workers", "ComEngineInfo32.dll")
        }
    ],
    MaxMemoryMB = 1024,  // well within 32-bit limit
    MaxGdiHandles = 10000,
    NewInstancePerProxy = true // Set to false if you don't want the instance to be cleared down on each run
    
};

Native DLL Calls

var config = new ProcessPoolConfiguration
{
    ProcessRecycleThreshold = 1000,
    MaxProcessLifetime = TimeSpan.FromHours(1)
};

Single-Threaded Legacy Code

var config = new ProcessPoolConfiguration
{
    MaxPoolSize = 10,  // Handle concurrent requests
    MethodCallTimeout = TimeSpan.FromMinutes(5)
};

Architecture

Your App (.NET) โ†’ ProcessProxy<T> โ†’ [Worker Pool] โ†’ Worker Process
                                        โ†“              โ†“
                                   Named Pipes    Legacy DLL/COM
  • ProcessProxy: Transparent proxy implementing your interface
  • Worker Pool: Manages process lifecycle and resource monitoring
  • Worker Process: Isolated process hosting your implementation
  • IPC: Named pipes with MessagePack serialization

Building

git clone https://github.com/yourusername/ProcessSandbox.git
cd ProcessSandbox
dotnet build
dotnet test

Documentation

Performance

Typical overhead per call:

  • Local calls: < 0.1ms
  • Process proxy: 1-2ms (named pipes + serialization)

Perfect for scenarios where isolation benefits outweigh small latency cost.

Contributing

Contributions welcome! Please read CONTRIBUTING.md first.

License

MIT License - see LICENSE for details.

Roadmap

  • Call com objects directly just via an interface contract. No need for the manifest or an intermediate c# wrapper. This would need to use the COM Binary Interface (the VTable) but skip the COM Infrastructure (the Registry and the Service Control Manager). DONE
  • Telemetry/metrics export

Support

Useful tips when building

If I'm having to make changes to ProcessSandbox and write an end to end solution which uses it from packages, I find it easier to test from a local nuget server rather than having to wait for nuget to publish new versions.

To do this set up a local nuget server e.g.

dotnet nuget add source ~/LocalNuGetFeed --name LocalTestFeed --at-position 1

Then to build all the packages and put them in the local feed you can do the following (from the root of ProcessSanbox - e.g. where the ProcessSandbox.sln is)

dotnet build --configuration Release
dotnet build src/ProcessSandbox.Worker/ProcessSandbox.Worker.csproj -c Release -f net48 -r win-x86
dotnet pack /p:ExcludeProjects="**/ProcessSandbox.Worker.csproj" --configuration Release --no-build --output push-ready-artifacts
dotnet pack src/ProcessSandbox.Worker/ProcessSandbox.Worker.nuspec --configuration Release --output push-ready-artifacts -p:NoWarn=NU5100
cp push-ready-artifacts/* ~/LocalNuGetFeed

Remember to clear the local cache when you want to use them

dotnet nuget locals all --clear

And remember to get rid of from the local package source if you really want to pick them up from nuget.

rm ~/LocalNuGetFeed/*

Deploying the com tutorials to azure

In order to build to SimpleCom object, you need a c compiler. On a mac you can do

brew install mingw-w64

And then to build you need to

i686-w64-mingw32-gcc -shared -static -o publish/workers/net48/win-x86/SimpleCom.dll SimpleCom/SimpleCom.c -lole32 -loleaut32 -lpsapi -Wl,--add-stdcall-alias

Replace jonnymoo_rg_9172 with the name of your resource group and com-sandbox-demo-app with the name of you web app (from src/tutorials/ComSandboxDemo)

dotnet clean
rm -rf publish
rm site.zip
dotnet nuget locals all --clear
dotnet publish AzureSandboxHost/AzureSandboxHost.csproj -c Release -o ./publish
i686-w64-mingw32-gcc -shared -static -o publish/workers/net48/win-x86/SimpleCom.dll SimpleCom/SimpleCom.c -lole32 -loleaut32 -lpsapi -Wl,--add-stdcall-alias
cd publish
zip -r ../site.zip *
cd ..
az webapp deployment source config-zip --resource-group jonnymoo_rg_9172 --name com-sandbox-demo-app --src site.zip

Remember if you want to deploy SimpleComDelphi.dll - you can get it from the artifacts on the github build. You will have to manually go put it onto azure in site/wwwroot/workers/net48/win-x86

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.8 99 1/20/2026
1.0.6 112 1/11/2026
1.0.5 94 1/11/2026
1.0.4 99 12/30/2025
1.0.3 102 12/28/2025
1.0.2 194 12/25/2025
1.0.1 191 12/23/2025
0.20.0 176 12/22/2025
0.19.0 176 12/22/2025
0.18.0 176 12/22/2025
0.17.0 180 12/22/2025
0.15.0 179 12/22/2025
0.14.0 179 12/21/2025
0.13.0 135 12/20/2025
0.12.0 141 12/20/2025
0.11.0 232 12/14/2025
0.10.0 447 12/9/2025
0.9.0 446 12/9/2025
0.8.0 449 12/8/2025
0.7.0 315 12/7/2025
Loading failed