win-wmi-fixed.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. /*
  2. Copyright 2021 Intel Corporation
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. var promise = require('promise');
  14. var GM = require('_GenericMarshal');
  15. const CLSID_WbemAdministrativeLocator = '{CB8555CC-9128-11D1-AD9B-00C04FD8FDFF}';
  16. const IID_WbemLocator = '{dc12a687-737f-11cf-884d-00aa004b2e24}';
  17. const WBEM_FLAG_BIDIRECTIONAL = 0;
  18. const WBEM_INFINITE = -1;
  19. const WBEM_FLAG_ALWAYS = 0;
  20. const E_NOINTERFACE = 0x80004002;
  21. var OleAut32 = GM.CreateNativeProxy('OleAut32.dll');
  22. OleAut32.CreateMethod('SafeArrayAccessData');
  23. var wmi_handlers = {};
  24. const LocatorFunctions = ['QueryInterface', 'AddRef', 'Release', 'ConnectToServer'];
  25. //
  26. // Reference for IWbemServices can be found at:
  27. // https://learn.microsoft.com/en-us/windows/win32/api/wbemcli/nn-wbemcli-iwbemservices
  28. //
  29. const ServiceFunctions = [
  30. 'QueryInterface',
  31. 'AddRef',
  32. 'Release',
  33. 'OpenNamespace',
  34. 'CancelAsyncCall',
  35. 'QueryObjectSink',
  36. 'GetObject',
  37. 'GetObjectAsync',
  38. 'PutClass',
  39. 'PutClassAsync',
  40. 'DeleteClass',
  41. 'DeleteClassAsync',
  42. 'CreateClassEnum',
  43. 'CreateClassEnumAsync',
  44. 'PutInstance',
  45. 'PutInstanceAsync',
  46. 'DeleteInstance',
  47. 'DeleteInstanceAsync',
  48. 'CreateInstanceEnum',
  49. 'CreateInstanceEnumAsync',
  50. 'ExecQuery',
  51. 'ExecQueryAsync',
  52. 'ExecNotificationQuery',
  53. 'ExecNotificationQueryAsync',
  54. 'ExecMethod',
  55. 'ExecMethodAsync'
  56. ];
  57. //
  58. // Reference to IEnumWbemClassObject can be found at:
  59. // https://learn.microsoft.com/en-us/windows/win32/api/wbemcli/nn-wbemcli-ienumwbemclassobject
  60. //
  61. const ResultsFunctions = [
  62. 'QueryInterface',
  63. 'AddRef',
  64. 'Release',
  65. 'Reset',
  66. 'Next',
  67. 'NextAsync',
  68. 'Clone',
  69. 'Skip'
  70. ];
  71. //
  72. // Reference to IWbemClassObject can be found at:
  73. // https://learn.microsoft.com/en-us/windows/win32/api/wbemcli/nn-wbemcli-iwbemclassobject
  74. //
  75. const ResultFunctions = [
  76. 'QueryInterface',
  77. 'AddRef',
  78. 'Release',
  79. 'GetQualifierSet',
  80. 'Get',
  81. 'Put',
  82. 'Delete',
  83. 'GetNames',
  84. 'BeginEnumeration',
  85. 'Next',
  86. 'EndEnumeration',
  87. 'GetPropertyQualifierSet',
  88. 'Clone',
  89. 'GetObjectText',
  90. 'SpawnDerivedClass',
  91. 'SpawnInstance',
  92. 'CompareTo',
  93. 'GetPropertyOrigin',
  94. 'InheritsFrom',
  95. 'GetMethod',
  96. 'PutMethod',
  97. 'DeleteMethod',
  98. 'BeginMethodEnumeration',
  99. 'NextMethod',
  100. 'EndMethodEnumeration',
  101. 'GetMethodQualifierSet',
  102. 'GetMethodOrigin'
  103. ];
  104. //
  105. // Reference to IWbemObjectSink can be found at:
  106. // https://learn.microsoft.com/en-us/windows/win32/wmisdk/iwbemobjectsink
  107. //
  108. const QueryAsyncHandler =
  109. [
  110. {
  111. cx: 10, parms: 3, name: 'QueryInterface', func: function (j, riid, ppv)
  112. {
  113. var ret = GM.CreateVariable(4);
  114. console.info1('QueryInterface', riid.Deref(0, 16).toBuffer().toString('hex'));
  115. switch (riid.Deref(0, 16).toBuffer().toString('hex'))
  116. {
  117. case '0000000000000000C000000000000046': // IID_IUnknown
  118. j.pointerBuffer().copy(ppv.Deref(0, GM.PointerSize).toBuffer());
  119. ret.increment(0, true);
  120. //++this.p.refcount;
  121. console.info1('QueryInterface (IID_IUnknown)', this.refcount);
  122. break;
  123. case '0178857C8173CF11884D00AA004B2E24': // IID_IWmiObjectSink
  124. j.pointerBuffer().copy(ppv.Deref(0, GM.PointerSize).toBuffer());
  125. ret.increment(0, true);
  126. //++this.p.refcount;
  127. console.info1('QueryInterface (IID_IWmiObjectSink)', this.refcount);
  128. break;
  129. default:
  130. ret.increment(E_NOINTERFACE, true);
  131. console.info1(riid.Deref(0, 16).toBuffer().toString('hex'), 'returning E_NOINTERFACE');
  132. break;
  133. }
  134. return (ret);
  135. }
  136. },
  137. {
  138. cx: 11, parms: 1, name: 'AddRef', func: function ()
  139. {
  140. ++this.refcount;
  141. console.info1('AddRef', this.refcount);
  142. return (GM.CreateVariable(4));
  143. }
  144. },
  145. {
  146. cx: 12, parms: 1, name: 'Release', func: function ()
  147. {
  148. --this.refcount;
  149. console.info1('Release', this.refcount);
  150. if (this.refcount == 0)
  151. {
  152. console.info1('No More References');
  153. this.cleanup();
  154. this.services.funcs.Release(this.services.Deref());
  155. this.services = null;
  156. this.p = null;
  157. if (this.callbackDispatched)
  158. {
  159. setImmediate(function (j) { j.locator = null; }, this);
  160. }
  161. else
  162. {
  163. this.locator = null;
  164. }
  165. console.info1('No More References [END]');
  166. }
  167. return (GM.CreateVariable(4));
  168. }
  169. },
  170. {
  171. cx: 13, parms: 3, name: 'Indicate', func: function (j, count, arr)
  172. {
  173. console.info1('Indicate', count.Val);
  174. var j, nme, len, nn;
  175. for (var i = 0; i < count.Val; ++i)
  176. {
  177. j = arr.Deref((i * GM.PointerSize) + 0, GM.PointerSize);
  178. this.results.push(enumerateProperties(j, this.fields));
  179. }
  180. var ret = GM.CreateVariable(4);
  181. ret.increment(0, true);
  182. return (ret);
  183. }
  184. },
  185. {
  186. cx: 14, parms: 5, name: 'SetStatus', func: function (j, lFlags, hResult, strParam, pObjParam)
  187. {
  188. console.info1('SetStatus', hResult.Val);
  189. var ret = GM.CreateVariable(4);
  190. ret.increment(0, true);
  191. if (hResult.Val == 0)
  192. {
  193. this.p.resolve(this.results);
  194. }
  195. else
  196. {
  197. this.p.reject(hResult.Val);
  198. }
  199. return (ret);
  200. }
  201. }
  202. ];
  203. function enumerateProperties(j, fields)
  204. {
  205. //
  206. // Reference to SafeArrayAccessData() can be found at:
  207. // https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearrayaccessdata
  208. //
  209. var nme, len, nn;
  210. var properties = [];
  211. var values = {};
  212. j.funcs = require('win-com').marshalFunctions(j.Deref(), ResultFunctions);
  213. // First we need to enumerate the COM Array
  214. if (fields != null && Array.isArray(fields))
  215. {
  216. properties = fields;
  217. }
  218. else
  219. {
  220. nme = GM.CreatePointer();
  221. j.funcs.GetNames(j.Deref(), 0, WBEM_FLAG_ALWAYS, 0, nme);
  222. len = nme.Deref().Deref(GM.PointerSize == 8 ? 24 : 16, 4).toBuffer().readUInt32LE();
  223. nn = GM.CreatePointer();
  224. OleAut32.SafeArrayAccessData(nme.Deref(), nn);
  225. for (var i = 0; i < len; ++i)
  226. {
  227. var propName = nn.Deref().increment(i * GM.PointerSize).Deref().Wide2UTF8;
  228. if (propName.length === 0) { continue; }
  229. properties.push(propName);
  230. }
  231. }
  232. // Now we need to introspect the Array Fields
  233. for (var i = 0; i < properties.length; ++i)
  234. {
  235. var tmp1 = GM.CreateVariable(24);
  236. if (j.funcs.Get(j.Deref(), GM.CreateVariable(properties[i], { wide: true }), 0, tmp1, 0, 0).Val == 0)
  237. {
  238. //
  239. // Reference for IWbemClassObject::Get() can be found at:
  240. // https://learn.microsoft.com/en-us/windows/win32/api/wbemcli/nf-wbemcli-iwbemclassobject-get
  241. //
  242. var vartype = tmp1.toBuffer().readUInt16LE();
  243. var isArray = (vartype & 0x2000) != 0; // VT_ARRAY flag
  244. var baseType = vartype & 0x0FFF;
  245. if (isArray)
  246. {
  247. // Handle array types (VT_ARRAY | base type)
  248. var safeArray = tmp1.Deref(8, GM.PointerSize).Deref();
  249. var arrayLength = safeArray.Deref(GM.PointerSize == 8 ? 24 : 16, 4).toBuffer().readUInt32LE();
  250. var arrayData = GM.CreatePointer();
  251. OleAut32.SafeArrayAccessData(safeArray, arrayData);
  252. var arrayValues = [];
  253. for (var k = 0; k < arrayLength; ++k)
  254. {
  255. switch (baseType)
  256. {
  257. case 0x0002: // VT_I2
  258. arrayValues.push(arrayData.Deref().Deref(k * 2, 2).toBuffer().readInt16LE());
  259. break;
  260. case 0x0003: // VT_I4
  261. case 0x0016: // VT_INT
  262. arrayValues.push(arrayData.Deref().Deref(k * 4, 4).toBuffer().readInt32LE());
  263. break;
  264. case 0x000B: // VT_BOOL
  265. arrayValues.push(arrayData.Deref().Deref(k * 2, 2).toBuffer().readInt16LE() != 0);
  266. break;
  267. case 0x0010: // VT_I1
  268. arrayValues.push(arrayData.Deref().Deref(k, 1).toBuffer().readInt8());
  269. break;
  270. case 0x0011: // VT_UI1
  271. arrayValues.push(arrayData.Deref().Deref(k, 1).toBuffer().readUInt8());
  272. break;
  273. case 0x0012: // VT_UI2
  274. arrayValues.push(arrayData.Deref().Deref(k * 2, 2).toBuffer().readUInt16LE());
  275. break;
  276. case 0x0013: // VT_UI4
  277. case 0x0017: // VT_UINT
  278. arrayValues.push(arrayData.Deref().Deref(k * 4, 4).toBuffer().readUInt32LE());
  279. break;
  280. case 0x0008: // VT_BSTR
  281. arrayValues.push(arrayData.Deref().Deref(k * GM.PointerSize, GM.PointerSize).Deref().Wide2UTF8);
  282. break;
  283. }
  284. }
  285. values[properties[i]] = arrayValues;
  286. }
  287. else
  288. {
  289. // Handle scalar types
  290. switch (vartype)
  291. {
  292. case 0x0000: // VT_EMPTY
  293. case 0x0001: // VT_NULL
  294. values[properties[i]] = null;
  295. break;
  296. case 0x0002: // VT_I2
  297. values[properties[i]] = tmp1.Deref(8, GM.PointerSize).toBuffer().readInt16LE();
  298. break;
  299. case 0x0003: // VT_I4
  300. case 0x0016: // VT_INT
  301. values[properties[i]] = tmp1.Deref(8, GM.PointerSize).toBuffer().readInt32LE();
  302. break;
  303. case 0x000B: // VT_BOOL
  304. values[properties[i]] = tmp1.Deref(8, GM.PointerSize).toBuffer().readInt32LE() != 0;
  305. break;
  306. case 0x000E: // VT_DECIMAL
  307. break;
  308. case 0x0010: // VT_I1
  309. values[properties[i]] = tmp1.Deref(8, GM.PointerSize).toBuffer().readInt8();
  310. break;
  311. case 0x0011: // VT_UI1
  312. values[properties[i]] = tmp1.Deref(8, GM.PointerSize).toBuffer().readUInt8();
  313. break;
  314. case 0x0012: // VT_UI2
  315. values[properties[i]] = tmp1.Deref(8, GM.PointerSize).toBuffer().readUInt16LE();
  316. break;
  317. case 0x0013: // VT_UI4
  318. case 0x0017: // VT_UINT
  319. values[properties[i]] = tmp1.Deref(8, GM.PointerSize).toBuffer().readUInt32LE();
  320. break;
  321. //case 0x0014: // VT_I8
  322. // break;
  323. //case 0x0015: // VT_UI8
  324. // break;
  325. case 0x0008: // VT_BSTR
  326. values[properties[i]] = tmp1.Deref(8, GM.PointerSize).Deref().Wide2UTF8;
  327. break;
  328. default:
  329. console.info1('VARTYPE: ' + vartype);
  330. break;
  331. }
  332. }
  333. }
  334. }
  335. return (values);
  336. }
  337. function queryAsync(resourceString, queryString, fields)
  338. {
  339. var p = new promise(require('promise').defaultInit);
  340. var resource = GM.CreateVariable(resourceString, { wide: true });
  341. var language = GM.CreateVariable("WQL", { wide: true });
  342. var query = GM.CreateVariable(queryString, { wide: true });
  343. var results = GM.CreatePointer();
  344. // Setup the Async COM handler for QueryAsync()
  345. var handlers = require('win-com').marshalInterface(QueryAsyncHandler);
  346. handlers.refcount = 1;
  347. handlers.results = [];
  348. handlers.fields = fields;
  349. handlers.locator = require('win-com').createInstance(require('win-com').CLSIDFromString(CLSID_WbemAdministrativeLocator), require('win-com').IID_IUnknown);
  350. handlers.locator.funcs = require('win-com').marshalFunctions(handlers.locator, LocatorFunctions);
  351. handlers.services = require('_GenericMarshal').CreatePointer();
  352. if (handlers.locator.funcs.ConnectToServer(handlers.locator, resource, 0, 0, 0, 0, 0, 0, handlers.services).Val != 0) { throw ('Error calling ConnectToService'); }
  353. handlers.services.funcs = require('win-com').marshalFunctions(handlers.services.Deref(), ServiceFunctions);
  354. handlers.p = p;
  355. // Make the COM call
  356. if (handlers.services.funcs.ExecQueryAsync(handlers.services.Deref(), language, query, WBEM_FLAG_BIDIRECTIONAL, 0, handlers).Val != 0)
  357. {
  358. throw ('Error in Query');
  359. }
  360. // Hold a reference to the callback object
  361. wmi_handlers[handlers._hashCode()] = handlers;
  362. return (p);
  363. }
  364. function query(resourceString, queryString, fields)
  365. {
  366. var resource = GM.CreateVariable(resourceString, { wide: true });
  367. var language = GM.CreateVariable("WQL", { wide: true });
  368. var query = GM.CreateVariable(queryString, { wide: true });
  369. var results = GM.CreatePointer();
  370. // Connect the locator connection for WMI
  371. var locator = require('win-com').createInstance(require('win-com').CLSIDFromString(CLSID_WbemAdministrativeLocator), require('win-com').IID_IUnknown);
  372. locator.funcs = require('win-com').marshalFunctions(locator, LocatorFunctions);
  373. var services = require('_GenericMarshal').CreatePointer();
  374. if (locator.funcs.ConnectToServer(locator, resource, 0, 0, 0, 0, 0, 0, services).Val != 0) { throw ('Error calling ConnectToService'); }
  375. // Execute the Query
  376. services.funcs = require('win-com').marshalFunctions(services.Deref(), ServiceFunctions);
  377. if (services.funcs.ExecQuery(services.Deref(), language, query, WBEM_FLAG_BIDIRECTIONAL, 0, results).Val != 0) { throw ('Error in Query'); }
  378. results.funcs = require('win-com').marshalFunctions(results.Deref(), ResultsFunctions);
  379. var returnedCount = GM.CreateVariable(8);
  380. var result = GM.CreatePointer();
  381. var ret = [];
  382. // Enumerate the results
  383. while (results.funcs.Next(results.Deref(), WBEM_INFINITE, 1, result, returnedCount).Val == 0)
  384. {
  385. ret.push(enumerateProperties(result, fields));
  386. }
  387. results.funcs.Release(results.Deref());
  388. services.funcs.Release(services.Deref());
  389. locator.funcs.Release(locator);
  390. return (ret);
  391. }
  392. module.exports = { query: query, queryAsync: queryAsync };