System information

Started by
6 comments, last by b2b3 17 years ago
Hey guys! I'm thinking about implementing a "report error" function into my game (yes, like the one you HATE in Windows) ;-) IMHO this would be quite useful for the beta testing phase. The big problem here is that I don't have a clue on how to get important System information (which would be very important for debugging). The language I'm using is C++. Of course it would be nice if Windows had some functionality to hand over these information directly as strings/char arrays with a single function call :-D What I'm looking for is: 1) What CPU is in the PC? 2) How much RAM? 3) What graphics card? 4) How much graphics RAM? 5) What OS Version (XP, SP1/SP2, Vista...) is installed? 6) etc......... What's the easiest way to get such information? Is there even one? (tools like Everest are doing this and much more, so it MUST be possible) ;-) Thanks again!
Advertisement
There's not a single function call, unless you use some 3rd party lib. But you can get the details for all of it individually. Here's a big chunk of source code from my engine. which logs the CPU, RAM and OS:
//============================================================================// Read the CPU speed from the registry//============================================================================DWORD ReadCPUSpeedFromRegistry(DWORD dwCPU){HKEY hKey;DWORD dwSpeed;	// Get the key name	wchar_t szKey[256];	_snwprintf(szKey, sizeof(szKey)/sizeof(wchar_t),		L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d\\", dwCPU);	// Open the key	if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,szKey, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)	{		return 0;	}	// Read the value	DWORD dwLen = 4;	if(RegQueryValueEx(hKey, L"~MHz", NULL, NULL, (LPBYTE)&dwSpeed, &dwLen) != ERROR_SUCCESS)	{		RegCloseKey(hKey);		return 0;	}	// Cleanup and return	RegCloseKey(hKey);    return dwSpeed;}//============================================================================// Calculate and log the CPU speed and features//============================================================================static const unsigned PROCESSOR_UNKNOWN = 0;static const unsigned PROCESSOR_AMD = 1;static const unsigned PROCESSOR_INTEL = 2;void LogCPU(){unsigned nHighestFeature;unsigned nHighestFeatureEx;int nBuff[4];char szMan[13];char szFeatures[256];unsigned nProcessorType;	// Get CPU manufacturer and highest CPUID	__cpuid(nBuff, 0);	nHighestFeature = (unsigned)nBuff[0];	*(int*)&szMan[0] = nBuff[1];	*(int*)&szMan[4] = nBuff[3];	*(int*)&szMan[8] = nBuff[2];	szMan[12] = 0;	if(strcmp(szMan, "AuthenticAMD") == 0)		nProcessorType = PROCESSOR_AMD;	else if(strcmp(szMan, "GenuineIntel") == 0)		nProcessorType = PROCESSOR_INTEL;	else		nProcessorType = PROCESSOR_UNKNOWN;	// Get highest extended feature	__cpuid(nBuff, 0x80000000);	nHighestFeatureEx = (unsigned)nBuff[0];	// Get processor brand name	if(nHighestFeatureEx >= 0x80000004)	{		char szCPUName[49];		szCPUName[0] = 0;		__cpuid((int*)&szCPUName[0], 0x80000002);		__cpuid((int*)&szCPUName[16], 0x80000003);		__cpuid((int*)&szCPUName[32], 0x80000004);		szCPUName[48] = 0;		for(int i=(int)strlen(szCPUName)-1; i>=0; --i)		{			if(szCPUName == ' ')				szCPUName = '\0';			else				break;		}		ELog::Get().SystemFormat(L"PERF    : CPU: %S (%S)\n", szCPUName, szMan);	}	else		ELog::Get().SystemFormat(L"PERF    : CPU: %S\n", szMan);	// Get CPU features	szFeatures[0] = 0;	if(nHighestFeature >= 1)	{		__cpuid(nBuff, 1);		if(nBuff[3] & 1<<0)			strcat(szFeatures, "FPU ");		if(nBuff[3] & 1<<23)			strcat(szFeatures, "MMX ");		if(nBuff[3] & 1<<25)			strcat(szFeatures, "SSE ");		if(nBuff[3] & 1<<26)			strcat(szFeatures, "SSE2 ");		if(nBuff[2] & 1<<0)			strcat(szFeatures, "SSE3 ");		// Intel specific:		if(nProcessorType == PROCESSOR_INTEL)		{			if(nBuff[2] & 1<<9)				strcat(szFeatures, "SSSE3 ");			if(nBuff[2] & 1<<7)				strcat(szFeatures, "EST ");		}		if(nBuff[3] & 1<<28)			strcat(szFeatures, "HTT ");	}	// AMD specific:	if(nProcessorType == PROCESSOR_AMD)	{		// Get extended features		__cpuid(nBuff, 0x80000000);		if(nHighestFeatureEx >= 0x80000001)		{			__cpuid(nBuff, 0x80000001);			if(nBuff[3] & 1<<31)				strcat(szFeatures, "3DNow! ");			if(nBuff[3] & 1<<30)				strcat(szFeatures, "Ex3DNow! ");			if(nBuff[3] & 1<<22)				strcat(szFeatures, "MmxExt ");		}		// Get level 1 cache size		if(nHighestFeatureEx >= 0x80000005)		{			__cpuid(nBuff, 0x80000005);			ELog::Get().SystemFormat(L"PERF    : L1 cache size: %dK\n", ((unsigned)nBuff[2])>>24);		}	}	// Get cache size	if(nHighestFeatureEx >= 0x80000006)	{		__cpuid(nBuff, 0x80000006);		ELog::Get().SystemFormat(L"PERF    : L2 cache size: %dK\n", ((unsigned)nBuff[2])>>16);	}	// Log features	ELog::Get().SystemFormat(L"PERF    : CPU Features: %S\n", szFeatures);	// Get misc system info	SYSTEM_INFO theInfo;	GetSystemInfo(&theInfo);	// Log number of CPUs and speeds	ELog::Get().SystemFormat(L"PERF    : Number of CPUs: %d\n", theInfo.dwNumberOfProcessors);	for(DWORD i=0; i<theInfo.dwNumberOfProcessors; ++i)	{		DWORD dwCPUSpeed = ReadCPUSpeedFromRegistry(i);		ELog::Get().SystemFormat(L"PERF    : * CPU %d speed: ~%dMHz\n", i, dwCPUSpeed);	}}//============================================================================// Calculate and log the amount of RAM in the machine//============================================================================void LogRAM(){	// Get memory status	MEMORYSTATUS theStatus;	ZeroMemory(&theStatus,sizeof(theStatus));	theStatus.dwLength = sizeof(theStatus);	GlobalMemoryStatus(&theStatus);	// Log it	DWORD dwRAM = (DWORD)(theStatus.dwTotalPhys/(1024*1024));	if(theStatus.dwTotalPhys != dwRAM*1024*1024)		++dwRAM;	ELog::Get().SystemFormat(L"PERF    : Available physical RAM: %dMB/%dMB\n",		theStatus.dwAvailPhys/(1024*1024), dwRAM);}//============================================================================// Cetermine OS type//============================================================================void LogOS(){wchar_t szOS[256];OSVERSIONINFOEX osInfo;SYSTEM_INFO sysInfo;	// Get system info	ZeroMemory(&sysInfo, sizeof(sysInfo));	GetSystemInfo(&sysInfo);	// Get OS version	wcscpy(szOS, L"PERF    : OS: ");	ZeroMemory(&osInfo, sizeof(osInfo));	osInfo.dwOSVersionInfoSize = sizeof(osInfo);	if(!GetVersionEx((OSVERSIONINFO*)&osInfo))	{		ZeroMemory(&osInfo, sizeof(osInfo));		osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);		GetVersionEx((OSVERSIONINFO*)&osInfo);	}	// Win 9x	if(osInfo.dwPlatformId == 1)	{		if((osInfo.dwMajorVersion == 4) && (osInfo.dwMinorVersion == 0))			wcscat(szOS, L"Windows 95 ");		else if((osInfo.dwMajorVersion == 4) && (osInfo.dwMinorVersion == 10))			wcscat(szOS, L"Windows 98 ");		else if((osInfo.dwMajorVersion == 4) && (osInfo.dwMinorVersion == 90))			wcscat(szOS, L"Windows ME ");		else			wcscat(szOS, L"Unknown Windows OS ");	}	// Win NT	else if(osInfo.dwPlatformId == 2)	{		if((osInfo.dwMajorVersion == 4) && (osInfo.dwMinorVersion == 0))			wcscat(szOS, L"Windows NT 4.0 ");		else if((osInfo.dwMajorVersion == 5) && (osInfo.dwMinorVersion == 0))			wcscat(szOS, L"Windows 2000 ");		else if((osInfo.dwMajorVersion == 5) && (osInfo.dwMinorVersion == 1))			wcscat(szOS, L"Windows XP ");		else if((osInfo.dwMajorVersion == 5) && (osInfo.dwMinorVersion == 2))		{			if(GetSystemMetrics(89))	// SM_SERVERR2 == 89				wcscat(szOS, L"Windows Server 2003 R2 ");			else if((osInfo.wProductType == VER_NT_WORKSTATION) &&				(sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64))			{				wcscat(szOS, L"Windows XP Professional x64 Edition ");			}			else				wcscat(szOS, L"Windows Server 2003 ");		}		else if((osInfo.dwMajorVersion == 6) && (osInfo.dwMinorVersion == 0))			wcscat(szOS, L"Windows Vista ");		else			wcscat(szOS, L"Unknown WinNT OS ");	}	// Unknown	else		wcscat(szOS, L"Unknown Operating System ");	wcscat(szOS, osInfo.szCSDVersion);	wcscat(szOS, L"\n");	// Log	ELog::Get().SystemLog(szOS);#if defined(BUILD_X64)	ELog::Get().SystemLog(L"PERF    : Win64 version\n");#elif defined(_WIN32)	ELog::Get().SystemLog(L"PERF    : Win32 version\n");#else	ELog::Get().SystemLog(L"PERF    : Unknown version\n");#endif}

The only part that probably needs some explanation is LogCPU(), which uses an assembly function (Well, a compiler intrinsic) to get CPU features.

The graphics card name you can get by creating an IDirect3D9 interface, either by linking to d3d9.lib, or using LoadLibrary() and GetProcAddress(), then use IDirect3D9::GetAdapterIdentifier() to get the name of the primary display adapter.
Booya! Quite complicated :-D
Thanks for sharing your code, I'll have a looooooooooooooooong look at it.
Quote:Original post by VanKurt
Booya! Quite complicated :-D
Thanks for sharing your code, I'll have a looooooooooooooooong look at it.
It's (hopefully [smile]) not that bad. The main mess is getting from random flags into human-readable information. The LogRAM() and ReadCPUSpeedFromRegistry() functions should be simple, and the LogOS() is just a lot of if..else statements. The LogCPU() function can be trimmed down to under 10 lines if you only want the CPU name (E.g. "AMD Athlon(tm) 64 Processor 3800+"). Give me a shout if you want me to give you the code for just that.
One potential - albeit kludgy - method would be to call dxdiag and attach that log to your error report;
Quote:
Usage: dxdiag [/x outfile] [/t outfile] [/whql:on | /whql:off]/x outfile	- silently save XML information to <outfile> and quit/t outfile	- silently save txt information to <outfile> and quit/whql:on	- allow dxdiag to check for WHQL digital signatures/whql:off	- do not allow dxdiag to check for WHQL digital signaturesNote: Checking for WHQL digital signatures may connect via internet to update WHQL certificates.
Easy, yes - elegant, no. I'd only use it if it was there briefly for testing purposes; stick with Evil Steve's.

[Website] [+++ Divide By Cucumber Error. Please Reinstall Universe And Reboot +++]

"Give me a shout if you want me to give you the code for just that."

Thank's for the offer :-) But you've done more than enough already. The code isn't as evil as I thought!

Thank's so much!!!
You could just use the WMI
I would also recommend WMI (if you are developing for Windows). It's not that hard to use and stores tons of data about every device you have installed. If you have good support code (that is code that accesses WMI resources), adding new features to your detection routines is very trivial.
Using WMI has multiple advantages over custom routines:

  • Usually you don't need super detailed data about CPU, so writing own asm routine is kind-of pointless (but it is still fun [smile]).

  • If you write your WMI code well, it will run on every Windows newer then Win2K. There is no guaranteee that certain registry keys will not move or be declared deprecated. With WMI you don't need to care about that. Just access data that is stored in WMI classes.

  • Everything is stored on one place. No need to search through obscure registry keys hoping that key XYZ stores value you want. Just look at list of the WMI classes and you are set.

  • WMI provides simple, database-like query mechanism (WQL language)

  • WMI can even be used to monitor your system performace. Again, zillion different counters are available.


I've used WMI to collect various system data and even monitor system performance and I was quite happy that I decided to switch to WMI, since my original code was really messy and was nightmare to test.

This topic is closed to new replies.

Advertisement