-
Notifications
You must be signed in to change notification settings - Fork 5
/
winrt.ahk
168 lines (156 loc) · 5.89 KB
/
winrt.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include overload.ahk
#include guid.ahk
#include hstring.ahk
#include rtmetadata.ahk
#include rtinterface.ahk
#include delegate.ahk
#include struct.ahk
MAX_NAME_CCH := 1024
/*
Core WinRT functions.
- WinRT(classname) creates a runtime object.
- WinRT(rtobj) "casts" rtobj to its most derived class.
- WinRT(ptr) wraps a runtime interface pointer, taking ownership of the reference.
- WinRT.GetType(name) returns a TypeInfo for the given type.
*/
class WinRT {
static Call(p) => (
p is String ? this.GetType(p).Class :
p is Object ? (ObjAddRef(p.ptr), _rt_WrapInspectable(p.ptr)) :
_rt_WrapInspectable(p)
)
static TypeCache := Map(
"Guid", RtRootTypes.Guid, ; Found in type names returned by GetRuntimeClassName.
"System.Guid", RtRootTypes.Guid, ; Resolved from metadata TypeRef.
; All WinRT typedefs tested on Windows 10.0.19043 derive from one of these.
'System.Attribute', RtRootTypes.Attribute,
'System.Enum', RtRootTypes.Enum,
'System.MulticastDelegate', RtRootTypes.Delegate,
'System.Object', RtRootTypes.Object,
'System.ValueType', RtRootTypes.Struct,
)
static __new() {
cache := this.TypeCache
for e, t in _rt_GetElementTypeMap() {
; Map the simple types in cache, for parsing generic type names.
cache[t.Name] := t
}
this.DefineProp('__set', {call: RtAny.__set})
}
static _CacheGetMetaData(typename, &td) {
#DllLoad wintypes.dll
DllCall("wintypes.dll\RoGetMetaDataFile"
, "ptr", HStringFromString(typename)
, "ptr", 0
, "ptr", 0
, "ptr*", m := RtMetaDataModule()
, "uint*", &td := 0
, "hresult")
static cache := Map()
; Cache modules by filename to conserve memory and so cached property values
; can be used by all namespaces within the module.
return cache.get(mn := m.Name, false) || cache[mn] := m
}
static _CacheGetTypeNS(name) {
if !(p := InStr(name, ".",, -1))
throw ValueError("Invalid typename", -1, name)
static cache := Map()
; Cache module by namespace, since all types *directly* within a namespace
; must be defined within the same file (but child namespaces can be defined
; in a different file).
try {
if m := cache.get(ns := SubStr(name, 1, p-1), false) {
; Module already loaded - find the TypeDef within it.
td := m.FindTypeDefByName(name)
}
else {
; Since we haven't seen this namespace before, let the system work out
; which module contains its metadata.
cache[ns] := m := this._CacheGetMetaData(name, &td)
}
}
catch OSError as e {
if e.number = 0x80073D54 {
e.message := "(0x80073D54) Type not found."
e.extra := name
}
throw
}
return RtTypeInfo(m, td)
}
static _CacheGetType(name) {
if p := InStr(name, "<") {
baseType := this.GetType(baseName := SubStr(name, 1, p-1))
typeArgs := []
while RegExMatch(name, "\G([^<>,]++(?:<(?:(?1)(?:,|(?=>)))++>)?)(?=[,>])", &m, ++p) {
typeArgs.Push(this.GetType(m.0))
p += m.Len
}
if p != StrLen(name) + 1
throw Error("Parse error or bad name.", -1, SubStr(name, p) || name)
return {
typeArgs: typeArgs,
m: baseType.m, t: baseType.t,
base: baseType.base
}
}
return this._CacheGetTypeNS(name)
}
static GetType(name) {
static cache := this.TypeCache
; Cache typeinfo by full name.
return cache.get(name, false)
|| cache[name] := this._CacheGetType(name)
}
}
class RtMetaDataModule extends MetaDataModule {
GetTypeByToken(t, typeArgs:=false) {
scope := -1
switch (t >> 24) {
case 0x01: ; TypeRef (most common)
; TODO: take advantage of GetTypeRefProps's scope parameter
return WinRT.GetType(this.GetTypeRefProps(t))
case 0x02: ; TypeDef
MsgBox 'DEBUG: GetTypeByToken was called with a TypeDef token.`n`n' Error().Stack
; TypeDefs usually aren't referenced directly, so just resolve it by
; name to ensure caching works correctly. Although GetType resolving
; the TypeDef will be a bit redundant, it should perform the same as
; if a TypeRef token was passed in.
return WinRT.GetType(this.GetTypeDefProps(t))
case 0x1b: ; TypeSpec
; GetTypeSpecFromToken
ComCall(44, this, "uint", t, "ptr*", &psig:=0, "uint*", &nsig:=0)
; Signature: 0x15 0x12 <typeref> <argcount> <args>
nsig += psig++
return _rt_DecodeSigGenericInst(this, &psig, nsig, typeArgs)
default:
throw Error(Format("Cannot resolve token 0x{:08x} to type info.", t), -1)
}
}
}
_rt_WrapInspectable(p, typeinfo:=false) {
if !p
return
; IInspectable::GetRuntimeClassName
hr := ComCall(4, p, "ptr*", &hcls:=0, "int")
if hr >= 0 {
cls := HStringRet(hcls)
if !typeinfo || !InStr(cls, "<")
typeinfo := WinRT.GetType(cls)
; else it's not a full runtime class, so just use the predetermined typeinfo.
}
else if !typeinfo || hr != -2147467263 { ; E_NOTIMPL
e := OSError(hr)
e.Message := "IInspectable::GetRuntimeClassName failed`n`t" e.Message
throw e
}
return {
ptr: p,
base: typeinfo.Class.prototype
}
}
_rt_memoize(this, propname, f := unset) {
value := IsSet(f) ? f(this) : this._init_%propname%()
this.DefineProp propname, {value: value}
return value
}