From 86539fd194f9275e5bff98a51ae74a3e97efd1db Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Sun, 24 Jul 2022 16:35:12 +0800 Subject: [PATCH] feat(win32-api): helper bufferToStruct() --- .../win32-api/src/func/winspool/helper.ts | 33 +++-- packages/win32-api/src/lib/helper.ts | 122 ++++++++++++++++++ .../test/helper/97.bufferToStruct.test.ts | 58 +++++++++ .../test/winspool/503.GetPrinter.test.ts | 13 +- 4 files changed, 213 insertions(+), 13 deletions(-) create mode 100644 packages/win32-api/test/helper/97.bufferToStruct.test.ts diff --git a/packages/win32-api/src/func/winspool/helper.ts b/packages/win32-api/src/func/winspool/helper.ts index 941a7902..854fd2bd 100644 --- a/packages/win32-api/src/func/winspool/helper.ts +++ b/packages/win32-api/src/func/winspool/helper.ts @@ -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' @@ -40,24 +39,38 @@ export function retriveStruct_PRINTER_INFO( 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( 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( pPrinter, + structDef, maxCount, pcb, - 4, - structDef, ) as M.PRINTER_INFO_X[L][] break } @@ -70,7 +83,7 @@ export function retriveStruct_PRINTER_INFO( } - +/* function rpi1( addressBuffer: Buffer, maxByteLength: number, @@ -169,3 +182,5 @@ function loopRead( return ret } +*/ + diff --git a/packages/win32-api/src/lib/helper.ts b/packages/win32-api/src/lib/helper.ts index c0e0a1b3..79f90f08 100644 --- a/packages/win32-api/src/lib/helper.ts +++ b/packages/win32-api/src/lib/helper.ts @@ -11,6 +11,7 @@ import { AsyncSyncFuncModel, DllFuncs, DllFuncsModel, + Def, ExpandFnModel, FnName, FnParams, @@ -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() // for DLL.load() with settings.singleton === true // const hasAsyncProxy = '__hasAsyncProxy__' @@ -368,3 +387,106 @@ export function ptrToString( const ret = txt ?? '' return ret } + +/** + * Retrieve struct from Buffer + */ +export function bufferToStruct( + 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(structDef, buf, blen, align) + ret.push(struct) + } + + return ret +} + +function retriveStruct( + structDef: StructDefType, // DS.PRINTER_INFO_[L], + src: Buffer, + maxReadByteLength: number, + align: 4 | 8, // 32bit or 64bit +): T { + + const struct = StructFactory(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 +} diff --git a/packages/win32-api/test/helper/97.bufferToStruct.test.ts b/packages/win32-api/test/helper/97.bufferToStruct.test.ts new file mode 100644 index 00000000..466ab34d --- /dev/null +++ b/packages/win32-api/test/helper/97.bufferToStruct.test.ts @@ -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(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(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(DS.DOC_INFO_1, { useStringBuffer: true, maxCharLength: 16 }) + const docInfo2 = StructFactory(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 + }) + + }) + +}) + diff --git a/packages/win32-api/test/winspool/503.GetPrinter.test.ts b/packages/win32-api/test/winspool/503.GetPrinter.test.ts index fbb8fa01..c5f3a473 100644 --- a/packages/win32-api/test/winspool/503.GetPrinter.test.ts +++ b/packages/win32-api/test/winspool/503.GetPrinter.test.ts @@ -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, @@ -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, @@ -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') @@ -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 === '')