1+ /*
2+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+ * SPDX-License-Identifier: Apache-2.0
4+ */
5+ package aws.sdk.kotlin.crt.util
6+
7+ // The functions below are adapted from C++ SDK:
8+ // https://github.com/aws/aws-sdk-cpp/blob/0e6085bf0dd9a1cb1f27d101c4cf2db6ade6f307/src/aws-cpp-sdk-core/source/platform/windows/OSVersionInfo.cpp#L49-L106
9+
10+ private val wordHexFormat = HexFormat {
11+ upperCase = false
12+ number {
13+ removeLeadingZeros = true
14+ minLength = 4
15+ }
16+ }
17+
18+ private data class LangCodePage (
19+ val language : UShort ,
20+ val codePage : UShort ,
21+ )
22+
23+ public fun osVersionFromKernel (): String? = memScoped {
24+ withFileVersionInfo(" Kernel32.dll" ) { versionInfoPtr ->
25+ getLangCodePage(versionInfoPtr)?.let { langCodePage ->
26+ getProductVersion(versionInfoPtr, langCodePage)
27+ }
28+ }
29+ }
30+
31+ private inline fun <R > withFileVersionInfo (fileName : String , block : (CPointer <ByteVarOf <Byte >>) -> R ? ): R ? {
32+ val blobSize = GetFileVersionInfoSizeW (fileName, null )
33+ val blob = ByteArray (blobSize.convert())
34+ blob.usePinned { pinned ->
35+ val result = GetFileVersionInfoW (fileName, 0u , blobSize, pinned.addressOf(0 ))
36+ return if (result == 0 ) {
37+ null
38+ } else {
39+ block(pinned.addressOf(0 ))
40+ }
41+ }
42+ }
43+
44+ private fun MemScope.getLangCodePage (versionInfoPtr : CPointer <ByteVarOf <Byte >>): LangCodePage ? {
45+ // Get _any_ language pack and codepage since they should all have the same version
46+ val langAndCodePagePtr = alloc<COpaquePointerVar >()
47+ val codePageSize = alloc<UIntVar >()
48+ val result = VerQueryValueW (
49+ versionInfoPtr,
50+ """ \VarFileInfo\Translation""" ,
51+ langAndCodePagePtr.ptr,
52+ codePageSize.ptr,
53+ )
54+
55+ return if (result == 0 ) {
56+ null
57+ } else {
58+ val langAndCodePage = langAndCodePagePtr.value!! .reinterpret<UIntVar >().pointed.value
59+ val language = (langAndCodePage and 0x0000ffffu ).toUShort() // low WORD
60+ val codePage = (langAndCodePage and 0xffff0000u shr 16 ).toUShort() // high WORD
61+ LangCodePage (language, codePage)
62+ }
63+ }
64+
65+ private fun MemScope.getProductVersion (versionInfoPtr : CPointer <ByteVarOf <Byte >>, langCodePage : LangCodePage ): String? {
66+ val versionId = buildString {
67+ // Something like: \StringFileInfo\04090fb0\ProductVersion
68+ append(""" \StringFileInfo\""" )
69+ append(langCodePage.language.toHexString(wordHexFormat))
70+ append(langCodePage.codePage.toHexString(wordHexFormat))
71+ append(""" \ProductVersion""" )
72+ }
73+
74+ // Get the block corresponding to versionId
75+ val block = alloc<COpaquePointerVar >()
76+ val blockSize = alloc<UIntVar >()
77+ val result = VerQueryValueW (versionInfoPtr, versionId, block.ptr, blockSize.ptr)
78+
79+ return if (result == 0 ) {
80+ null
81+ } else {
82+ // Copy the bytes into a Kotlin byte array
83+ val blockBytes = ByteArray (blockSize.value.convert())
84+ blockBytes.usePinned { pinned ->
85+ memcpy(pinned.addressOf(0 ), block.value!! .reinterpret<ByteVar >(), blockSize.value.convert())
86+ }
87+ blockBytes.decodeToString()
88+ }
89+ }
0 commit comments