KiCad PCB EDA Suite
Loading...
Searching...
No Matches
windows/sysinfo.cpp
Go to the documentation of this file.
1/*
2* This program source code file is part of KiCad, a free EDA CAD application.
3*
4* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
5*
6* This program is free software; you can redistribute it and/or
7* modify it under the terms of the GNU General Public License
8* as published by the Free Software Foundation; either version 2
9* of the License, or (at your option) any later version.
10*
11* This program is distributed in the hope that it will be useful,
12* but WITHOUT ANY WARRANTY; without even the implied warranty of
13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14* GNU General Public License for more details.
15*
16* You should have received a copy of the GNU General Public License
17* along with this program; if not, you may find one here:
18* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19* or you may search the http://www.gnu.org website for the version 2 license,
20* or you may write to the Free Software Foundation, Inc.,
21* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22*/
23
24#include <Wbemidl.h>
25#include <cstdio>
26#define _WIN32_DCOM
27#include <comdef.h>
28
29
30#include <stdexcept>
31#include <string>
32#include <vector>
33
34#include <kiplatform/sysinfo.h>
35
36#include <algorithm>
37#include <sstream>
38#include <vector>
39
40#include <wx/string.h>
41#include <wx/msw/registry.h>
42
43#include "d3d9.h"
44#include "dxgi.h"
45
46
47namespace KIPLATFORM
48{
49namespace
50{
51 /*
52 * Helper functions from https://stackoverflow.com/questions/6284524/bstr-to-stdstring-stdwstring-and-vice-versa
53 * Licensed CC BY-SA 3.0
54 */
55 std::string ConvertWCSToMBS( const wchar_t* pstr, long wslen )
56 {
57 int len = ::WideCharToMultiByte( CP_ACP, 0, pstr, wslen, NULL, 0, NULL, NULL );
58
59 std::string dblstr( len, '\0' );
60 len = ::WideCharToMultiByte( CP_ACP, 0 /* no flags */, pstr,
61 wslen /* not necessary NULL-terminated */, &dblstr[0], len, NULL,
62 NULL /* no default char */ );
63
64 return dblstr;
65 }
66
67
68 std::string ConvertBSTRToMBS( BSTR bstr )
69 {
70 int wslen = ::SysStringLen( bstr );
71 return ConvertWCSToMBS( (wchar_t*) bstr, wslen );
72 }
73
74
75 BSTR ConvertMBSToBSTR( const std::string& str )
76 {
77 int wsLen = ::MultiByteToWideChar( CP_ACP, 0 /* no flags */, str.data(), str.length(), NULL,
78 0 );
79
80 BSTR wsData = ::SysAllocStringLen( NULL, wsLen );
81 if( ::MultiByteToWideChar( CP_ACP, 0 /* no flags */,
82 str.data(),
83 str.length(),
84 wsData,
85 wsLen ) == 0 )
86 {
87 // conversion failure
88 ::SysFreeString( wsData );
89 wsData = NULL;
90 }
91
92 return wsData;
93 }
94}
95
96
98{
99 openWmi();
100}
101
102
104{
105 freeWmi();
106}
107
108
110{
111 HRESULT hres;
112
113 // We assume wxwidgets is handling calling of CoInitializeEx and CoInitializeSecurity
114 hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator,
115 (LPVOID*) &m_pLoc );
116
117 if( FAILED( hres ) )
118 {
119 throw std::runtime_error( "Failed to create IWbemLocator object." );
120 return hres;
121 }
122
123 // Connect to the root\cimv2 namespace with
124 // the current user and obtain pointer pSvc
125 // to make IWbemServices calls.
126 hres = m_pLoc->ConnectServer( ConvertMBSToBSTR( "ROOT\\CIMV2" ), // Object path of WMI namespace
127 NULL, // User name. NULL = current user
128 NULL, // User password. NULL = current
129 0, // Locale. NULL indicates current
130 0, // Security flags.
131 0, // Authority (for example, Kerberos)
132 0, // Context object
133 &m_pSvc // pointer to IWbemServices proxy
134 );
135
136 if( FAILED( hres ) )
137 {
138 m_pLoc->Release();
139 throw std::runtime_error( "Could not connect to root\\cimv2." );
140 return hres;
141 }
142
143 hres = CoSetProxyBlanket( m_pSvc, // Indicates the proxy to set
144 RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
145 RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
146 NULL, // Server principal name
147 RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
148 RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
149 NULL, // client identity
150 EOAC_NONE // proxy capabilities
151 );
152
153 if( FAILED( hres ) )
154 {
155 m_pSvc->Release();
156 m_pLoc->Release();
157 throw std::runtime_error( "Could not set proxy blanket." );
158 return hres;
159 }
160
161 return hres;
162}
163
164
166{
167 HRESULT hres = 0;
168
169 if( m_pSvc != nullptr )
170 {
171 hres = m_pSvc->Release();
172 }
173
174 if( m_pLoc != nullptr )
175 {
176 hres = m_pLoc->Release();
177 }
178
179 if( m_pEnumerator != nullptr )
180 {
181 hres = m_pEnumerator->Release();
182 }
183
184 return hres;
185}
186
187
188std::string SYSINFO::improveDriverVersion( const std::string& aManufacturer,
189 const std::string& aVersion )
190{
191 if( aManufacturer.find( "NVIDIA" ) != std::string::npos
192 || aManufacturer.find( "Nvidia" ) != std::string::npos )
193 {
194 std::istringstream versionStream( aVersion );
195 std::string s;
196 std::vector<std::string> versionBits;
197 while( getline( versionStream, s, '.' ) )
198 {
199 versionBits.push_back( s );
200 }
201
202 if( versionBits.size() == 4 )
203 {
204 unsigned int numChars2 = versionBits[2].length();
205 unsigned int numChars3 = versionBits[2].length();
206 if( numChars2 >= 1 && numChars3 >= 2 )
207 {
208 if( numChars3 < 4 )
209 {
210 versionBits[3].insert( 0, ( 4 - versionBits[3].size() ), '0' );
211 numChars3 = 4;
212 }
213 }
214
215 std::string major1 = versionBits[2].substr( numChars2 - 1, numChars2 - 1 );
216 std::string major2 = versionBits[3].substr( 0, 1 );
217 std::string major = major1 + major2;
218 std::string minor = versionBits[3].substr( 2, numChars3 - 1 );
219
220
221 return major + "." + minor;
222 }
223 }
224
225 return aVersion;
226}
227
228
229bool SYSINFO::GetMemoryInfo( MEMORY_INFO& aMemoryInfo )
230{
231 MEMORYSTATUSEX statex;
232
233 statex.dwLength = sizeof( statex );
234
235 if( GlobalMemoryStatusEx( &statex ) != 0 )
236 {
237 aMemoryInfo.Usage = statex.dwMemoryLoad;
238 aMemoryInfo.TotalPhysical = statex.ullTotalPhys;
239 aMemoryInfo.FreePhysical = statex.ullAvailPhys;
240 aMemoryInfo.TotalPaging = statex.ullTotalPageFile;
241 aMemoryInfo.FreePaging = statex.ullAvailPageFile;
242 aMemoryInfo.TotalVirtual = statex.ullTotalVirtual;
243 aMemoryInfo.FreeVirtual = statex.ullAvailVirtual;
244
245 return true;
246 }
247
248 return false;
249}
250
251
252bool SYSINFO::GetCPUInfo( std::vector<CPU_INFO>& aCpuInfos )
253{
254 HRESULT hres;
255 DWORD returned = 0;
256
257 IWbemClassObject* processors[2];
258 hres = m_pSvc->ExecQuery(
259 ConvertMBSToBSTR( "WQL" ), ConvertMBSToBSTR( "SELECT * FROM Win32_Processor" ),
260 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 0, &m_pEnumerator );
261
262 if( SUCCEEDED( hres ) )
263 {
264 hres = m_pEnumerator->Next( WBEM_INFINITE, 2, processors, &returned );
265 if( FAILED( hres ) || returned == 0 )
266 {
267 return false;
268 }
269 }
270
271 VARIANT var;
272 VariantInit( &var );
273
274 std::string chRetValue = "";
275 for( UINT ctrlIndex = 0; ctrlIndex < returned; ctrlIndex++ )
276 {
277 IWbemClassObject* processor = processors[ctrlIndex];
278
279 CPU_INFO cpuInfo;
280 hres = processor->Get( ConvertMBSToBSTR( "Name" ), 0, &var, NULL, NULL );
281 if( SUCCEEDED( hres ) )
282 {
283 variantToString( &var, chRetValue );
284 cpuInfo.Name = chRetValue;
285
286 VariantClear( &var );
287 processor->Get( ConvertMBSToBSTR( "Manufacturer" ), 0, &var, NULL, NULL );
288 variantToString( &var, chRetValue );
289 cpuInfo.Manufacturer = chRetValue;
290
291 VariantClear( &var );
292 processor->Get( ConvertMBSToBSTR( "NumberOfCores" ), 0, &var, NULL, NULL );
293 variantToString( &var, chRetValue );
294 long long cores = atoll( chRetValue.c_str() );
295 cpuInfo.NumberCores = cores;
296
297 VariantClear( &var );
298 processor->Get( ConvertMBSToBSTR( "NumberOfLogicalProcessors" ), 0, &var, NULL, NULL );
299 variantToString( &var, chRetValue );
300 long long logical = atoll( chRetValue.c_str() );
301 cpuInfo.NumberLogical = logical;
302
303
304 aCpuInfos.push_back( cpuInfo );
305 }
306
307 VariantClear( &var );
308 }
309
310
311 return true;
312}
313
314
315bool SYSINFO::getVersionFromDXRegistry( int64_t aAdapterLuid, std::string& aDriverVersion )
316{
317 wxString baseKeyName = "SOFTWARE\\Microsoft\\DirectX";
318 wxRegKey key( wxRegKey::HKLM, baseKeyName );
319 HKEY tmpKey;
320
321 if( key.HasSubkeys() )
322 {
323 wxString adapterGuid;
324 long index = 0;
325 for( bool cont = key.GetFirstKey( adapterGuid, index ); cont;
326 cont = key.GetNextKey( adapterGuid, index ) )
327 {
328 wxString subKeyName = baseKeyName + "\\" + adapterGuid;
329
330 LSTATUS status;
331 status = ::RegOpenKeyEx(
332 (HKEY) HKEY_LOCAL_MACHINE, subKeyName.t_str(), 0, KEY_READ, &tmpKey );
333
334 ULONGLONG adapterLuid;
335 DWORD dwType, dwSize = sizeof(ULONGLONG);
336 status = ::RegQueryValueEx((HKEY)tmpKey, L"AdapterLuid",
337 0,
338 &dwType, (LPBYTE)&adapterLuid, &dwSize);
339
340 if (status != ERROR_SUCCESS)
341 {
342 ::RegCloseKey(tmpKey);
343 continue;
344 }
345
346 //access error, target ended up weirdly the wrong type
347 if ( dwType != REG_QWORD_LITTLE_ENDIAN && dwType != REG_QWORD ) {
348 ::RegCloseKey(tmpKey);
349 continue;
350 }
351
352 if (adapterLuid != aAdapterLuid)
353 {
354 ::RegCloseKey(tmpKey);
355 continue;
356 }
357
358 ULONGLONG driverVersion;
359 status = ::RegQueryValueEx((HKEY)tmpKey, L"DriverVersion",
360 0,
361 &dwType, (LPBYTE)&driverVersion, &dwSize);
362
363 if (status != ERROR_SUCCESS)
364 {
365 ::RegCloseKey(tmpKey);
366 continue;
367 }
368
369 //access error, target ended up weirdly the wrong type
370 if ( dwType != REG_QWORD_LITTLE_ENDIAN && dwType != REG_QWORD ) {
371 ::RegCloseKey(tmpKey);
372 continue;
373 }
374
375 std::stringstream fmt;
376 fmt << ( ( driverVersion >> 48 ) & 0xFFFF ) << "."
377 << ( ( driverVersion >> 32 ) & 0xFFFF ) << "."
378 << std::to_string( ( driverVersion >> 16 ) & 0xFFFF ) << "."
379 << std::to_string( driverVersion & 0xFFFF );
380 aDriverVersion = fmt.str();
381
382 ::RegCloseKey(tmpKey);
383 return true;
384 }
385 }
386
387 return false;
388}
389
390
391bool SYSINFO::gpuFromDirectX( std::vector<GPU_INFO>& aGpuInfos )
392{
393 typedef HRESULT( WINAPI * LPCREATEDXGIFACTORY )( REFIID, void** );
394 LPCREATEDXGIFACTORY pCreateDXGIFactory = NULL;
395 HMODULE hDXGI = NULL;
396
397 hDXGI = LoadLibrary( L"dxgi.dll" );
398 //
399 if( NULL == hDXGI )
400 {
401 return false;
402 }
403
404 IID iid_IDXGIFactory;
405
406 //to avoid having to link dxgi.dll, we also hardcode the guid of the interface
407 IIDFromString( L"{7b7166ec-21c7-44ae-b21a-c9ae321ae369}", &iid_IDXGIFactory );
408
409 IDXGIAdapter* pAdapter = NULL;
410 pCreateDXGIFactory = (LPCREATEDXGIFACTORY) GetProcAddress( hDXGI, "CreateDXGIFactory" );
411 if( pCreateDXGIFactory )
412 {
413 IDXGIFactory* pDXGIFactory;
414 HRESULT hr = pCreateDXGIFactory( iid_IDXGIFactory, (void**) &pDXGIFactory );
415 if( SUCCEEDED( hr ) )
416 {
417 UINT adapter = 0;
418 while( pDXGIFactory->EnumAdapters( adapter, &pAdapter ) != DXGI_ERROR_NOT_FOUND )
419 {
420 DXGI_ADAPTER_DESC desc;
421 hr = pAdapter->GetDesc( &desc );
422 if( SUCCEEDED( hr ) )
423 {
424 // Microsoft Basic Render Driver that will always show up
425 if( desc.VendorId == 5140 && desc.DeviceId == 140 )
426 {
427 adapter++;
428 continue;
429 }
430 GPU_INFO gpuInfo;
431
432 char descriptionBuffer[260];
433 char defaultChar = ' ';
434 WideCharToMultiByte( CP_ACP, 0, desc.Description, -1, descriptionBuffer,
435 sizeof( descriptionBuffer ), &defaultChar, NULL );
436
437 gpuInfo.Name = descriptionBuffer;
438 // Names are generally formatted with Manu<space>Model
439 gpuInfo.Manufacturer = gpuInfo.Name.substr( 0, gpuInfo.Name.find( " " ) );
440 ;
441 gpuInfo.MemorySize = desc.DedicatedVideoMemory;
442
443 int64_t luid = ( ( (int64_t) desc.AdapterLuid.HighPart ) << 32 )
444 | desc.AdapterLuid.LowPart;
445
446 if( getVersionFromDXRegistry( luid, gpuInfo.DriverVersion ) )
447 {
449 }
450
451 aGpuInfos.push_back( gpuInfo );
452 }
453
454 adapter++;
455 }
456
457 pDXGIFactory->Release();
458 }
459 }
460
461 FreeLibrary( hDXGI );
462
463 return true;
464}
465
466
467bool SYSINFO::GetGPUInfo( std::vector<GPU_INFO>& aGpuInfos )
468{
469 if (gpuFromDirectX(aGpuInfos))
470 {
471 return true;
472 }
473
474 return false;
475}
476
477
478void SYSINFO::variantToString( const LPVARIANT aVar, std::string& aReturnString ) const
479{
480 HRESULT hr;
481 char returnBuffer[256];
482
483 switch( aVar->vt )
484 {
485 case VT_BSTR:
486 {
487 aReturnString = ConvertBSTRToMBS( aVar->bstrVal );
488 }
489 break;
490 case VT_BOOL:
491 {
492 if( VARIANT_TRUE == aVar->boolVal )
493 aReturnString = "true";
494 else
495 aReturnString = "false";
496 }
497 break;
498 case VT_I4:
499 {
500 sprintf_s( returnBuffer, sizeof( returnBuffer ) - 1, "%u", aVar->uintVal );
501
502 aReturnString = returnBuffer;
503 }
504 break;
505 case VT_UI1:
506 {
507 sprintf_s( returnBuffer, sizeof( returnBuffer ) - 1, "%u", aVar->uintVal );
508 aReturnString = returnBuffer;
509 }
510 break;
511 case VT_UI4:
512 {
513 sprintf_s( returnBuffer, sizeof( returnBuffer ) - 1, "%u", aVar->uintVal );
514 aReturnString = returnBuffer;
515 }
516 break;
517 case VT_BSTR | VT_ARRAY:
518 {
519 VARIANT* raw;
520 hr = SafeArrayAccessData( aVar->parray, (void**) &raw );
521
522 const _bstr_t bstr( raw[0] );
523 hr = SafeArrayUnaccessData( aVar->parray );
524
525 aReturnString = ConvertBSTRToMBS( bstr );
526 }
527 break;
528 case VT_I4 | VT_ARRAY:
529 {
530 BYTE HUGEP* pBuf;
531 LONG low, high;
532 SafeArrayGetLBound( aVar->parray, 1, &low );
533 SafeArrayGetUBound( aVar->parray, 1, &high );
534
535 hr = SafeArrayAccessData( aVar->parray, (void HUGEP**) &pBuf );
536 hr = SafeArrayUnaccessData( aVar->parray );
537 std::string strTmp;
538 high = std::min( high, (long) MAX_PATH * 2 - 1 );
539 for( LONG i = low; i <= high; ++i )
540 {
541 sprintf_s( returnBuffer, sizeof( returnBuffer ) - 1, "%02X", pBuf[i] );
542 aReturnString += returnBuffer;
543 }
544 }
545 break;
546 default: break;
547 }
548}
549
550} // namespace KIPLATFORM
IWbemLocator * m_pLoc
void variantToString(const LPVARIANT aVar, std::string &aReturnString) const
Converts a win32 LPVARIANT to a string.
std::string improveDriverVersion(const std::string &aManufacturer, const std::string &aVersion)
Attempts to convert the Windows driver version string to the vendor version string if possible.
bool gpuFromDirectX(std::vector< GPU_INFO > &aGpuInfos)
Fetches gpu info from directx and registry WMI unforunately has a uint32 max value issue for reportin...
bool GetCPUInfo(std::vector< CPU_INFO > &aCpuInfos) override
Retrieve CPU info for the system.
IWbemServices * m_pSvc
bool GetGPUInfo(std::vector< GPU_INFO > &aGpuInfos) override
Retrieve GPU info for the system.
bool getVersionFromDXRegistry(int64_t aAdapterLuid, std::string &aDriverVersion)
Extracts the driver version for an dapter from the directx section of the registry.
IEnumWbemClassObject * m_pEnumerator
bool GetMemoryInfo(MEMORY_INFO &aRamInfo) override
Retrieve memory info for the system.
Definition: app.h:28
std::string Name
Definition: sysinfo.h:36
std::string DriverVersion
Definition: sysinfo.h:38
long long MemorySize
Definition: sysinfo.h:37
std::string Manufacturer
Definition: sysinfo.h:39
uint64_t FreePhysical
Definition: sysinfo.h:56
uint64_t FreeVirtual
Definition: sysinfo.h:60
uint64_t TotalPaging
Definition: sysinfo.h:57
uint64_t TotalVirtual
Definition: sysinfo.h:59
uint64_t TotalPhysical
Definition: sysinfo.h:55