Skip to content

Commit

Permalink
feat(win32-api): winspoolGetPrinter()
Browse files Browse the repository at this point in the history
current support PRINTER_INFO Level:
- PRINTER_INFO_1
- PRINTER_INFO_4
  • Loading branch information
waitingsong committed Jul 22, 2022
1 parent 6a3fc4c commit c380490
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 11 deletions.
118 changes: 115 additions & 3 deletions packages/win32-api/src/func/winspool/helper.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,125 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import ref from 'ref-napi'

import { DllNames } from '../../index.js'
import {
DllNames,
DStruct as DS,
StructFactory,
ucsBufferSplit,
DModel as M,
} from '../../index.js'
import { Winspool as DLL } from '../../index.promise.js'


export { DModel as M } from '../../index.js'
export { ref }
export {
M, DS, ref,
}

export const dllName = DllNames.winspool
export type Win32Fns = DLL.Win32Fns


export function retriveStruct_PRINTER_INFO<L extends M.PRINTER_INFO_LEVEL>(
pPrinter: Buffer,
Level: L,
): M.PRINTER_INFO_X[L] {

switch (Level) {
case 1:
return retriveStruct_PRINTER_INFO_1(pPrinter) as M.PRINTER_INFO_X[L]
case 4:
return retriveStruct_PRINTER_INFO_4(pPrinter) as M.PRINTER_INFO_X[L]
default:
throw new Error(`Level not implemented:${Level}`)
}

}

export function retriveStruct_PRINTER_INFO_1(pPrinter: Buffer): M.PRINTER_INFO_1 {
const structDef = DS.PRINTER_INFO_1
const pbuf = Buffer.alloc(pPrinter.byteLength)
pPrinter.copy(pbuf, 0, 32)

const Flags = pPrinter.readUInt32LE()
const txtArr = ucsBufferSplit(pbuf)

const struct = StructFactory<M.PRINTER_INFO_X[1]>(structDef)
struct.Flags = Flags

const keys = Object.keys(structDef)
const keysRev = keys.reverse()
const txtLen = txtArr.length
keysRev.forEach((key, idx) => {
if (idx >= txtLen) {
return
}
if (key === 'Attributes') { return }
if (typeof txtArr[idx] === 'undefined') {
return
}
const str = txtArr[idx] ?? ''
// @ts-ignore
if (typeof struct[key] === 'string') {
// @ts-ignore
struct[key] = str
}
})

return struct

// const pDescriptionAddr = pPrinter.readUInt64LE(8)
// const pNameAddr = pPrinter.readUInt64LE(16)
// const pCommentAddr = pPrinter.readUInt64LE(24)
// // @ts-ignore
// const ptr = ref.alloc('void **')
// ptr.writeBigInt64LE(BigInt(pDescriptionAddr))
// const pName = ptr.deref()
// void pName

}

export function retriveStruct_PRINTER_INFO_4(pPrinter: Buffer): M.PRINTER_INFO_4 {
const structDef = DS.PRINTER_INFO_4
const pbuf = Buffer.alloc(pPrinter.byteLength)
pPrinter.copy(pbuf, 0, 24)

const attr = pPrinter.readUInt32LE(16)
// pPrinterName: WCHAR_String
// pServerName: WCHAR_String
// Attributes: DWORD

const txtArr = ucsBufferSplit(pbuf)

const struct = StructFactory<M.PRINTER_INFO_X[4]>(structDef)
struct.Attributes = attr

const keys = Object.keys(structDef)
// const keysRev = keys.reverse()
const txtLen = txtArr.length
keys.forEach((key, idx) => {
if (idx >= txtLen) {
return
}
if (typeof txtArr[idx] === 'undefined') {
return
}
const str = txtArr[idx] ?? ''
// @ts-ignore
if (typeof struct[key] === 'string') {
// @ts-ignore
struct[key] = str
}
})

return struct

// const pDescriptionAddr = pPrinter.readUInt64LE(8)
// const pNameAddr = pPrinter.readUInt64LE(16)
// const pCommentAddr = pPrinter.readUInt64LE(24)
// // @ts-ignore
// const ptr = ref.alloc('void **')
// ptr.writeBigInt64LE(BigInt(pDescriptionAddr))
// const pName = ptr.deref()
// void pName

}
50 changes: 48 additions & 2 deletions packages/win32-api/src/func/winspool/index.winspool.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import assert from 'node:assert'

import { ucsBufferFrom, ucsBufferToString } from '../../index.js'
import {
ucsBufferFrom,
ucsBufferToString,
} from '../../index.js'
import { getMod } from '../func.helper.js'

import { Win32Fns, dllName, M, ref } from './helper.js'
import {
Win32Fns,
M,
dllName,
ref,
retriveStruct_PRINTER_INFO,
} from './helper.js'


/**
Expand Down Expand Up @@ -56,3 +65,40 @@ export async function winspoolOpenPrinter(printerName: string): Promise<M.HANDLE
return 0
}


/**
* Retrieves information about a specified printer.
* @docs https://docs.microsoft.com/en-us/windows/win32/printdocs/getprinter
* @docs https://docs.microsoft.com/zh-cn/windows/win32/printdocs/getprinter
*/
export async function winspoolGetPrinter<Level extends M.PRINTER_INFO_LEVEL>(
hPrinter: M.HANDLE,
Level: Level,
maxByteLength = 4096,
): Promise<M.PRINTER_INFO_X[Level] | undefined> {

const mod = getMod<Win32Fns>(dllName)

const pPrinter = Buffer.alloc(maxByteLength)
const cbBuf = pPrinter.byteLength
const pcbNeeded = Buffer.alloc(8)

const ret = await mod.GetPrinterW(
hPrinter.toString(),
Level,
pPrinter,
cbBuf,
pcbNeeded,
)
const pcb = pcbNeeded.readUInt32LE()

if (ret) {
const struct = retriveStruct_PRINTER_INFO(pPrinter, Level)
return struct
}

if (pcb > 0 && pcb > maxByteLength) {
throw new Error(`maxByteLength is too small, increase to value grater than ${pcb}`)
}
}

30 changes: 30 additions & 0 deletions packages/win32-api/src/lib/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,33 @@ export function ucsBufferToString(buffer: Buffer, charCount?: number | undefined
return str.replace(/\0+$/u, '').replace(/^\0+/u, '')
}


/**
* Split with null till next char (\0)
*/
export function ucsBufferSplit(buffer: Buffer): string[] {
const ret: string[] = []
const row: string[] = []
const blen = buffer.byteLength

if (! blen) { return ret }

for (let i = 0; i < blen;) {
const t1 = ref.readCString(buffer, i)
if (t1) {
row.push(t1)
i += t1.length * 2
continue
}
else if (row.length > 0) {
ret.push(row.join(''))
row.length = 0
}
else {
i += 2
}
}

row.length = 0
return ret
}
51 changes: 46 additions & 5 deletions packages/win32-api/test/winspool/503.GetPrinter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,63 @@ import ref from 'ref-napi'
import { sleep } from 'zx'

import * as CS from '../../src/index.consts.js'
import {
winspoolGetDefaultPrinter,
winspoolGetPrinter,
winspoolOpenPrinter,
} from '../../src/index.fun.js'
import {
DModel as M,
DTypes as W,
DStruct as DS,
StructFactory,
} from '../../src/index.js'
import { winspool } from '../helper.js'
import { CI } from '../root.config.js'


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

describe('Should GetPrinter() work', () => {
it('normal', async () => {
// const ret = await winspool.GetPrinter()
// assert(ret)
assert(true)
it('Level 1', async () => {
const pname = await winspoolGetDefaultPrinter()
assert(pname)
const hWnd = await winspoolOpenPrinter(pname)
assert(hWnd)

const ret = await winspoolGetPrinter(hWnd, 1)
assert(ret)

const {
pDescription,
pName,
pComment,
} = ret
const desc = pDescription
const name = pName
const comment = pComment.toString()
console.log({ desc, name, comment })
if (CI) {
assert(name.includes('Microsoft Print to PDF'))
assert(comment.includes('Microsoft Print to PDF'))
}
})

it.only('Level 4', async () => {
const pname = await winspoolGetDefaultPrinter()
assert(pname)
const hWnd = await winspoolOpenPrinter(pname)
assert(hWnd)

const ret = await winspoolGetPrinter(hWnd, 4)
assert(ret)

const { pPrinterName, pServerName } = ret
assert(pPrinterName)
console.log({ pPrinterName, pServerName })
if (CI) {
assert(pPrinterName.includes('Microsoft Print to PDF'))
assert(pServerName === '')
}
})
})
})
Expand Down
15 changes: 14 additions & 1 deletion packages/win32-def/src/lib/struct/winspool.def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
DWORD,
LPTSTR,
} from '../common.def.js'
// import { wcharBuffer } from '../fixed-buffer.js'
import { StructType } from '../helper.js'

import { DEVMODEW } from './wingdi.def.js'
Expand Down Expand Up @@ -38,3 +37,17 @@ export const PRINTER_INFO_1 = {
pComment: LPTSTR,
} as const


/**
* Specifies general printer information
* @docs https://docs.microsoft.com/en-us/windows/win32/printdocs/printer-info-4
* @description The structure can be used to retrieve minimal printer information on a call to EnumPrinters.
* Such a call is a fast and easy way to retrieve the names and attributes of all locally installed printers
* on a system and all remote printer connections that a user has established.
*/
export const PRINTER_INFO_4 = {
pPrinterName: LPTSTR,
pServerName: LPTSTR,
Attributes: DWORD,
} as const

15 changes: 15 additions & 0 deletions packages/win32-def/src/lib/struct/winspool.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export type LPPRINTER_DEFAULTS = _POINTER
*/
export interface PRINTER_INFO_X {
1: PRINTER_INFO_1
4: PRINTER_INFO_4
[key: number]: StructInstanceBase
}

Expand All @@ -54,3 +55,17 @@ export interface PRINTER_INFO_1 extends StructInstanceBase {
}
export type PPRINTER_INFO_1 = _POINTER

/**
* Specifies general printer information
* @docs https://docs.microsoft.com/en-us/windows/win32/printdocs/printer-info-4
* @description The structure can be used to retrieve minimal printer information on a call to EnumPrinters.
* Such a call is a fast and easy way to retrieve the names and attributes of all locally installed printers
* on a system and all remote printer connections that a user has established.
*/
export interface PRINTER_INFO_4 extends StructInstanceBase {
pPrinterName: WCHAR_String
pServerName: WCHAR_String
Attributes: DWORD
}
export type PPRINTER_INFO_4 = _POINTER

0 comments on commit c380490

Please sign in to comment.