MASES.JNet
2.6.7
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
<PackageReference Include="MASES.JNet" Version="2.6.7" />
<PackageVersion Include="MASES.JNet" Version="2.6.7" />
<PackageReference Include="MASES.JNet" />
paket add MASES.JNet --version 2.6.7
#r "nuget: MASES.JNet, 2.6.7"
#:package MASES.JNet@2.6.7
#addin nuget:?package=MASES.JNet&version=2.6.7
#tool nuget:?package=MASES.JNet&version=2.6.7
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:
string pathToJVM = "C:\\Program Files\\Eclipse Adoptium\\jdk-11.0.18.10-hotspot\\bin\\server\\jvm.dll";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:
- Open a command prompt and run
set | findstr JAVA_HOME. - 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_HOMEat system level, e.g.JAVA_HOME=C:\Program Files\Eclipse Adoptium\jdk-11.0.18.10-hotspot\ - Set
JCOBRIDGE_JVMPathat 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_JVMPathtakes precedence overJAVA_HOMEand the Windows registry: setting it to the full path ofjvm.dllavoids the need to overrideJVMPathin 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:
Target a .NET version that does not enable CET by default, such as .NET 8.
Disable CET for the executable in the
.csproj(JNet project templates include this automatically):
<PropertyGroup Condition="'$(TargetFramework)' == 'net9.0'">
<CETCompat>false</CETCompat>
</PropertyGroup>
- Run via the
dotnetapp host instead of the native executable, as described in this comment:
dotnet MyApplication.dll
instead of:
MyApplication.exe
- 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
sethas 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 | Versions 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. |
-
.NETFramework 4.6.2
- MASES.CLIParser (>= 3.2.1)
- MASES.JCOBridge (>= 2.6.7)
- Microsoft.Bcl.AsyncInterfaces (>= 10.0.5)
- Microsoft.IO.RecyclableMemoryStream (>= 3.0.1)
-
net10.0
- MASES.CLIParser (>= 3.2.1)
- MASES.JCOBridge (>= 2.6.7)
- Microsoft.IO.RecyclableMemoryStream (>= 3.0.1)
-
net8.0
- MASES.CLIParser (>= 3.2.1)
- MASES.JCOBridge (>= 2.6.7)
- Microsoft.IO.RecyclableMemoryStream (>= 3.0.1)
-
net9.0
- MASES.CLIParser (>= 3.2.1)
- MASES.JCOBridge (>= 2.6.7)
- Microsoft.IO.RecyclableMemoryStream (>= 3.0.1)
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.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 |