refactor(user32): rework WNDCLASS[EX] registration

This commit is contained in:
Stephan Schreiber
2025-08-21 07:23:13 +02:00
parent f9e2a23839
commit f6fc2e4e2e
2 changed files with 69 additions and 126 deletions
+35 -62
View File
@@ -26,12 +26,8 @@ import type {
*/
export class ACL {
readonly AclRevision = Internals.ACL_REVISION
readonly Sbsz1 = 0 // Used for alignment
readonly Sbsz2 = 0 // Used for alignment
constructor(
public AceCount: number = 0,
public AclSize: number = 0
) {}
public AceCount!: number
public AclSize!: number
}
/** @internal */
@@ -63,15 +59,9 @@ export const cSID_IDENTIFIER_AUTHORITY = koffi.array(cBYTE, 6, 'Array')
*/
export class SID {
readonly Revision = Internals.SID_REVISION
public SubAuthorityCount: number
public SubAuthority: number[]
constructor(
public IdentifierAuthority: SID_IDENTIFIER_AUTHORITY,
...SubAuthority: number[]
) {
this.SubAuthorityCount = SubAuthority.length
this.SubAuthority = SubAuthority.slice()
}
public SubAuthorityCount!: number
public SubAuthority!: number[]
public IdentifierAuthority!: SID_IDENTIFIER_AUTHORITY
}
/** @internal */
@@ -156,32 +146,17 @@ export const cCLAIM_SECURITY_ATTRIBUTE_OCTET_STRING_VALUE = koffi.struct({
* https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-claim_security_attribute_v1
*/
export class CLAIM_SECURITY_ATTRIBUTE_V1 {
readonly Reserved = 0
public Values = new koffi.Union('CLAIM_SECURITY_ATTRIBUTE_V1_Values') as {
public Name!: string
public ValueType!: CLAIM_SECURITY_ATTRIBUTE_TYPE_
public Flags!: CLAIM_SECURITY_ATTRIBUTE_
public ValueCount!: number
public Values = new koffi.Union('CSA_V1_Values') as {
pInt64?: BigInt[]
pUint64?: BigInt[]
ppString?: string[]
pFqbn?: CLAIM_SECURITY_ATTRIBUTE_FQBN_VALUE[]
pOctetString?: CLAIM_SECURITY_ATTRIBUTE_OCTET_STRING_VALUE[]
}
constructor(
public Name: string = null!,
public Flags: CLAIM_SECURITY_ATTRIBUTE_ = 0 as CLAIM_SECURITY_ATTRIBUTE_,
public ValueType: CLAIM_SECURITY_ATTRIBUTE_TYPE_ = 0,
public ValueCount = 0,
{ pInt64, pUint64, ppString, pFqbn, pOctetString }: CLAIM_SECURITY_ATTRIBUTE_V1['Values'] = {}
) {
if (pInt64)
this.Values.pInt64 = pInt64
else if (pUint64)
this.Values.pInt64 = pUint64
else if (ppString)
this.Values.ppString = ppString
else if (pFqbn)
this.Values.pFqbn = pFqbn
else if (pOctetString)
this.Values.pOctetString = pOctetString
}
}
/** @internal */
@@ -191,7 +166,7 @@ export const cCLAIM_SECURITY_ATTRIBUTE_V1 = koffi.struct({
Reserved: cWORD,
Flags: cDWORD,
ValueCount: cDWORD,
Values: koffi.union('CLAIM_SECURITY_ATTRIBUTE_V1_Values', {
Values: koffi.union('CSA_V1_Values', {
pInt64: koffi.pointer(cLONG64, Internals.ANYSIZE_ARRAY),
pUint64: koffi.pointer(cDWORD64, Internals.ANYSIZE_ARRAY),
ppString: koffi.pointer(cSTR, Internals.ANYSIZE_ARRAY),
@@ -217,7 +192,7 @@ export const cCLAIM_SECURITY_ATTRIBUTES_INFORMATION = koffi.struct({
Version: cWORD,
Reserved: cWORD,
AttributeCount: cDWORD,
Attribute: koffi.struct({ // Should be a union, but there is only one member...
Attribute: koffi.struct({ // Should be a union, but since there is only one member...
pAttributeV1: koffi.pointer(cCLAIM_SECURITY_ATTRIBUTE_V1, Internals.ANYSIZE_ARRAY)
})
})
@@ -645,19 +620,17 @@ export const cDLGPROC = koffi.pointer(koffi.proto('DLG', cLRESULT, [ cHANDLE, cU
*
* https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassw
*/
export class WNDCLASS {
constructor(
public hInstance: HINSTANCE | null = null,
public lpszClassName: string = '',
public style: CS_ = 0,
public lpfnWndProc: WNDPROC | null = null,
public hCursor: HCURSOR | null = null,
public hIcon: HICON | null = null,
public hbrBackground: HBRUSH | null = null,
public lpszMenuName: string | null = null,
public cbClsExtra: number = 0,
public cwWndExtra: number = 0
) {}
export interface WNDCLASS {
hInstance: HINSTANCE | null
lpszClassName: string | null
style: CS_
lpfnWndProc: WNDPROC | null
hCursor: HCURSOR | null
hIcon: HICON | null
hbrBackground: HBRUSH | null
lpszMenuName: string | null
cbClsExtra: number
cwWndExtra: number
}
/** @internal */
@@ -683,16 +656,16 @@ export class WNDCLASSEX {
readonly cbSize = SIZEOF_WNDCLASSEX
constructor(
public hInstance: HINSTANCE | null = null,
public lpszClassName: string = '',
public style: CS_ = 0,
public lpfnWndProc: WNDPROC | null = null,
public hCursor: HCURSOR | null = null,
public hIcon: HICON | null = null,
public hIconSm: HICON | null = null,
public hbrBackground: HBRUSH | null = null,
public lpszMenuName: string | null = null,
public cbClsExtra: number = 0,
public cwWndExtra: number = 0
public lpszClassName: string | null = null,
public style: CS_ = 0,
public lpfnWndProc: WNDPROC | null = null,
public hCursor: HCURSOR | null = null,
public hIcon: HICON | null = null,
public hIconSm: HICON | null = null,
public hbrBackground: HBRUSH | null = null,
public lpszMenuName: string | null = null,
public cbClsExtra: number = 0,
public cwWndExtra: number = 0
) {}
}
@@ -1108,7 +1081,7 @@ export const cPOLICY_ACCOUNT_DOMAIN_INFO = koffi.struct({
/**
* Used to set and query the system's auditing rules.
*
* learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-policy_audit_events_info
* https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-policy_audit_events_info
*/
export interface POLICY_AUDIT_EVENTS_INFO {
AuditingMode: boolean
@@ -1119,7 +1092,7 @@ export interface POLICY_AUDIT_EVENTS_INFO {
/** @internal */
export const cPOLICY_AUDIT_EVENTS_INFO = koffi.struct({
AuditingMode: cBOOL,
EventAuditingOptions: koffi.array(cULONG, 'MaximumAuditEventCount', Internals.POLICY_AUDIT_EVENTS_INFO_MAX_OPTIONS),
EventAuditingOptions: koffi.pointer(null, cULONG, 'MaximumAuditEventCount'),
MaximumAuditEventCount: cULONG
})
+34 -64
View File
@@ -5,56 +5,40 @@ import {
cATOM, type ATOM, cHANDLE, type HINSTANCE, type HWND
} from '../ctypes.js'
import {
cWNDCLASS, WNDCLASS,
cWNDCLASS, type WNDCLASS,
cWNDCLASSEX, WNDCLASSEX,
cWNDPROC
} from '../structs.js'
import { user32 } from './lib.js'
const wndProcs = new Map<string | ATOM, koffi.IKoffiRegisteredCallback>()
const wndProcs = new Map<ATOM, koffi.IKoffiRegisteredCallback | undefined>()
/**
* Retrieves information about a window class.
*
* FIXME: at the moment, libwin32 does not support passing an ATOM for `className`.
* Note: libwin32 only supports querying by class name (string).
*
* https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclassinfow
*/
export function GetClassInfo(hInstance: HINSTANCE | null, className: string): WNDCLASS | null {
const wndClass = new WNDCLASS()
return typeof className === 'number' ? info_by_atom() : info_by_string()
GetClassInfo.native ??= user32.func('GetClassInfoW', cBOOL, [ cHANDLE, cSTR, koffi.out(koffi.pointer(cWNDCLASS)) ])
function info_by_atom() {
info_by_atom.native ??= user32.func('GetClassInfoW', cBOOL, [ cHANDLE, cATOM, koffi.out(koffi.pointer(cWNDCLASS)) ])
return info_by_atom.native(hInstance, className, wndClass) !== 0 ? wndClass : null
}
function info_by_string() {
info_by_string.native ??= user32.func('GetClassInfoW', cBOOL, [ cHANDLE, cSTR, koffi.out(koffi.pointer(cWNDCLASS)) ])
return info_by_string.native(hInstance, className, wndClass) !== 0 ? wndClass : null
}
const wndClass = {} as WNDCLASS
return GetClassInfo.native(hInstance, className, wndClass) !== 0 ? wndClass : null
}
/**
* Retrieves information about a window class, including a handle to the small icon associated with the window class.
*
* FIXME: at the moment, libwin32 does not support passing an ATOM for `className`.
* Note: libwin32 only supports querying by class name (string).
*
* https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclassinfoexw
*/
export function GetClassInfoEx(hInstance: HINSTANCE | null, className: string): WNDCLASSEX | null {
GetClassInfoEx.native ??= user32.func('GetClassInfoExW', cBOOL, [ cHANDLE, cSTR, koffi.out(koffi.pointer(cWNDCLASSEX)) ])
const wndClassEx = new WNDCLASSEX()
return typeof className === 'number' ? info_by_atom() : info_by_string()
function info_by_atom() {
info_by_atom.native ??= user32.func('GetClassInfoExW', cBOOL, [ cHANDLE, cATOM, koffi.out(koffi.pointer(cWNDCLASSEX)) ])
return info_by_atom.native(hInstance, className, wndClassEx) !== 0 ? wndClassEx : null
}
function info_by_string() {
info_by_string.native ??= user32.func('GetClassInfoExW', cBOOL, [ cHANDLE, cSTR, koffi.out(koffi.pointer(cWNDCLASSEX)) ])
return info_by_string.native(hInstance, className, wndClassEx) !== 0 ? wndClassEx : null
}
return GetClassInfoEx.native(hInstance, className, wndClassEx) !== 0 ? wndClassEx : null
}
/**
@@ -62,12 +46,12 @@ export function GetClassInfoEx(hInstance: HINSTANCE | null, className: string):
*
* https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclassnamew
*/
export function GetClassName(hWnd: HWND): string {
export function GetClassName(hWnd: HWND): string | null {
GetClassName.native ??= user32.func('GetClassNameW', cINT, [ cHANDLE, koffi.out(cPVOID), cINT ])
const className = new StringOutputBuffer(256)
const len = GetClassName.native(hWnd, className.buffer, className.length)
return className.decode(len)
return len ? className.decode(len) : null
}
/**
@@ -75,10 +59,10 @@ export function GetClassName(hWnd: HWND): string {
*
* https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclassw
*/
export function RegisterClass(wndClass: WNDCLASS): ATOM {
export function RegisterClass(wndClass: WNDCLASS): ATOM | null {
RegisterClass.native ??= user32.func('RegisterClassW', cATOM, [ koffi.pointer(cWNDCLASS) ])
let wndProc: koffi.IKoffiRegisteredCallback | undefined = undefined
let wndProc: koffi.IKoffiRegisteredCallback | undefined
if (typeof wndClass.lpfnWndProc === 'function') {
wndProc = koffi.register(wndClass.lpfnWndProc, cWNDPROC)
wndClass = {
@@ -88,15 +72,9 @@ export function RegisterClass(wndClass: WNDCLASS): ATOM {
}
const atom = RegisterClass.native(wndClass)
if (atom && wndProc) {
wndProcs.set(atom, wndProc)
if (wndClass.lpszClassName)
wndProcs.set(wndClass.lpszClassName, wndProc)
}
else if (wndProc)
koffi.unregister(wndProc)
return atom
? (wndProcs.set(atom, wndProc), atom)
: (wndProc && koffi.unregister(wndProc), null)
}
/**
@@ -104,10 +82,10 @@ export function RegisterClass(wndClass: WNDCLASS): ATOM {
*
* https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclassexw
*/
export function RegisterClassEx(wndClassEx: WNDCLASSEX): ATOM {
export function RegisterClassEx(wndClassEx: WNDCLASSEX): ATOM | null {
RegisterClassEx.native ??= user32.func('RegisterClassExW', cATOM, [ koffi.pointer(cWNDCLASSEX) ])
let wndProc: koffi.IKoffiRegisteredCallback | undefined = undefined
let wndProc: koffi.IKoffiRegisteredCallback | undefined
if (typeof wndClassEx.lpfnWndProc === 'function') {
wndProc = koffi.register(wndClassEx.lpfnWndProc, cWNDPROC)
wndClassEx = {
@@ -117,38 +95,30 @@ export function RegisterClassEx(wndClassEx: WNDCLASSEX): ATOM {
}
const atom = RegisterClassEx.native(wndClassEx)
if (atom && wndProc) {
wndProcs.set(atom, wndProc)
if (wndClassEx.lpszClassName)
wndProcs.set(wndClassEx.lpszClassName, wndProc)
}
else if (wndProc)
koffi.unregister(wndProc)
return atom
? (wndProcs.set(atom, wndProc), atom)
: (wndProc && koffi.unregister(wndProc), null)
}
/**
* Unregisters a window class, freeing the memory required for the class.
*
* Note: despite the parameter being named `className`, as in the native API,
* libwin32 only supports unregistering by ATOM -- the one you received
* from RegisterClass/RegisterClassEx.
*
* https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-unregisterclassw
*/
export function UnregisterClass(className: ATOM | string, hInstance: HINSTANCE | null = null): boolean {
const wndProc = wndProcs.get(className)
const ret = typeof className === 'number' ? unregister_by_atom() : unregister_by_string()
if (wndProc) {
koffi.unregister(wndProc)
wndProcs.forEach((v, k) => v === wndProc && wndProcs.delete(k))
export function UnregisterClass(className: ATOM, hInstance: HINSTANCE | null = null): boolean {
UnregisterClass.native ??= user32.func('UnregisterClassW', cBOOL, [ cATOM, cHANDLE ])
const ret = UnregisterClass.native(className, hInstance) !== 0
if (ret) {
const wndProc = wndProcs.get(className)
if (wndProc) {
koffi.unregister(wndProc)
wndProcs.delete(className)
}
}
return ret
function unregister_by_atom() {
unregister_by_atom.native ??= user32.func('UnregisterClassW', cBOOL, [ cATOM, cHANDLE ])
return unregister_by_atom.native(className, hInstance) !== 0
}
function unregister_by_string() {
unregister_by_string.native ??= user32.func('UnregisterClassW', cBOOL, [ cSTR, cHANDLE ])
return unregister_by_string.native(className, hInstance) !== 0
}
}