LoginSignup
1
2

More than 1 year has passed since last update.

Kotlin/Native - mingwでWin32 API メモ

Last updated at Posted at 2022-08-11

(自分用メモ)

  • KotlinでWindows向けネイティブプログラム
  • Win32APIの呼び出し

※「Kotlin/jvm - JNAでWin32 API」はまたそのうち。

環境

OS: Windows 11
IntelliJ: 2022.2
Kotlin: 1.7

テストコード

ネイティブヒープの確保/解放

例1.kt
    val dw = nativeHeap.alloc<DWORDVar>()
    //..使用..
    nativeHeap.free(dw) //解放
例2.kt
fun enumService() = memScoped {
    val dw = alloc<DWORDVar>()
    val buffer = allocArray<ByteVar>(1024)
    //..使用..
} //スコープを外れると中でallocされたヒープは解放される

Win32APIコール - 入力パラメータの例

API(C言語関数)の定義例: interop.def
void func_in_params(DWORD dw,CHAR c,WCHAR wc,LPCSTR lpCStr, LPCWSTR lpCWStr) {}
Kotlinでの呼び出し例: Test.kt
    fun test_in_params() = memScoped {
        val dw: DWORD = 0x12345678.toUInt() // DWORD = UInt
        val c: CHAR = 'c'.code.toByte() // CHAR = Byte
        val wc: WCHAR = 'w'.code.toUShort() //WCHAR = UShort
        val lpCStr: String = "const string" //  @CCall.CString LPCSTR型の仮引数にはkotlin.String?型を渡す
        val lpCWStr: String = "const wide string" //  @CCall.WCString LPCWSTR型の仮引数にはkotlin.String?型を渡す

        func_in_params(
            dw = dw,
            c = c,
            wc = wc,
            lpCStr = lpCStr,
            lpCWStr = lpCWStr
        )
    }

Win32APIコール - 出力パラメータの例

API(C言語関数)の定義例: interop.def
void func_out_params(LPDWORD lpDW_Out,LPSTR lpStr_Out,LPWSTR lpWStr_Out ) {}
Kotlinでの呼び出し例: Test.kt
    fun test_out_params() = memScoped {
        val lpDw_Out: LPDWORD = alloc<DWORDVar>().ptr //  LPDWORD = CPointer<DWORDVar>
        val lpStr_Out: LPSTR = allocArray<CHARVar>(256) //  LPSTR = CPointer<CHARVar>
        val lpWStr_Out: LPWSTR = allocArray<WCHARVar>(256) // LPWSTR = CPointer<WCHARVar>

        func_out_params(
            lpDW_Out = lpDw_Out,
            lpStr_Out = lpStr_Out,
            lpWStr_Out = lpWStr_Out
        )

        //出力を参照
        val resDw: UInt = lpDw_Out.pointed.value
        val resStr: String = lpStr_Out.toKString()
        val resWStr: String = lpWStr_Out.toKString()
    }

cinteropコマンドを使用し、宣言をc言語からKotlinへ変換

※今回は勉強のために使用

src/nativeInterop/cinterop/interop.def


void ints(char c, short d, int e, long f) { }
void uints(unsigned char c, unsigned short d, unsigned int e, unsigned long f) { }
void doubles(float a, double b) { }

gradlew cinteropInteropNative

生成ファイル: build/classes/kotlin/native/main/cinterop/KtNativeWin-cinterop-interop.klib
に含まれる.knmファイルをIntelliJで開くと、Kotlinでの表現が確認できる

default/linkdata/package_interop/0_interop.knm
package interop
@kotlinx.cinterop.internal.CCall public external fun doubles(a: kotlin.Float, b: kotlin.Double): kotlin.Unit { /* compiled code */ }
@kotlinx.cinterop.internal.CCall public external fun ints(c: kotlin.Byte, d: kotlin.Short, e: kotlin.Int, f: kotlin.Int): kotlin.Unit { /* compiled code */ }
@kotlinx.cinterop.internal.CCall public external fun uints(c: kotlin.UByte, d: kotlin.UShort, e: kotlin.UInt, f: kotlin.UInt): kotlin.Unit { /* compiled code */ }

メモリ確保/ポインタの参照/ポインタのキャスト

メモリを指定サイズ確保し、それを指定の型のポインタに変更。

Kotlin
    fun reinterpretCastTest() = memScoped {
        val bufferSize = sizeOf<DWORDVar>() * 10 // DWORD 10個分のメモリサイズ取得(単位:バイト)
        val buffer = allocArray<ByteVar>(bufferSize) // Byteの配列として確保
        fun printBuffer() = println((0 until bufferSize).map { buffer[it] }.joinToString { it.toString(0x10) }) // デバック用

        val pDw = buffer.reinterpret<DWORDVar>() // Byteの配列の先頭ポインタをDWORDのポインタにキャスト
        pDw.pointed.value = 0x01020304u // ポインタが指すDWORD型変数に書き込み
        pDw[2] = 0x02020202u //ポインタを配列として扱い3番目のDWORD要素に書き込み

        val v: DWORD = pDw.pointed.value // ポインタが指すDWORD変数の値を参照
        val v2: DWORD = pDw[2] // // ポインタを配列として扱い、3番目の要素を参照

        assert(v == 0x01020304u)
        assert(v2 == 0x02020202u)
        println("v:DWORD=0x${v.toString(0x10)}")
        println("v2:DWORD=0x${v2.toString(0x10)}")
        printBuffer()
    }

構造体と共用体の使用 / struct & union

interop.def
---

typedef struct {
  unsigned char uc0;
  unsigned char uc1;
  unsigned char uc2;
  unsigned char uc3;
} Struct1;

typedef union {
  int i;
  Struct1 m;
  unsigned char uc[4];
} Union1;
Kotlin.kt
fun unionSample() {
    val cUnion = cValue<Union1>()
    cUnion.useContents {
        i = 0x01020304
        println("cUnion.i=${i.toString(0x10)}")
        with(m) { listOf(uc3, uc2, uc1, uc0) }.joinToString { it.toString(0x10) }.let { println("cUnion.m=$it") }
        m.uc2 = 0xffu
        with(m) { listOf(uc3, uc2, uc1, uc0) }.joinToString { it.toString(0x10) }.let { println("cUnion.m=$it") }
        println("cUnion.i=${i.toString(0x10)}")
    }
}
実行結果
cUnion.i=1020304
cUnion.m=1, 2, 3, 4
cUnion.m=1, ff, 3, 4
cUnion.i=1ff0304

その他WindowsOS一般

プロジェクト履歴

  • IntelliJ > 新規 > プロジェクト > Kotlin マルチプラットフォーム > Native Application

  • cinteropコマンドを使用する設定(習熟目的)

build.gradle.kts
//..中略..
kotlin {
  mingwX64("native") {
    val main by compilations.getting // 追加
    val interop by main.cinterops.creating // 追加
   // ..中略..
  }
}

Javaはgradleに添えるだけ

参考

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2