arcane-os 0.78.0 → 0.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,833 @@
1
+ # Invoked through stdin by the SDK's owned PowerShell process. Keep stdout JSON-only.
2
+ $ErrorActionPreference = 'Stop'
3
+ $ProgressPreference = 'SilentlyContinue'
4
+ [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
5
+
6
+ $inventoryDevices = [System.Collections.Generic.List[object]]::new()
7
+ $inventoryIssues = [System.Collections.Generic.List[object]]::new()
8
+
9
+ function Add-InventoryIssue {
10
+ param([string]$Source, [string]$Code, [string]$Message, $DeviceId = $null)
11
+
12
+ $inventoryIssues.Add([ordered]@{
13
+ source = $Source
14
+ code = $Code
15
+ message = $Message
16
+ deviceId = $DeviceId
17
+ })
18
+ }
19
+
20
+ function Get-InventoryErrorText {
21
+ param([System.Management.Automation.ErrorRecord]$Record)
22
+
23
+ $details = [System.Collections.Generic.List[string]]::new()
24
+ $details.Add($Record.Exception.ToString())
25
+ if ($null -ne $Record.ErrorDetails) {
26
+ $details.Add($Record.ErrorDetails.Message)
27
+ }
28
+ if ($null -ne $Record.InvocationInfo) {
29
+ $details.Add($Record.InvocationInfo.PositionMessage)
30
+ }
31
+ if ($Record.ScriptStackTrace) {
32
+ $details.Add($Record.ScriptStackTrace)
33
+ }
34
+ return [string]::Join([Environment]::NewLine, $details)
35
+ }
36
+
37
+ function Add-HostCpu {
38
+ try {
39
+ $processors = [System.Collections.Generic.List[object]]::new()
40
+ $names = [System.Collections.Generic.List[string]]::new()
41
+ $reportedProcessors = @(Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop)
42
+ foreach ($processor in $reportedProcessors) {
43
+ $processorName = [string]$processor.Name
44
+ $processorId = [string]$processor.DeviceID
45
+ if ($processorName -ne '') {
46
+ $names.Add($processorName)
47
+ }
48
+ if ($processorName -eq '' -or $processorId -eq '') {
49
+ Add-InventoryIssue 'cpu' 'property-unavailable' 'Win32_Processor did not report a processor Name or DeviceID.' 'cpu'
50
+ }
51
+ $processors.Add([ordered]@{
52
+ deviceId = $processorId
53
+ name = $processorName
54
+ socketDesignation = if ($null -eq $processor.SocketDesignation) { $null } else { [string]$processor.SocketDesignation }
55
+ cores = if ($null -eq $processor.NumberOfCores) { $null } else { [uint32]$processor.NumberOfCores }
56
+ logicalProcessors = if ($null -eq $processor.NumberOfLogicalProcessors) { $null } else { [uint32]$processor.NumberOfLogicalProcessors }
57
+ })
58
+ }
59
+ if ($processors.Count -eq 0) {
60
+ Add-InventoryIssue 'cpu' 'enumeration-failed' 'Win32_Processor returned no processor records.'
61
+ return
62
+ }
63
+ $cpuName = if ($names.Count -gt 0) { [string]::Join(' / ', $names) } else { 'Host CPU' }
64
+ $inventoryDevices.Add([ordered]@{
65
+ deviceId = 'cpu'
66
+ identitySource = 'host-cpu'
67
+ kind = 'cpu'
68
+ name = $cpuName
69
+ present = $true
70
+ isHardware = $null
71
+ isIntegrated = $null
72
+ dedicatedMemoryMiB = $null
73
+ addresses = [ordered]@{
74
+ adapterLuid = $null
75
+ pci = $null
76
+ dxgiAdapterIndex = $null
77
+ }
78
+ processors = $processors.ToArray()
79
+ })
80
+ } catch {
81
+ Add-InventoryIssue 'cpu' 'enumeration-failed' (Get-InventoryErrorText $_)
82
+ }
83
+ }
84
+
85
+ function Get-PresentPciDevices {
86
+ # One present-device census and one batched property query serve both kinds.
87
+ # A PCI bus address is a correlation fact, never a persisted selection key.
88
+ $devicesById = @{}
89
+ try {
90
+ $census = @(Get-PnpDevice -PresentOnly -ErrorAction Continue 2>&1 3>&1)
91
+ foreach ($item in $census) {
92
+ if ($item -is [System.Management.Automation.ErrorRecord]) {
93
+ Add-InventoryIssue 'identity' 'enumeration-failed' (Get-InventoryErrorText $item)
94
+ continue
95
+ }
96
+ if ($item -is [System.Management.Automation.WarningRecord]) {
97
+ Add-InventoryIssue 'identity' 'enumeration-failed' $item.Message
98
+ continue
99
+ }
100
+ $instanceId = [string]$item.InstanceId
101
+ if ($instanceId.StartsWith('PCI\', [StringComparison]::OrdinalIgnoreCase)) {
102
+ $devicesById[$instanceId] = [ordered]@{
103
+ instanceId = $instanceId
104
+ bus = $null
105
+ address = $null
106
+ }
107
+ }
108
+ }
109
+ if ($devicesById.Count -gt 0) {
110
+ $properties = @(
111
+ Get-PnpDeviceProperty -InstanceId ([string[]]@($devicesById.Keys)) -KeyName 'DEVPKEY_Device_BusNumber', 'DEVPKEY_Device_Address' -ErrorAction Continue 2>&1 3>&1
112
+ )
113
+ foreach ($property in $properties) {
114
+ if ($property -is [System.Management.Automation.ErrorRecord]) {
115
+ Add-InventoryIssue 'identity' 'property-failed' (Get-InventoryErrorText $property)
116
+ continue
117
+ }
118
+ if ($property -is [System.Management.Automation.WarningRecord]) {
119
+ Add-InventoryIssue 'identity' 'property-failed' $property.Message
120
+ continue
121
+ }
122
+ $entry = $devicesById[[string]$property.InstanceId]
123
+ if ($null -eq $entry -or $null -eq $property.Data) {
124
+ continue
125
+ }
126
+ if ($property.KeyName -eq 'DEVPKEY_Device_BusNumber') {
127
+ $entry.bus = [uint32]$property.Data
128
+ } elseif ($property.KeyName -eq 'DEVPKEY_Device_Address') {
129
+ # DEVPKEY_Device_Address is INT32; preserve its unsigned bits.
130
+ $address = [uint32]([int64]$property.Data -band 4294967295L)
131
+ if ($address -eq 4294967295L) {
132
+ Add-InventoryIssue 'identity' 'property-unavailable' 'The PCI bus driver did not supply a device address.' $entry.instanceId
133
+ } else {
134
+ $entry.address = $address
135
+ }
136
+ }
137
+ }
138
+ }
139
+ } catch [System.Management.Automation.CommandNotFoundException] {
140
+ Add-InventoryIssue 'identity' 'enumeration-unavailable' (Get-InventoryErrorText $_)
141
+ } catch {
142
+ Add-InventoryIssue 'identity' 'enumeration-failed' (Get-InventoryErrorText $_)
143
+ }
144
+ return $devicesById
145
+ }
146
+
147
+ # SDK-authored ABI declarations for the Windows system libraries. Buffer lengths
148
+ # below are native-call framing only; no inventory content limit is imposed.
149
+ $nativeSource = @'
150
+ using System;
151
+ using System.Collections.Generic;
152
+ using System.Runtime.InteropServices;
153
+ using System.Text;
154
+
155
+ namespace Arcane.ExecutionDevices
156
+ {
157
+ public sealed class InventoryIssue
158
+ {
159
+ public string Source;
160
+ public string Code;
161
+ public string Message;
162
+
163
+ public InventoryIssue(string source, string code, string message)
164
+ {
165
+ Source = source;
166
+ Code = code;
167
+ Message = message;
168
+ }
169
+ }
170
+
171
+ [StructLayout(LayoutKind.Sequential)]
172
+ public struct AdapterLuid
173
+ {
174
+ public uint LowPart;
175
+ public int HighPart;
176
+ }
177
+
178
+ [StructLayout(LayoutKind.Sequential)]
179
+ public struct PciAddress
180
+ {
181
+ public uint Bus;
182
+ public uint Device;
183
+ public uint Function;
184
+ }
185
+
186
+ public sealed class Adapter
187
+ {
188
+ public string Kind;
189
+ public string Name;
190
+ public bool Present;
191
+ public bool? IsHardware;
192
+ public bool? IsIntegrated;
193
+ public double? DedicatedMemoryMiB;
194
+ public AdapterLuid? Luid;
195
+ public PciAddress? Pci;
196
+ public uint? DxgiAdapterIndex;
197
+ public List<InventoryIssue> Issues = new List<InventoryIssue>();
198
+
199
+ public Adapter(string kind)
200
+ {
201
+ Kind = kind;
202
+ Name = "Windows " + kind.ToUpperInvariant();
203
+ }
204
+ }
205
+
206
+ public sealed class Inventory
207
+ {
208
+ public List<Adapter> Adapters = new List<Adapter>();
209
+ public List<InventoryIssue> Issues = new List<InventoryIssue>();
210
+ }
211
+
212
+ public static class WindowsInventory
213
+ {
214
+ private enum AdapterProperty : uint
215
+ {
216
+ InstanceLuid = 0,
217
+ DriverDescription = 2,
218
+ DedicatedAdapterMemory = 7,
219
+ IsHardware = 11,
220
+ IsIntegrated = 12
221
+ }
222
+
223
+ [StructLayout(LayoutKind.Sequential)]
224
+ private struct OpenAdapterFromLuid
225
+ {
226
+ public AdapterLuid Luid;
227
+ public uint Handle;
228
+ }
229
+
230
+ [StructLayout(LayoutKind.Sequential)]
231
+ private struct QueryAdapterInfo
232
+ {
233
+ public uint Handle;
234
+ public uint Type;
235
+ public IntPtr Data;
236
+ public uint DataLength;
237
+ }
238
+
239
+ [StructLayout(LayoutKind.Sequential)]
240
+ private struct CloseAdapter
241
+ {
242
+ public uint Handle;
243
+ }
244
+
245
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
246
+ private struct DxgiAdapterDescription
247
+ {
248
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
249
+ public string Description;
250
+ public uint VendorId;
251
+ public uint DeviceId;
252
+ public uint SubSystemId;
253
+ public uint Revision;
254
+ public UIntPtr DedicatedVideoMemory;
255
+ public UIntPtr DedicatedSystemMemory;
256
+ public UIntPtr SharedSystemMemory;
257
+ public AdapterLuid Luid;
258
+ }
259
+
260
+ [DllImport("dxcore.dll", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
261
+ private static extern int DXCoreCreateAdapterFactory(ref Guid iid, out IntPtr factory);
262
+
263
+ [DllImport("dxgi.dll", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
264
+ private static extern int CreateDXGIFactory(ref Guid iid, out IntPtr factory);
265
+
266
+ [DllImport("gdi32.dll", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
267
+ private static extern int D3DKMTOpenAdapterFromLuid(ref OpenAdapterFromLuid adapter);
268
+
269
+ [DllImport("gdi32.dll", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
270
+ private static extern int D3DKMTQueryAdapterInfo(ref QueryAdapterInfo query);
271
+
272
+ [DllImport("gdi32.dll", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
273
+ private static extern int D3DKMTCloseAdapter(ref CloseAdapter adapter);
274
+
275
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
276
+ private delegate int CreateAdapterList(IntPtr self, uint count, ref Guid attribute, ref Guid iid, out IntPtr list);
277
+
278
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
279
+ private delegate int GetAdapter(IntPtr self, uint index, ref Guid iid, out IntPtr adapter);
280
+
281
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
282
+ private delegate uint GetAdapterCount(IntPtr self);
283
+
284
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
285
+ [return: MarshalAs(UnmanagedType.I1)]
286
+ private delegate bool GetNativeBoolean(IntPtr self);
287
+
288
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
289
+ [return: MarshalAs(UnmanagedType.I1)]
290
+ private delegate bool IsPropertySupported(IntPtr self, AdapterProperty property);
291
+
292
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
293
+ private delegate int GetProperty(IntPtr self, AdapterProperty property, UIntPtr bufferLength, IntPtr data);
294
+
295
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
296
+ private delegate int GetPropertySize(IntPtr self, AdapterProperty property, out UIntPtr bufferLength);
297
+
298
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
299
+ private delegate int EnumerateDxgiAdapter(IntPtr self, uint index, out IntPtr adapter);
300
+
301
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
302
+ private delegate int GetDxgiDescription(IntPtr self, out DxgiAdapterDescription description);
303
+
304
+ public static Inventory Read()
305
+ {
306
+ Inventory inventory = new Inventory();
307
+ IntPtr factory = IntPtr.Zero;
308
+ try
309
+ {
310
+ Guid factoryId = new Guid("78ee5945-c36e-4b13-a669-005dd11c0f06");
311
+ int result = DXCoreCreateAdapterFactory(ref factoryId, out factory);
312
+ RequireComResult(result, factory);
313
+ Guid gpuAttribute = new Guid("b69eb219-3ded-4464-979f-a00bd4687006");
314
+ Guid npuAttribute = new Guid("d46140c4-add7-451b-9e56-06fe8c3b58ed");
315
+ ReadAdapterKind(factory, "gpu", gpuAttribute, inventory);
316
+ ReadAdapterKind(factory, "npu", npuAttribute, inventory);
317
+ }
318
+ catch (Exception error)
319
+ {
320
+ string code = IsApiUnavailable(error) ? "enumeration-unavailable" : "enumeration-failed";
321
+ inventory.Issues.Add(
322
+ new InventoryIssue("gpu", code, "DXCore factory: " + error.ToString())
323
+ );
324
+ inventory.Issues.Add(
325
+ new InventoryIssue("npu", code, "DXCore factory: " + error.ToString())
326
+ );
327
+ }
328
+ finally
329
+ {
330
+ ReleaseCom(factory, "identity", inventory.Issues);
331
+ }
332
+
333
+ if (inventory.Adapters.Count > 0)
334
+ {
335
+ ReadDxgiAddresses(inventory);
336
+ }
337
+ return inventory;
338
+ }
339
+
340
+ private static void ReadAdapterKind(IntPtr factory, string kind, Guid attribute, Inventory inventory)
341
+ {
342
+ IntPtr list = IntPtr.Zero;
343
+ try
344
+ {
345
+ Guid listId = new Guid("526c7776-40e9-459b-b711-f32ad76dfc28");
346
+ CreateAdapterList createList = GetMethod<CreateAdapterList>(factory, 3);
347
+ int result = createList(factory, 1, ref attribute, ref listId, out list);
348
+ RequireComResult(result, list);
349
+ GetAdapterCount getCount = GetMethod<GetAdapterCount>(list, 4);
350
+ GetAdapter getAdapter = GetMethod<GetAdapter>(list, 3);
351
+ uint count = getCount(list);
352
+ Guid adapterId = new Guid("f0db4c7f-fe5a-42a2-bd62-f2a6cf6fc83e");
353
+ for (uint index = 0; index < count; index++)
354
+ {
355
+ IntPtr pointer = IntPtr.Zero;
356
+ Adapter adapter = null;
357
+ try
358
+ {
359
+ result = getAdapter(list, index, ref adapterId, out pointer);
360
+ RequireComResult(result, pointer);
361
+ adapter = new Adapter(kind);
362
+ inventory.Adapters.Add(adapter);
363
+ GetNativeBoolean isValid = GetMethod<GetNativeBoolean>(pointer, 3);
364
+ adapter.Present = isValid(pointer);
365
+ ReadProperties(pointer, adapter);
366
+ if (adapter.Luid.HasValue)
367
+ {
368
+ ReadPciAddress(adapter);
369
+ }
370
+ if (!isValid(pointer))
371
+ {
372
+ adapter.Present = false;
373
+ adapter.Issues.Add(
374
+ new InventoryIssue(kind, "enumeration-failed", "The DXCore adapter became invalid during discovery.")
375
+ );
376
+ }
377
+ }
378
+ catch (Exception error)
379
+ {
380
+ List<InventoryIssue> issues = adapter == null ? inventory.Issues : adapter.Issues;
381
+ issues.Add(
382
+ new InventoryIssue(kind, "enumeration-failed", "DXCore adapter " + index + ": " + error.ToString())
383
+ );
384
+ }
385
+ finally
386
+ {
387
+ ReleaseCom(pointer, kind, adapter == null ? inventory.Issues : adapter.Issues);
388
+ }
389
+ }
390
+ GetNativeBoolean isStale = GetMethod<GetNativeBoolean>(list, 5);
391
+ if (isStale(list))
392
+ {
393
+ inventory.Issues.Add(
394
+ new InventoryIssue(kind, "enumeration-failed", "The DXCore adapter list changed during discovery; refresh the inventory.")
395
+ );
396
+ }
397
+ }
398
+ catch (Exception error)
399
+ {
400
+ string code = IsApiUnavailable(error) ? "enumeration-unavailable" : "enumeration-failed";
401
+ inventory.Issues.Add(
402
+ new InventoryIssue(kind, code, "DXCore " + kind + " enumeration: " + error.ToString())
403
+ );
404
+ }
405
+ finally
406
+ {
407
+ ReleaseCom(list, kind, inventory.Issues);
408
+ }
409
+ }
410
+
411
+ private static void ReadProperties(IntPtr pointer, Adapter adapter)
412
+ {
413
+ IsPropertySupported isSupported = GetMethod<IsPropertySupported>(pointer, 5);
414
+ GetProperty getProperty = GetMethod<GetProperty>(pointer, 6);
415
+ GetPropertySize getPropertySize = GetMethod<GetPropertySize>(pointer, 7);
416
+ AdapterProperty[] properties =
417
+ {
418
+ AdapterProperty.InstanceLuid,
419
+ AdapterProperty.DriverDescription,
420
+ AdapterProperty.IsHardware,
421
+ AdapterProperty.IsIntegrated,
422
+ AdapterProperty.DedicatedAdapterMemory
423
+ };
424
+ foreach (AdapterProperty property in properties)
425
+ {
426
+ IntPtr data = IntPtr.Zero;
427
+ try
428
+ {
429
+ if (!isSupported(pointer, property))
430
+ {
431
+ adapter.Issues.Add(
432
+ new InventoryIssue(adapter.Kind, "property-unavailable", "DXCore does not expose " + property + " for this adapter.")
433
+ );
434
+ continue;
435
+ }
436
+ UIntPtr nativeLength;
437
+ int result = getPropertySize(pointer, property, out nativeLength);
438
+ Marshal.ThrowExceptionForHR(result);
439
+ int length = checked((int)nativeLength.ToUInt64());
440
+ int requiredLength = property == AdapterProperty.DriverDescription
441
+ || property == AdapterProperty.IsHardware
442
+ || property == AdapterProperty.IsIntegrated ? 1 : 8;
443
+ if (length < requiredLength)
444
+ {
445
+ throw new InvalidOperationException("DXCore returned an incomplete native " + property + " representation.");
446
+ }
447
+ data = Marshal.AllocHGlobal(length);
448
+ result = getProperty(pointer, property, nativeLength, data);
449
+ Marshal.ThrowExceptionForHR(result);
450
+ switch (property)
451
+ {
452
+ case AdapterProperty.InstanceLuid:
453
+ adapter.Luid = (AdapterLuid)Marshal.PtrToStructure(data, typeof(AdapterLuid));
454
+ break;
455
+ case AdapterProperty.DriverDescription:
456
+ byte[] description = new byte[length];
457
+ Marshal.Copy(data, description, 0, length);
458
+ int terminator = Array.IndexOf(description, (byte)0);
459
+ if (terminator < 0)
460
+ {
461
+ throw new InvalidOperationException("DXCore returned a driver description without its required null terminator.");
462
+ }
463
+ adapter.Name = Encoding.UTF8.GetString(description, 0, terminator);
464
+ break;
465
+ case AdapterProperty.IsHardware:
466
+ adapter.IsHardware = Marshal.ReadByte(data) != 0;
467
+ break;
468
+ case AdapterProperty.IsIntegrated:
469
+ adapter.IsIntegrated = Marshal.ReadByte(data) != 0;
470
+ break;
471
+ case AdapterProperty.DedicatedAdapterMemory:
472
+ adapter.DedicatedMemoryMiB = unchecked((ulong)Marshal.ReadInt64(data)) / 1048576.0;
473
+ break;
474
+ }
475
+ }
476
+ catch (Exception error)
477
+ {
478
+ adapter.Issues.Add(
479
+ new InventoryIssue(adapter.Kind, "property-failed", "DXCore " + property + ": " + error.ToString())
480
+ );
481
+ }
482
+ finally
483
+ {
484
+ FreeBuffer(data, adapter.Kind, adapter.Issues);
485
+ }
486
+ }
487
+ }
488
+
489
+ private static void ReadPciAddress(Adapter adapter)
490
+ {
491
+ OpenAdapterFromLuid opened = new OpenAdapterFromLuid();
492
+ opened.Luid = adapter.Luid.Value;
493
+ IntPtr data = IntPtr.Zero;
494
+ bool ownsHandle = false;
495
+ try
496
+ {
497
+ int status = D3DKMTOpenAdapterFromLuid(ref opened);
498
+ RequireNtSuccess(status, "D3DKMTOpenAdapterFromLuid");
499
+ ownsHandle = true;
500
+ int length = Marshal.SizeOf(typeof(PciAddress));
501
+ data = Marshal.AllocHGlobal(length);
502
+ QueryAdapterInfo query = new QueryAdapterInfo();
503
+ query.Handle = opened.Handle;
504
+ query.Type = 6; // KMTQAITYPE_ADAPTERADDRESS
505
+ query.Data = data;
506
+ query.DataLength = (uint)length;
507
+ status = D3DKMTQueryAdapterInfo(ref query);
508
+ RequireNtSuccess(status, "D3DKMTQueryAdapterInfo(KMTQAITYPE_ADAPTERADDRESS)");
509
+ adapter.Pci = (PciAddress)Marshal.PtrToStructure(data, typeof(PciAddress));
510
+ }
511
+ catch (Exception error)
512
+ {
513
+ string code = IsApiUnavailable(error) ? "property-unavailable" : "property-failed";
514
+ adapter.Issues.Add(
515
+ new InventoryIssue("identity", code, "LUID to PCI address: " + error.ToString())
516
+ );
517
+ }
518
+ finally
519
+ {
520
+ FreeBuffer(data, "identity", adapter.Issues);
521
+ if (ownsHandle)
522
+ {
523
+ try
524
+ {
525
+ CloseAdapter closed = new CloseAdapter();
526
+ closed.Handle = opened.Handle;
527
+ int status = D3DKMTCloseAdapter(ref closed);
528
+ RequireNtSuccess(status, "D3DKMTCloseAdapter");
529
+ }
530
+ catch (Exception error)
531
+ {
532
+ adapter.Issues.Add(
533
+ new InventoryIssue("identity", "cleanup-failed", error.ToString())
534
+ );
535
+ }
536
+ }
537
+ }
538
+ }
539
+
540
+ private static void ReadDxgiAddresses(Inventory inventory)
541
+ {
542
+ IntPtr factory = IntPtr.Zero;
543
+ List<AdapterLuid> luids = new List<AdapterLuid>();
544
+ List<uint> indexes = new List<uint>();
545
+ try
546
+ {
547
+ Guid factoryId = new Guid("7b7166ec-21c7-44ae-b21a-c9ae321ae369");
548
+ int factoryResult = CreateDXGIFactory(ref factoryId, out factory);
549
+ RequireComResult(factoryResult, factory);
550
+ EnumerateDxgiAdapter enumerate = GetMethod<EnumerateDxgiAdapter>(factory, 7);
551
+ for (uint index = 0; ; index++)
552
+ {
553
+ IntPtr pointer = IntPtr.Zero;
554
+ bool enumerated = false;
555
+ try
556
+ {
557
+ int result = enumerate(factory, index, out pointer);
558
+ if (result == unchecked((int)0x887A0002)) // DXGI_ERROR_NOT_FOUND
559
+ {
560
+ break;
561
+ }
562
+ RequireComResult(result, pointer);
563
+ enumerated = true;
564
+ GetDxgiDescription getDescription = GetMethod<GetDxgiDescription>(pointer, 8);
565
+ DxgiAdapterDescription description;
566
+ int descriptionResult = getDescription(pointer, out description);
567
+ Marshal.ThrowExceptionForHR(descriptionResult);
568
+ luids.Add(description.Luid);
569
+ indexes.Add(index);
570
+ }
571
+ catch (Exception error)
572
+ {
573
+ string code = enumerated ? "property-failed" : "enumeration-failed";
574
+ inventory.Issues.Add(
575
+ new InventoryIssue("dxgi", code, "DXGI adapter " + index + ": " + error.ToString())
576
+ );
577
+ if (!enumerated)
578
+ {
579
+ // Only description failures leave later indices queryable.
580
+ break;
581
+ }
582
+ }
583
+ finally
584
+ {
585
+ ReleaseCom(pointer, "dxgi", inventory.Issues);
586
+ }
587
+ }
588
+ }
589
+ catch (Exception error)
590
+ {
591
+ string code = IsApiUnavailable(error) ? "enumeration-unavailable" : "enumeration-failed";
592
+ inventory.Issues.Add(
593
+ new InventoryIssue("dxgi", code, "DXGI enumeration: " + error.ToString())
594
+ );
595
+ }
596
+ finally
597
+ {
598
+ ReleaseCom(factory, "dxgi", inventory.Issues);
599
+ }
600
+
601
+ foreach (Adapter adapter in inventory.Adapters)
602
+ {
603
+ if (!adapter.Luid.HasValue)
604
+ {
605
+ continue;
606
+ }
607
+ int matches = 0;
608
+ uint matchedIndex = 0;
609
+ for (int index = 0; index < luids.Count; index++)
610
+ {
611
+ if (SameLuid(adapter.Luid.Value, luids[index]))
612
+ {
613
+ matches++;
614
+ matchedIndex = indexes[index];
615
+ }
616
+ }
617
+ if (matches == 1)
618
+ {
619
+ adapter.DxgiAdapterIndex = matchedIndex;
620
+ }
621
+ else
622
+ {
623
+ string message = matches == 0
624
+ ? "No DXGI adapter index was observed for this DXCore LUID."
625
+ : "More than one DXGI adapter index was observed for this DXCore LUID.";
626
+ adapter.Issues.Add(
627
+ new InventoryIssue("dxgi", "property-unavailable", message)
628
+ );
629
+ }
630
+ }
631
+ }
632
+
633
+ private static bool SameLuid(AdapterLuid left, AdapterLuid right)
634
+ {
635
+ return left.LowPart == right.LowPart && left.HighPart == right.HighPart;
636
+ }
637
+
638
+ private static T GetMethod<T>(IntPtr instance, int slot) where T : class
639
+ {
640
+ IntPtr table = Marshal.ReadIntPtr(instance);
641
+ IntPtr method = Marshal.ReadIntPtr(table, slot * IntPtr.Size);
642
+ return (T)(object)Marshal.GetDelegateForFunctionPointer(method, typeof(T));
643
+ }
644
+
645
+ private static void RequireComResult(int result, IntPtr instance)
646
+ {
647
+ Marshal.ThrowExceptionForHR(result);
648
+ if (instance == IntPtr.Zero)
649
+ {
650
+ throw new InvalidOperationException("The Windows COM operation succeeded without returning an interface.");
651
+ }
652
+ }
653
+
654
+ private static void RequireNtSuccess(int status, string operation)
655
+ {
656
+ if (status < 0)
657
+ {
658
+ throw new InvalidOperationException(operation + " returned NTSTATUS 0x" + unchecked((uint)status).ToString("X8") + ".");
659
+ }
660
+ }
661
+
662
+ private static bool IsApiUnavailable(Exception error)
663
+ {
664
+ return error is DllNotFoundException
665
+ || error is EntryPointNotFoundException
666
+ || error is BadImageFormatException
667
+ || error is PlatformNotSupportedException;
668
+ }
669
+
670
+ private static void ReleaseCom(IntPtr instance, string source, List<InventoryIssue> issues)
671
+ {
672
+ if (instance == IntPtr.Zero)
673
+ {
674
+ return;
675
+ }
676
+ try
677
+ {
678
+ Marshal.Release(instance);
679
+ }
680
+ catch (Exception error)
681
+ {
682
+ issues.Add(
683
+ new InventoryIssue(source, "cleanup-failed", error.ToString())
684
+ );
685
+ }
686
+ }
687
+
688
+ private static void FreeBuffer(IntPtr data, string source, List<InventoryIssue> issues)
689
+ {
690
+ if (data == IntPtr.Zero)
691
+ {
692
+ return;
693
+ }
694
+ try
695
+ {
696
+ Marshal.FreeHGlobal(data);
697
+ }
698
+ catch (Exception error)
699
+ {
700
+ issues.Add(
701
+ new InventoryIssue(source, "cleanup-failed", error.ToString())
702
+ );
703
+ }
704
+ }
705
+ }
706
+ }
707
+ '@
708
+
709
+ function Add-NativeAdapters {
710
+ try {
711
+ # Collect all compiler diagnostics as data; none may contaminate stdout.
712
+ $compilerFailed = $false
713
+ $compilerOutput = @(Add-Type -TypeDefinition $nativeSource -Language CSharp -ErrorAction Continue -WarningAction Continue 2>&1 3>&1)
714
+ foreach ($diagnostic in $compilerOutput) {
715
+ if ($diagnostic -is [System.Management.Automation.ErrorRecord]) {
716
+ $compilerFailed = $true
717
+ $message = Get-InventoryErrorText $diagnostic
718
+ } else {
719
+ $message = $diagnostic.ToString()
720
+ }
721
+ Add-InventoryIssue 'identity' 'interop-diagnostic' $message
722
+ }
723
+ if ($compilerFailed) {
724
+ Add-InventoryIssue 'gpu' 'enumeration-unavailable' 'The Windows inventory interop could not be compiled; complete diagnostics are included in issues.'
725
+ Add-InventoryIssue 'npu' 'enumeration-unavailable' 'The Windows inventory interop could not be compiled; complete diagnostics are included in issues.'
726
+ return
727
+ }
728
+ $nativeInventory = [Arcane.ExecutionDevices.WindowsInventory]::Read()
729
+ foreach ($issue in $nativeInventory.Issues) {
730
+ Add-InventoryIssue $issue.Source $issue.Code $issue.Message
731
+ }
732
+ $pnpDevices = Get-PresentPciDevices
733
+ $deviceRows = [System.Collections.Generic.List[object]]::new()
734
+ foreach ($adapter in $nativeInventory.Adapters) {
735
+ $deviceId = $null
736
+ $identitySource = $null
737
+ $luid = $null
738
+ $pci = $null
739
+ if ($null -ne $adapter.Luid) {
740
+ $luid = [ordered]@{
741
+ highPart = $adapter.Luid.HighPart
742
+ lowPart = $adapter.Luid.LowPart
743
+ }
744
+ }
745
+ if ($null -ne $adapter.Pci) {
746
+ $pci = [ordered]@{
747
+ bus = $adapter.Pci.Bus
748
+ device = $adapter.Pci.Device
749
+ function = $adapter.Pci.Function
750
+ }
751
+ $matchedDevices = [System.Collections.Generic.List[object]]::new()
752
+ foreach ($candidate in $pnpDevices.Values) {
753
+ if ($null -ne $candidate.bus -and $null -ne $candidate.address -and
754
+ $candidate.bus -eq $pci.bus -and
755
+ (($candidate.address -shr 16) -band 65535) -eq $pci.device -and
756
+ ($candidate.address -band 65535) -eq $pci.function) {
757
+ $matchedDevices.Add($candidate)
758
+ }
759
+ }
760
+ if ($matchedDevices.Count -eq 1 -and $adapter.Present) {
761
+ $deviceId = $matchedDevices[0].instanceId
762
+ $identitySource = 'windows-pnp-instance'
763
+ } elseif ($matchedDevices.Count -gt 1) {
764
+ Add-InventoryIssue 'identity' 'identity-ambiguous' 'Multiple present PnP instances share this adapter PCI address.'
765
+ }
766
+ }
767
+ $row = [ordered]@{
768
+ deviceId = $deviceId
769
+ identitySource = $identitySource
770
+ kind = $adapter.Kind
771
+ name = $adapter.Name
772
+ present = $adapter.Present
773
+ isHardware = $adapter.IsHardware
774
+ isIntegrated = $adapter.IsIntegrated
775
+ dedicatedMemoryMiB = $adapter.DedicatedMemoryMiB
776
+ addresses = [ordered]@{
777
+ adapterLuid = $luid
778
+ pci = $pci
779
+ dxgiAdapterIndex = $adapter.DxgiAdapterIndex
780
+ }
781
+ processors = @()
782
+ }
783
+ $deviceRows.Add([ordered]@{row = $row; issues = $adapter.Issues})
784
+ }
785
+ # Both sides of the join must be unique. A single PnP instance cannot
786
+ # establish distinct physical identities for multiple DXCore records.
787
+ $rowsById = @{}
788
+ foreach ($entry in $deviceRows) {
789
+ $deviceId = $entry.row.deviceId
790
+ if ($null -ne $deviceId) {
791
+ if (!$rowsById.ContainsKey($deviceId)) {
792
+ $rowsById[$deviceId] = [System.Collections.Generic.List[object]]::new()
793
+ }
794
+ $rowsById[$deviceId].Add($entry.row)
795
+ }
796
+ }
797
+ foreach ($rows in $rowsById.Values) {
798
+ if ($rows.Count -gt 1) {
799
+ foreach ($row in $rows) {
800
+ $row.deviceId = $null
801
+ $row.identitySource = $null
802
+ }
803
+ Add-InventoryIssue 'identity' 'identity-ambiguous' 'Multiple DXCore adapters correlate to the same present PnP instance.'
804
+ }
805
+ }
806
+ foreach ($entry in $deviceRows) {
807
+ $row = $entry.row
808
+ if ($null -eq $row.deviceId) {
809
+ Add-InventoryIssue 'identity' 'identity-unresolved' ("No unique present PnP instance could be correlated to the Windows {0} '{1}'." -f $row.kind, $row.name)
810
+ }
811
+ foreach ($issue in $entry.issues) {
812
+ Add-InventoryIssue $issue.Source $issue.Code $issue.Message $row.deviceId
813
+ }
814
+ $inventoryDevices.Add($row)
815
+ }
816
+ } catch {
817
+ $message = Get-InventoryErrorText $_
818
+ Add-InventoryIssue 'gpu' 'enumeration-failed' $message
819
+ Add-InventoryIssue 'npu' 'enumeration-failed' $message
820
+ }
821
+ }
822
+
823
+ Add-HostCpu
824
+ Add-NativeAdapters
825
+
826
+ $inventoryState = if ($inventoryDevices.Count -eq 0) { 'unavailable' } elseif ($inventoryIssues.Count -gt 0) { 'partial' } else { 'ready' }
827
+ $inventory = [ordered]@{
828
+ platform = 'win32'
829
+ state = $inventoryState
830
+ devices = $inventoryDevices.ToArray()
831
+ issues = $inventoryIssues.ToArray()
832
+ }
833
+ [Console]::Out.WriteLine((ConvertTo-Json -InputObject $inventory -Depth 12 -Compress))