Kotlin: Create and refer true Java arrays (for JNA)

1k views Asked by At

I am trying to use JNA with Kotlin and I've ran into a problem. Caused by: java.lang.IllegalArgumentException: class [Lcom.sun.jna.platform.win32.WinDef$HMODULE; is not a supported argument type (in method EnumProcessModulesEx in class kotmem.unsafe.Psapi)

My Psapi direct-mapped object:

package kotmem.unsafe

import com.sun.jna.*
import com.sun.jna.platform.win32.*
import com.sun.jna.ptr.*

object Psapi {

    // note Array<WinDef.HMODULE?>
    external fun EnumProcessModulesEx(process: Pointer, modules: Array<WinDef.HMODULE?>, cb: Int,
                                      neededModules: IntByReference, flags: Int): Boolean

    external fun GetModuleInformation(process: Pointer, module: WinDef.HMODULE, moduleInfo: LPMODULEINFO, cb: Int): Boolean

    external fun GetModuleBaseNameA(process: Pointer, module: WinDef.HMODULE, fileName: ByteArray, size: Int): Int

    init {
        Native.register(NativeLibrary.getInstance("Psapi"))
    }

}

The issue seems to lay in the way I'm calling it. JNA doesn't like Kotlin's Array class I'm assuming because it doesn't know how to map such. Is there a way to refer to real Java arrays so that JNA can map this function? Also, is there a way to construct such?

This is the way I'm calling it:

fun modulesOfProcess(process: UnsafeProcess): List<UnsafeModule> {
    val list = emptyList<UnsafeModule>()

    val process = process.handle.pointer

    // note that I construct using arrayOfNulls
    val modules = arrayOfNulls<WinDef.HMODULE>(1024)
    val needed = IntByReference()

    Psapi.EnumProcessModulesEx(process, modules, modules.size, needed, 1)

    for (i in 0..needed.value / 4) {
        val module = modules[i] ?: continue
        val info = LPMODULEINFO()
        if (!Psapi.GetModuleInformation(process, module, info, info.size()))
            list + UnsafeModule(module, info)
    }

    return list
}
2

There are 2 answers

1
technomage On BEST ANSWER

Direct mapping does not support arrays of Pointer or arrays of NativeMapped (see docs).

You can manually construct a buffer of pointers and pass that:

Pointer modules = new Memory(Pointer.SIZE * length);
int offset = 0;
for (h: hmodules) {
    modules.setPointer(Pointer.SIZE * offset, h.getPointer())
    offset += 1;
}
0
Ahmed Jihad On
var modules = ptrTypeToPtr<HMODULE>(arrayOfNulls(1024*2))
        var lpNeeded: IntByReference = IntByReference();
        EnumProcessModulesEx(procDetails!!.processHandle!!, modules, 1024 * 4, lpNeeded, 0x03)
        var moduelsCount = ((lpNeeded.value/8)-1);
        for(offset: Long in 0L..moduelsCount){
            var moduleHandle:Long =  BigInteger(modules.getPointer(offset* 8).toString().replace("native@0x", ""), 16).toLong()
            println(moduleHandle) // now this is the address in integer of the module
            print("  ")
            println(moduleHandle.toHexString(HexFormat.UpperCase)) // now this is the same address of module but in Hex format
        }


fun <T : PointerType?> ptrTypeToPtr(objects: Array<T?>): Pointer {
        val memory = Memory(Native.POINTER_SIZE * objects.size.toLong())
        var offset = 0L
        for (obj in objects) {
            memory.setPointer(Native.POINTER_SIZE * offset, (if(obj == null) { Pointer.NULL }else{ obj.pointer }) as Pointer? )
            offset++
        }
        return memory
    }