UnsatisfiedLinkError: no objectbox-jni-windows-x64 when installed to Program Files
Is there an existing issue?
- [x] I have searched existing issues
Build info
- ObjectBox version: 5.0.0
- OS: Windows 10
- Device/ABI/architecture: x64
Steps to reproduce
I am building a KMP project with ObjectBox. When I run the jvmApp module directly (e.g., from the IDE), the ObjectBox .dll file is found and loaded correctly.
However, when I build a distributable package for deployment (e.g., using jpackage), the .dll file seems to be missing from the final package, causing it to fail at runtime.
What could be the problem?
(I apologize for asking this again. I posted this on StackOverflow, but since I haven't received a response yet, I'm posting it here as well.)
Expected behavior
Create objectbox-jni-windows-x64, when the installer installs the executable.
Actual behavior
The .dll file is not included when the installer installs the executable.
Code
Code
settings.gradle.kts(root)
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
resolutionStrategy {
eachPlugin {
if (requested.id.id == "io.objectbox") {
useModule("io.objectbox:objectbox-gradle-plugin:${requested.version}")
}
if (requested.id.id == "com.google.dagger") {
useModule("com.google.dagger:dagger:${requested.version}")
}
if (requested.id.id == "com.google.dagger:hilt-android") {
useModule("com.google.dagger:hilt-android:${requested.version}")
}
}
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "FireAlarmKMP"
include(":androidApp")
include(":jvmApp")
include(":shared:data-common")
include(":shared:data-jvm")
include(":shared:domain")
include(":shared:presentation-common")
include(":shared:data-android")
include(":shared:core-common")
include(":shared:core-android")
include(":shared:core-jvm")
include(":shared:presentation-android")
include(":shared:presentation-jvm")
build.gradle.kts(:jvmApp)
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
plugins {
alias(libs.plugins.jetbrains.kotlin.jvm)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.kapt)
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11
}
}
dependencies {
implementation(compose.desktop.currentOs)
implementation(libs.kotlinx.coroutines.swing)
implementation(libs.dagger)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.lifecycle.runtime.compose)
configurations["kapt"].dependencies.add(project.dependencies.create(libs.dagger.compiler.get()))
implementation(project(":shared:presentation-common"))
implementation(project(":shared:presentation-jvm"))
implementation(project(":shared:domain"))
implementation(project(":shared:data-common"))
implementation(project(":shared:data-jvm"))
implementation(project(":shared:core-common"))
implementation(project(":shared:core-jvm"))
}
compose.desktop {
application {
mainClass = "com.blabla.jvmapp.MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb, TargetFormat.Exe)
packageName = "jvmapp"
packageVersion = "1.0.28"
}
}
}
build.gradle.kts(:data-jvm)
plugins {
alias(libs.plugins.jetbrains.kotlin.jvm)
alias(libs.plugins.kapt)
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11
}
}
dependencies {
implementation(libs.arrow.core)
implementation(libs.arrow.fx.coroutines)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.objectbox.windows)
implementation(libs.objectbox.kotlin)
kapt(libs.objectbox.processor)
implementation(libs.dagger)
kapt(libs.dagger.compiler)
implementation(project(":shared:data-common"))
implementation(project(":shared:core-common"))
implementation(project(":shared:domain"))
}
apply(plugin = "java-library")
apply(plugin = "io.objectbox")
Logs, stack traces
Logs
Caused by: java.lang.UnsatisfiedLinkError: no objectbox-jni-windows-x64 in java.library.path: C:\Program Files\com.blabla.jvmapp\app/resources
at java.base/java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.base/java.lang.Runtime.loadLibrary0(Unknown Source)
at java.base/java.lang.System.loadLibrary(Unknown Source)
at io.objectbox.internal.NativeLibraryLoader.<clinit>(NativeLibraryLoader.java:98)
... 48 more
The .dll file is not included when the installer installs the executable.
By default the library file is extracted into the working directory from the JAR resources when ObjectBox code is first run. See NativeLibraryLoader. It looks like that default behavior doesn't work for a Kotlin Multiplatform app?
Assuming that extracting doesn't work, the solution is likely to manually include the library and make sure it's placed in the correct location before the app uses ObjectBox APIs. You can find the library included in the platform-specific JAR, so for Windows in the Maven artifact io.objectbox:objectbox-windows.
The .dll file is not included when the installer installs the executable.
By default the library file is extracted into the working directory from the JAR resources when ObjectBox code is first run. See NativeLibraryLoader. It looks like that default behavior doesn't work for a Kotlin Multiplatform app?
Assuming that extracting doesn't work, the solution is likely to manually include the library and make sure it's placed in the correct location before the app uses ObjectBox APIs. You can find the library included in the platform-specific JAR, so for Windows in the Maven artifact
io.objectbox:objectbox-windows.
I've found the root cause. When installed in Program Files, the app lacks the permissions to extract the DLL file at runtime. If installed in a directory other than Program Files, the DLL is generated successfully, and the app starts normally.
Thanks for the update!
The proper solution to install to (the write protected) Program Files on Windows would be to install the DLL into the application installation directory. I'm not sure how to do this when using Kotlin Multiplatform build tools, but maybe we should at least provide general documentation on how to extract and bundle the required ObjectBox database library file(s) for desktop applications.
Thanks for the update!
The proper solution to install to (the write protected) Program Files on Windows would be to install the DLL into the application installation directory. I'm not sure how to do this when using Kotlin Multiplatform build tools, but maybe we should at least provide general documentation on how to extract and bundle the required ObjectBox database library file(s) for desktop applications.
I tried modifying build.gradle to include the DLL during the installation phase as you suggested, but unfortunately, it didn't work. If preparing the official documentation is going to take some time, could you please provide a temporary solution or instructions right here?
could you please provide a temporary solution or instructions right here?
I already told you that the DLL can be extracted from the Maven JAR artifact (and it also is automatically extracted if running the app during testing). Bundling that file in the installation archive or executable is specific to the build system and setup.