From 10ea159606a2c4a490a78749664a7cdbac6455b3 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Thu, 4 Sep 2025 22:43:01 +0300 Subject: [PATCH] WindowsApiException --- .gitmodules | 6 ++ build.gradle.kts | 3 + lib/closeable-state | 1 + lib/highlevel-try-finally | 1 + settings.gradle.kts | 4 +- .../OsException.kt | 10 ++ .../WindowsApiException.kt | 99 +++++++++++++++++++ 7 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 160000 lib/closeable-state create mode 160000 lib/highlevel-try-finally create mode 100644 src/commonMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/OsException.kt create mode 100644 src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/WindowsApiException.kt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bfedcd1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "lib/closeable-state"] + path = lib/closeable-state + url = https://git.landgrafhomyak.ru/LanguageUtilities/closeable-state.kt +[submodule "lib/highlevel-try-finally"] + path = lib/highlevel-try-finally + url = https://git.landgrafhomyak.ru/xomrk/highlevel-try-finally.kt diff --git a/build.gradle.kts b/build.gradle.kts index d1691e4..9e295ec 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,8 @@ repositories { } kotlin { + explicitApi() + mingwX64() linuxX64() @@ -23,6 +25,7 @@ kotlin { sourceSets { commonMain { dependencies { + implementation("ru.landgrafhomyak.utility:highlevel-try-finally:0.6") implementation("ru.landgrafhomyak.utility:closeable-state-1:1.1") } } diff --git a/lib/closeable-state b/lib/closeable-state new file mode 160000 index 0000000..bdb1fe5 --- /dev/null +++ b/lib/closeable-state @@ -0,0 +1 @@ +Subproject commit bdb1fe56fa79e50850e8072db0b857955d48b479 diff --git a/lib/highlevel-try-finally b/lib/highlevel-try-finally new file mode 160000 index 0000000..6cfa6bd --- /dev/null +++ b/lib/highlevel-try-finally @@ -0,0 +1 @@ +Subproject commit 6cfa6bddd04a041ae02ec8098c478762313c71de diff --git a/settings.gradle.kts b/settings.gradle.kts index 32de920..da6fda2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,2 +1,4 @@ -rootProject.name = "kotlin-native-stdlib-1" +rootProject.name = "kotlin-native-interop-stdlib-0" +includeBuild("lib/closeable-state") +includeBuild("lib/highlevel-try-finally") \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/OsException.kt b/src/commonMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/OsException.kt new file mode 100644 index 0000000..d63e81c --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/OsException.kt @@ -0,0 +1,10 @@ +package ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0 + +public abstract class OsException : RuntimeException { + public constructor() : super() + public constructor(message: String?) : super(message) + public constructor(message: String?, cause: Throwable?) : super(message, cause) + public constructor(cause: Throwable?) : super(cause) + + abstract override fun toString(): String +} \ No newline at end of file diff --git a/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/WindowsApiException.kt b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/WindowsApiException.kt new file mode 100644 index 0000000..9b8ad90 --- /dev/null +++ b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/WindowsApiException.kt @@ -0,0 +1,99 @@ +package ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0 + +import kotlinx.cinterop.ByteVar +import kotlinx.cinterop.CPointerVar +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.alloc +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.ptr +import kotlinx.cinterop.reinterpret +import kotlinx.cinterop.toKString +import kotlinx.cinterop.value +import platform.windows.FORMAT_MESSAGE_ALLOCATE_BUFFER +import platform.windows.FORMAT_MESSAGE_FROM_SYSTEM +import platform.windows.FORMAT_MESSAGE_IGNORE_INSERTS +import platform.windows.FormatMessageA +import platform.windows.GetLastError +import platform.windows.LocalFree +import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1 + +public class WindowsApiException : OsException { + private val errno: UInt + private val nativeMessage: String? + + public constructor(errno: UInt, nativeMessage: String?) : super() { + this.errno = errno + this.nativeMessage = nativeMessage + } + + public constructor(errno: UInt, nativeMessage: String?, customMessage: String?) : super(customMessage) { + this.errno = errno + this.nativeMessage = nativeMessage + } + + public constructor(errno: UInt, nativeMessage: String?, customMessage: String?, cause: Throwable?) : super(customMessage, cause) { + this.errno = errno + this.nativeMessage = nativeMessage + } + + public constructor(errno: UInt, nativeMessage: String?, cause: Throwable?) : super(cause) { + this.errno = errno + this.nativeMessage = nativeMessage + } + + override fun toString(): String = + "" + + public companion object { + public fun throwFromLastWindowsErr(): Nothing { + throw this.formatFromLastWindowsErr() + } + + public fun throwFromWindowsErrCode(code: UInt): Nothing { + throw this.formatFromWindowsErrCode(code) + } + + public fun formatFromLastWindowsErr(): WindowsApiException = this.formatFromWindowsErrCode(GetLastError()) + + public fun formatFromWindowsErrCode(code: UInt): WindowsApiException { + var err = WindowsApiException(errno = code, nativeMessage = null, customMessage = "[errno=${code}]") + try { + val nativeMessage = this.getNativeMessageByErrno(code) + err = WindowsApiException(errno = code, nativeMessage = nativeMessage, "[errno=${err}] ${nativeMessage}") + } catch (newErr: Throwable) { + err.addSuppressed(newErr) + } + return err + } + + @OptIn(ExperimentalForeignApi::class) + public fun getNativeMessageByErrno(code: UInt): String { + memScoped { + var buffer = alloc>() + val result = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER.toUInt() or FORMAT_MESSAGE_FROM_SYSTEM.toUInt() or FORMAT_MESSAGE_IGNORE_INSERTS.toUInt(), + null, + code, + 0u, // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buffer.ptr.reinterpret(), + 0u, + null + ) + + if (result == 0u) { + val newErrno = GetLastError() + throw WindowsApiException(errno = newErrno, nativeMessage = null, customMessage = "[errno=${newErrno}]") + } + + safeAutoClose1( + action = { + return buffer.value!!.toKString() // todo handle nullablity + }, + finally = { + LocalFree(buffer.value) + } + ) + } + } + } +} \ No newline at end of file