objectbox-java icon indicating copy to clipboard operation
objectbox-java copied to clipboard

UnsatisfiedLinkError: no objectbox-jni-windows-x64 when installed to Program Files

Open udyr-woo opened this issue 3 months ago • 5 comments

Is there an existing issue?

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

udyr-woo avatar Nov 26 '25 08:11 udyr-woo

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.

greenrobot-team avatar Dec 01 '25 06:12 greenrobot-team

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.

udyr-woo avatar Dec 01 '25 08:12 udyr-woo

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.

greenrobot-team avatar Dec 08 '25 06:12 greenrobot-team

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?

udyr-woo avatar Dec 09 '25 03:12 udyr-woo

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.

greenrobot-team avatar Dec 09 '25 05:12 greenrobot-team