From bcb2ba3dee5e9e1181a96f29315202032f5e07f7 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Fri, 5 Sep 2025 00:50:19 +0300 Subject: [PATCH] WinApi HANDLE wrapper and functions to auto-throw WinApi error --- build.gradle.kts | 4 +- gradle.properties | 1 + lib/closeable-state | 2 +- .../windows/WindowsHandleWrapper.kt | 35 +++++++++++ .../windows/error_asserters.kt | 58 +++++++++++++++++++ 5 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 gradle.properties create mode 100644 src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsHandleWrapper.kt create mode 100644 src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/error_asserters.kt diff --git a/build.gradle.kts b/build.gradle.kts index 9e295ec..f42b17f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("multiplatform") version "2.1.10" + kotlin("multiplatform") version "2.2.10" } group = "ru.landgrafhomyak.utilities" @@ -26,7 +26,7 @@ kotlin { commonMain { dependencies { implementation("ru.landgrafhomyak.utility:highlevel-try-finally:0.6") - implementation("ru.landgrafhomyak.utility:closeable-state-1:1.1") + implementation("ru.landgrafhomyak.utility:closeable-state-1:1.2") } } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..3f87a74 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +kotlin.mpp.applyDefaultHierarchyTemplate=false \ No newline at end of file diff --git a/lib/closeable-state b/lib/closeable-state index bdb1fe5..4b799c0 160000 --- a/lib/closeable-state +++ b/lib/closeable-state @@ -1 +1 @@ -Subproject commit bdb1fe56fa79e50850e8072db0b857955d48b479 +Subproject commit 4b799c0ecfac0d501ce335147c2ed021e5d4bea9 diff --git a/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsHandleWrapper.kt b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsHandleWrapper.kt new file mode 100644 index 0000000..ae1ff9a --- /dev/null +++ b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsHandleWrapper.kt @@ -0,0 +1,35 @@ +package ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows + +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlinx.cinterop.ExperimentalForeignApi +import platform.windows.CloseHandle +import platform.windows.HANDLE +import ru.landgrafhomyak.utility.closeable_state_1.CloseableState +import ru.landgrafhomyak.utility.closeable_state_1.HandleWrapper +import ru.landgrafhomyak.utility.closeable_state_1.ManualStateManipulation + +@OptIn(ExperimentalForeignApi::class) +public class WindowsHandleWrapper( + handle: HANDLE, + state: CloseableState, +) { + private val _orig = HandleWrapper(handle, state) + + @Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND") + @OptIn(ExperimentalContracts::class) + public fun useHandle(method: (HANDLE) -> R): R { + contract { + callsInPlace(method, InvocationKind.EXACTLY_ONCE) + } + + return this._orig.useHandle(method) + } + + @ManualStateManipulation + public fun close() { + this._orig.close() + zeroToWinApiErr { CloseHandle(this._orig.handle) } + } +} \ No newline at end of file diff --git a/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/error_asserters.kt b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/error_asserters.kt new file mode 100644 index 0000000..8d7abbb --- /dev/null +++ b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/error_asserters.kt @@ -0,0 +1,58 @@ +@file:OptIn(ExperimentalContracts::class) + +package ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows + +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlinx.cinterop.ExperimentalForeignApi +import platform.windows.ERROR_SUCCESS +import platform.windows.GetLastError +import platform.windows.NULL + +private fun _checkErrnoAndThrow(): Nothing { + val code = GetLastError() + if (code == ERROR_SUCCESS.toUInt()) + throw RuntimeException("Function returned error status, but no error set") + WindowsApiException.throwFromLastWindowsErr() +} + +public fun Int.zeroToWinApiErr(): Int { + if (this != 0) + return this + + _checkErrnoAndThrow() +} + +public fun zeroToWinApiErr(call: () -> Int): Int { + contract { + callsInPlace(call, InvocationKind.EXACTLY_ONCE) + } + + val status = call() + if (status != 0) + return status + + _checkErrnoAndThrow() +} + +@OptIn(ExperimentalForeignApi::class) +public fun R?.nullToWinApiErr(): R { + if (this != null && this != NULL) + return this + + _checkErrnoAndThrow() +} + +@OptIn(ExperimentalForeignApi::class) +public fun nullToWinApiErr(call: () -> R?): R { + contract { + callsInPlace(call, InvocationKind.EXACTLY_ONCE) + } + + val ref = call() + if (ref != null && ref != NULL) + return ref + + _checkErrnoAndThrow() +} \ No newline at end of file