Skip to content

Commit

Permalink
feat(win32-api): helper bufferToStruct()
Browse files Browse the repository at this point in the history
  • Loading branch information
waitingsong committed Jul 25, 2022
1 parent fbaf9ea commit 86539fd
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 13 deletions.
33 changes: 24 additions & 9 deletions packages/win32-api/src/func/winspool/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import ref from 'ref-napi'
import {
DllNames,
DStruct as DS,
StructFactory,
DModel as M,
ptrToString,
bufferToStruct,
} from '../../index.js'
import { Winspool as DLL } from '../../index.promise.js'

Expand Down Expand Up @@ -40,24 +39,38 @@ export function retriveStruct_PRINTER_INFO<L extends M.PRINTER_INFO_LEVEL>(
switch (Level) {
case 1: {
const structDef = DS.PRINTER_INFO_1
ret = loopRead(
// ret = loopRead(
// pPrinter,
// maxCount,
// pcb,
// 1,
// structDef,
// ) as M.PRINTER_INFO_X[L][]
ret = bufferToStruct<M.PRINTER_INFO_X[1]>(
pPrinter,
structDef,
maxCount,
pcb,
1,
structDef,
) as M.PRINTER_INFO_X[L][]

break
}

case 4: {
const structDef = DS.PRINTER_INFO_4
ret = loopRead(
// ret = loopRead(
// pPrinter,
// maxCount,
// pcb,
// 4,
// structDef,
// ) as M.PRINTER_INFO_X[L][]

ret = bufferToStruct<M.PRINTER_INFO_X[4]>(
pPrinter,
structDef,
maxCount,
pcb,
4,
structDef,
) as M.PRINTER_INFO_X[L][]
break
}
Expand All @@ -70,7 +83,7 @@ export function retriveStruct_PRINTER_INFO<L extends M.PRINTER_INFO_LEVEL>(
}



/*
function rpi1(
addressBuffer: Buffer,
maxByteLength: number,
Expand Down Expand Up @@ -169,3 +182,5 @@ function loopRead<L extends M.PRINTER_INFO_LEVEL>(
return ret
}
*/

122 changes: 122 additions & 0 deletions packages/win32-api/src/lib/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
AsyncSyncFuncModel,
DllFuncs,
DllFuncsModel,
Def,
ExpandFnModel,
FnName,
FnParams,
Expand All @@ -24,6 +25,24 @@ import {
} from 'win32-def'


export const isArch64 = process.arch.includes('64')

export const defGroupNumber: Def[] = [
Def.float, Def.int16, Def.int32, Def.int64, Def.int8,
Def.uint16, Def.uint32, Def.uint64, Def.uint8,
Def.long, Def.ulong, Def.longlong, Def.ulonglong,
]

export const defGroupPointer: Def[] = [
Def.boolPtr, Def.bytePtr, Def.charPtr, Def.intPtr, Def.int8Ptr,
Def.int16Ptr, Def.int32Ptr, Def.int64Ptr, Def.floatPtr,
Def.longPtr, Def.uintPtr, Def.uint8Ptr,
Def.intPtrPtr, Def.uint16Ptr, Def.uint32Ptr, Def.uint64Ptr,
Def.ulonglongPtr, Def.voidPtr,
Def.uintPtrPtr, Def.uint16PtrPtr, Def.uint32PtrPtr, Def.uint64PtrPtr,
Def.ulonglongPtrPtr, Def.voidPtrPtr,
]


const dllInstMap = new Map<string, unknown>() // for DLL.load() with settings.singleton === true
// const hasAsyncProxy = '__hasAsyncProxy__'
Expand Down Expand Up @@ -368,3 +387,106 @@ export function ptrToString(
const ret = txt ?? ''
return ret
}

/**
* Retrieve struct from Buffer
*/
export function bufferToStruct<T extends StructInstanceBase>(
src: Buffer,
structDef: StructDefType,
maxCount = 1,
pcbNeeded?: number,
align: 4 | 8 = 8, // btye
): T[] {

const ret: T[] = []

const blen = pcbNeeded ? pcbNeeded : src.byteLength
assert(blen >= 16, 'Buffer too small')

// const structDef = DS.PRINTER_INFO_1
const keyLen = Object.keys(structDef).length
assert(keyLen >= 1, 'keyLen must be >= 1')

const groupBtyeLen = keyLen * align
const bufByteLen = maxCount * groupBtyeLen

for (let i = 0; i < maxCount; i += 1) {
const buf = Buffer.alloc(bufByteLen)
src.copy(buf, 0, i * groupBtyeLen)
const struct = retriveStruct<T>(structDef, buf, blen, align)
ret.push(struct)
}

return ret
}

function retriveStruct<T extends StructInstanceBase>(
structDef: StructDefType, // DS.PRINTER_INFO_[L],
src: Buffer,
maxReadByteLength: number,
align: 4 | 8, // 32bit or 64bit
): T {

const struct = StructFactory<T>(structDef, { useStringBuffer: true })

Object.entries(structDef).forEach(([key, defType], idx) => {
const pos = idx * align

if (typeof defType === 'string') {
const valOrAddr = readAddrValue(src, defType, pos)
assert(typeof valOrAddr !== 'undefined')

if (defGroupNumber.includes(defType)) { // number value
// @ts-ignore
struct[key] = valOrAddr
}
else if (defGroupPointer.includes(defType)) { // pointer value
const ptrVal = ptrToString(valOrAddr, maxReadByteLength)
// @ts-ignore
struct[key] = ptrVal
}
else {
throw new TypeError(`Unknown key: "${key}", type: "${defType}"`)
}
}
else {
throw new Error(`Not implemented, only Def type is supported: key: "${key}"`)
}
})

return struct
}


function readAddrValue(
src: Buffer,
defType: Def,
pos: number,
): string | number | bigint | undefined {

let ret

if (defGroupPointer.includes(defType)) {
ret = isArch64 ? src.readInt64LE(pos) : src.readInt32LE(pos)
}
else if (defGroupNumber.includes(defType)) {
if (defType.includes('64')) {
ret = src.readInt64LE(pos)
}
else if (defType.includes('32')) {
ret = src.readInt32LE(pos)
}
else if (defType.includes('16')) {
ret = src.readInt16LE(pos)
}
else {
throw new Error(`Unknown defType: ${defType}`)
}
}
else {
throw new Error(`Unknown defType: ${defType}`)
}

return ret
}
58 changes: 58 additions & 0 deletions packages/win32-api/test/helper/97.bufferToStruct.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import assert from 'node:assert/strict'

import { fileShortPath } from '@waiting/shared-core'
import ref from 'ref-napi'

import {
DModel as M,
DTypes as W,
DStruct as DS,
StructFactory,
ucsBufferFrom,
bufferToStruct,
} from '../../src/index.js'
import { githubPrinterNames } from '../config.unittest.js'
import { CI } from '../root.config.js'


describe(fileShortPath(import.meta.url), () => {

describe('Should work ', () => {
it('useStringBuffer: false', async () => {
const docInfo = StructFactory<M.DOC_INFO_1>(DS.DOC_INFO_1, { useStringBuffer: false })
assert(Buffer.isBuffer(docInfo.pDocName))
docInfo.pDocName = ucsBufferFrom('nodefoo')
docInfo.pDatatype = ucsBufferFrom('RAW')

const docInfoBuf = docInfo.ref()
const arr = bufferToStruct<M.DOC_INFO_1>(docInfoBuf, DS.DOC_INFO_1)
assert(arr.length === 1)
const [struct] = arr
assert(struct)
assert(struct.pDocName.toString() === 'nodefoo')
assert(struct.pDatatype.toString() === 'RAW')
void arr
})

it.skip('useStringBuffer: true not support', async () => {
const name = 'nodefoo'
const docInfo = StructFactory<M.DOC_INFO_1>(DS.DOC_INFO_1, { useStringBuffer: true, maxCharLength: 16 })
const docInfo2 = StructFactory<M.DOC_INFO_1>(DS.DOC_INFO_1, { useStringBuffer: false, maxCharLength: 16 })
// assert(typeof docInfo.pDocName === 'string')
docInfo.pDocName = ucsBufferFrom(name)
docInfo.pDatatype = ucsBufferFrom('RAW')

docInfo2.pDocName = ucsBufferFrom(name)
docInfo2.pDatatype = ucsBufferFrom('RAW')

const docInfoBuf = docInfo.ref()
const docInfoBuf2 = docInfo2.ref()
const arr = bufferToStruct(docInfoBuf, DS.DOC_INFO_1)
assert(arr)
void arr
})

})

})

13 changes: 9 additions & 4 deletions packages/win32-api/test/winspool/503.GetPrinter.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import assert from 'node:assert/strict'

import { fileShortPath } from '@waiting/shared-core'
import { PRINTER_INFO_1, PRINTER_INFO_4 } from 'win32-def'

import {
winspoolGetDefaultPrinter,
Expand All @@ -19,10 +20,11 @@ describe(fileShortPath(import.meta.url), () => {
const hWnd = await winspoolOpenPrinter(pname)
assert(hWnd)

const ret = await winspoolGetPrinter(hWnd, 1)
const ret: PRINTER_INFO_1 | undefined = await winspoolGetPrinter(hWnd, 1)
assert(ret)

const {
Flags,
pDescription,
pName,
pComment,
Expand All @@ -31,6 +33,8 @@ describe(fileShortPath(import.meta.url), () => {
const name = pName
const comment = pComment.toString()

assert(typeof Flags === 'number')

assert(typeof pDescription === 'string')
assert(typeof pName === 'string')
assert(typeof pComment === 'string')
Expand All @@ -48,12 +52,13 @@ describe(fileShortPath(import.meta.url), () => {
const hWnd = await winspoolOpenPrinter(pname)
assert(hWnd)

const ret = await winspoolGetPrinter(hWnd, 4)
const ret: PRINTER_INFO_4 | undefined = await winspoolGetPrinter(hWnd, 4)
assert(ret)

const { pPrinterName, pServerName } = ret
const { pPrinterName, pServerName, Attributes } = ret
assert(pPrinterName)
console.log({ pPrinterName, pServerName })
assert(Attributes === 576)
console.log({ pPrinterName, pServerName, Attributes })
if (CI) {
assert(pPrinterName.includes('Microsoft Print to PDF'))
assert(pServerName === '')
Expand Down

0 comments on commit 86539fd

Please sign in to comment.