MASES.JNet 2.6.7

There is a newer version of this package available.
See the version list below for details.
dotnet add package MASES.JNet --version 2.6.7
                    
NuGet\Install-Package MASES.JNet -Version 2.6.7
                    
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="MASES.JNet" Version="2.6.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MASES.JNet" Version="2.6.7" />
                    
Directory.Packages.props
<PackageReference Include="MASES.JNet" />
                    
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 MASES.JNet --version 2.6.7
                    
#r "nuget: MASES.JNet, 2.6.7"
                    
#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 MASES.JNet@2.6.7
                    
#: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=MASES.JNet&version=2.6.7
                    
Install as a Cake Addin
#tool nuget:?package=MASES.JNet&version=2.6.7
                    
Install as a Cake Tool

title: Using JNet — .NET suite for Java™/JVM™ _description: How to configure the environment, locate the JVM™, and write robust JNet code

JNet usage

JNet exposes Java™ classes directly in .NET, letting you write C# code against the same types available in the official Java™ packages. If a class or method has not been mapped yet, see What to do if an API was not yet implemented.

Environment setup

JNet accepts many command-line switches to customize its behavior. The full list is available at the Command line switch page.

JVM™ identification

One of the most important command-line switches is JVMPath, available in JCOBridge switches: it can be used to set the location of the JVM™ library (jvm.dll / libjvm.so) if JCOBridge is not able to identify a suitable JRE installation.

If you are embedding JNet in your own product, you can override the JVMPath property as shown below:

class MyJNetCore : JNetCore<MyJNetCore>
{
    // Override JVMPath when JCOBridge cannot auto-detect the JRE/JDK installation,
    // or when you need to pin a specific JVM version in your application.
    public override string JVMPath
    {
        get
        {
            string pathToJVM = "Set here the path to the JVM library (jvm.dll / libjvm.so)";
            return pathToJVM;
        }
    }
}

pathToJVM must be properly escaped:

  1. string pathToJVM = "C:\\Program Files\\Eclipse Adoptium\\jdk-11.0.18.10-hotspot\\bin\\server\\jvm.dll";
  2. string pathToJVM = @"C:\Program Files\Eclipse Adoptium\jdk-11.0.18.10-hotspot\bin\server\jvm.dll";

Special initialization conditions

JCOBridge attempts to locate a suitable JRE/JDK installation using standard mechanisms: the JAVA_HOME environment variable or the Windows registry (where available).

If the application fails with InvalidOperationException: Missing Java Key in registry, neither JAVA_HOME nor the Windows registry contains a reference to a JRE/JDK installation.

Diagnose the issue:

  1. Open a command prompt and run set | findstr JAVA_HOME.
  2. If a value is returned, it may be set at user level rather than system level, making it invisible to the JNet process that raised the exception.

Fix the issue (choose one):

  • Set JAVA_HOME at system level, e.g. JAVA_HOME=C:\Program Files\Eclipse Adoptium\jdk-11.0.18.10-hotspot\
  • Set JCOBRIDGE_JVMPath at system level to point directly to the JVM library, e.g. JCOBRIDGE_JVMPath=C:\Program Files\Eclipse Adoptium\jdk-11.0.18.10-hotspot\bin\server\jvm.dll

  • At least one of JCOBRIDGE_JVMPath, JAVA_HOME, or the Windows registry (on Windows) must be available.
  • JCOBRIDGE_JVMPath takes precedence over JAVA_HOME and the Windows registry: setting it to the full path of jvm.dll avoids the need to override JVMPath in code.
  • After first initialization, JVMPath (set in code) takes precedence over both environment variables and the registry.

Intel CET and JNet

JNet uses an embedded JVM™ through JCOBridge. However, JVM™ initialization is incompatible with CET (Control-flow Enforcement Technology) because the code used to identify the CPU attempts to modify the return address, which CET treats as a violation — see this issue comment.

From .NET 9 preview 6, CET is enabled by default on supported hardware when the build output is an executable (i.e. the .csproj contains <OutputType>Exe</OutputType>).

If the application fails at startup with error 0xc0000409 (subcode 0x30), CET is enabled and conflicting with JVM™ initialization.

Solutions 2 and 3 are the recommended approaches for most projects. Solution 1 requires targeting an older .NET version; solution 4 requires elevated privileges and a registry change.

There are four possible workarounds:

  1. Target a .NET version that does not enable CET by default, such as .NET 8.

  2. Disable CET for the executable in the .csproj (JNet project templates include this automatically):

<PropertyGroup Condition="'$(TargetFramework)' == 'net9.0'">
    
    <CETCompat>false</CETCompat>
</PropertyGroup>
  1. Run via the dotnet app host instead of the native executable, as described in this comment:
dotnet MyApplication.dll

instead of:

MyApplication.exe
  1. Register a CET mitigation for the specific executable from an elevated shell:
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\MyApplication.exe" /v MitigationOptions /t REG_BINARY /d "0000000000000000000000000000002000" /f

then run:

MyApplication.exe

Basic example

Below is a basic example demonstrating how to create a JNet-based program, including generics and exception handling. Comments in the code explain each step.

using Java.Util;
using MASES.JNet.Extensions;
using System.Diagnostics;
using Java.Lang;

namespace MASES.JNetExample
{
    // Define a concrete implementation of JNetCore<> for this application.
    class MyJNetCore : JNetCore<MyJNetCore>
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Mandatory first step: allocate the JVM and prepare the interop environment.
            MyJNetCore.CreateGlobalInstance();

            // Arguments not consumed by JNet/JCOBridge are available here,
            // just like standard command-line args.
            var appArgs = MyJNetCore.FilteredArgs;

            try
            {
                // Allocate a java.util.Set<String> in the JVM via Collections.Singleton,
                // returned as a Java.Util.Set<string> on the .NET side.
                Java.Util.Set<string> set = Collections.Singleton("test");

                // Attempt to add an element if one was passed on the command line.
                // Collections.Singleton returns an immutable Set, so this will throw.
                // See: https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#singleton(T)
                if (appArgs.Length != 0) set.Add(appArgs[0]);
            }
            // JNet translates Java exceptions into equivalent .NET exceptions,
            // so UnsupportedOperationException is caught here just like any C# exception.
            catch (UnsupportedOperationException)
            {
                System.Console.WriteLine("Operation not supported as expected");
            }
            // Catch-all: print any unexpected exception and let the application exit cleanly.
            catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
        }
    }
}

Avoiding Java.Lang.NullPointerException — Understanding .NET/JVM GC interaction

Occasionally, a Java.Lang.NullPointerException is raised with no obvious cause in the .NET code. This is a cross-boundary GC issue: the .NET Garbage Collector may collect a JNet wrapper object while the JVM™ is still using the underlying Java object it references.

In the basic example above, Collections.Singleton("test") creates a wrapper held by set, which remains reachable until set.Add(appArgs[0]) completes — so the GC does not collect it prematurely.

Consider this slightly different snippet:

using Java.Util;
using MASES.JNet.Extensions;
using System.Diagnostics;
using Java.Lang;

namespace MASES.JNetExample
{
    class MyJNetCore : JNetCore<MyJNetCore> { }

    class Program
    {
        static void Main(string[] args)
        {
            MyJNetCore.CreateGlobalInstance();
            try
            {
                Java.Util.Set<string> set = Collections.Singleton("test");
                ArrayList<string> arrayList = new();
                arrayList.AddAll(0, set); // Java.Lang.NullPointerException may occur here
            }
            catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
        }
    }
}

At the point arrayList.AddAll(0, set) is called:

  • Java.Util.Set<string> is a .NET wrapper around a JVM™ java.util.Set<String>.
  • The call passes the JVM™ reference across the boundary, but from .NET's perspective the wrapper set has no further uses and is eligible for collection.
  • If the .NET GC runs at this moment — which it may do arbitrarily based on memory pressure — the wrapper is collected and the JVM™ receives a null reference.

Most of the time the code works fine, but the failure is non-deterministic and hard to reproduce. The solutions below prevent it.

The using pattern is the most idiomatic approach in modern C# and should be preferred in new code. The SuppressFinalize/ReRegisterForFinalize pattern is useful when refactoring to using blocks is not practical.

using or try-finally with Dispose

All JNet classes implement IDisposable. Wrapping the object in a using block keeps it alive and releases the JVM reference deterministically:

using Java.Util;
using MASES.JNet.Extensions;
using System.Diagnostics;
using Java.Lang;

namespace MASES.JNetExample
{
    class MyJNetCore : JNetCore<MyJNetCore> { }

    class Program
    {
        static void Main(string[] args)
        {
            MyJNetCore.CreateGlobalInstance();
            try
            {
                using (Java.Util.Set<string> set = Collections.Singleton("test"))
                {
                    ArrayList<string> arrayList = new();
                    arrayList.AddAll(0, set);
                }
            }
            catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
        }
    }
}

Or equivalently with try-finally:

using Java.Util;
using MASES.JNet.Extensions;
using System.Diagnostics;
using Java.Lang;

namespace MASES.JNetExample
{
    class MyJNetCore : JNetCore<MyJNetCore> { }

    class Program
    {
        static void Main(string[] args)
        {
            MyJNetCore.CreateGlobalInstance();
            try
            {
                Java.Util.Set<string> set = null;
                try
                {
                    set = Collections.Singleton("test");
                    ArrayList<string> arrayList = new();
                    arrayList.AddAll(0, set);
                }
                finally { set?.Dispose(); }
            }
            catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
        }
    }
}

SuppressFinalize/ReRegisterForFinalize pattern

When restructuring to using is not practical, you can suppress finalization for the duration of the cross-boundary call:

using Java.Util;
using MASES.JNet.Extensions;
using System.Diagnostics;
using Java.Lang;

namespace MASES.JNetExample
{
    class MyJNetCore : JNetCore<MyJNetCore> { }

    class Program
    {
        static void Main(string[] args)
        {
            MyJNetCore.CreateGlobalInstance();
            try
            {
                Java.Util.Set<string> set = Collections.Singleton("test");
                try
                {
                    System.GC.SuppressFinalize(set);
                    ArrayList<string> arrayList = new();
                    arrayList.AddAll(0, set);
                }
                finally { System.GC.ReRegisterForFinalize(set); }
            }
            catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
        }
    }
}
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 is compatible.  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 net462 is compatible.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (7)

Showing the top 5 NuGet packages that depend on MASES.JNet:

Package Downloads
MASES.KNet

Core of .NET suite for Apache Kafka. KNet is a comprehensive .NET suite for Apache Kafka providing all features: Producer, Consumer, Admin, Streams, Connect, backends (KRaft).

MASES.PLCOnNet

.NET suite for PLC4X - a comprehensive suite of libraries and tools to use PLC4X and .NET side-by-side

MASES.NetPDF

.NET suite for PDFBox™ - a comprehensive suite of libraries and tools to use PDFBox™ and .NET side-by-side

MASES.Naven

.NET suite for Apache Maven™ - a comprehensive suite of libraries and tools to use Apache Maven™ and .NET side-by-side

MASES.JNetPSCore

JNetPSCore - JNet (.NET suite for Java™/JVM™) PowerShell base library

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.6.8 242 4/10/2026
2.6.7 322 4/9/2026
2.6.6 66,328 2/12/2026
2.6.5 118,946 11/27/2025
2.6.4 113,031 9/22/2025
2.6.3 813 9/13/2025 2.6.3 is deprecated because it has critical bugs.
2.6.2 1,049 8/21/2025
2.6.1 21,917 8/4/2025
2.6.0 255 8/3/2025
2.5.13 34,161 7/19/2025
2.5.12 340,317 2/2/2025
2.5.11 1,699 12/20/2024
2.5.10 64,712 11/4/2024
2.5.9 142,633 9/19/2024
2.5.8 54,612 7/31/2024
2.5.7 5,703 7/30/2024
2.5.6 483 7/28/2024
2.5.5 83,321 6/27/2024
2.5.4 28,357 6/22/2024
2.5.3 455 6/18/2024
Loading failed