reactos/dll/cpl/sysdm/general.c
Joachim Henze be2518704e [SYSDM] Fix version string cutoff CORE-17429
The version string cutoff started to happen when we switched from SVN
with its short revision number to git with the longer hashes.
0.4.7-dev-502-gc2c66af was the first git-only rev (2017-Oct-03)

This brings the dialogs layout closer to XP.
Also fixes some other truncations for specific languages.
And unifies the touched text controls sizes for all languages.

The credits have been moved to readme.txt where they are more present and
we can avoid having to groom all languages files each time we want to add
a new dev (and sysdm.cpl to grow each time). Less maintenance.

0.4.15-dev-1629-g9aa73da gcc 8.4.0 dbg RosBE2.2.1 I18N=all binary size
sysdm.cpl before: 925.696bytes  after: 705.024bytes
readme.txt still fits into a single NTFS cluster afterwards with 3702bytes
2021-01-20 02:45:44 +01:00

616 lines
18 KiB
C

/*
* PROJECT: ReactOS System Control Panel Applet
* LICENSE: GPL - See COPYING in the top level directory
* FILE: dll/cpl/sysdm/general.c
* PURPOSE: General System Information
* COPYRIGHT: Copyright Thomas Weidenmueller <w3seek@reactos.org>
* Copyright 2006 Ged Murphy <gedmurphy@gmail.com>
* Copyright 2006-2007 Colin Finck <mail@colinfinck.de>
*
*/
#include "precomp.h"
#include <winnls.h>
#include <powrprof.h>
#include <buildno.h>
#include <strsafe.h>
#define ANIM_STEP 2
#define ANIM_TIME 50
typedef struct _IMGINFO
{
HBITMAP hBitmap;
INT cxSource;
INT cySource;
INT iPlanes;
INT iBits;
} IMGINFO, *PIMGINFO;
static PIMGINFO pImgInfo;
static const BLENDFUNCTION BlendFunc = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
VOID ShowLastWin32Error(HWND hWndOwner)
{
LPTSTR lpMsg;
DWORD LastError;
LastError = GetLastError();
if (LastError == ERROR_SUCCESS)
return;
if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
LastError,
LANG_USER_DEFAULT,
(LPTSTR)&lpMsg,
0, NULL))
{
return;
}
MessageBox(hWndOwner, lpMsg, NULL, MB_OK | MB_ICONERROR);
LocalFree(lpMsg);
}
static VOID InitLogo(HWND hwndDlg)
{
BITMAP logoBitmap;
BITMAP maskBitmap;
BITMAPINFO bmpi;
HDC hDC = GetDC(hwndDlg);
HDC hDCLogo = CreateCompatibleDC(NULL);
HDC hDCMask = CreateCompatibleDC(NULL);
HBITMAP hMask, hLogo, hAlphaLogo = NULL;
COLORREF *pBits;
INT line, column;
if (hDC == NULL || hDCLogo == NULL || hDCMask == NULL)
goto Cleanup;
ZeroMemory(pImgInfo, sizeof(*pImgInfo));
ZeroMemory(&bmpi, sizeof(bmpi));
hLogo = (HBITMAP)LoadImageW(hApplet, MAKEINTRESOURCEW(IDB_ROSBMP), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
hMask = (HBITMAP)LoadImageW(hApplet, MAKEINTRESOURCEW(IDB_ROSMASK), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
if (hLogo != NULL && hMask != NULL)
{
GetObject(hLogo, sizeof(logoBitmap), &logoBitmap);
GetObject(hMask, sizeof(maskBitmap), &maskBitmap);
if (logoBitmap.bmHeight != maskBitmap.bmHeight || logoBitmap.bmWidth != maskBitmap.bmWidth)
goto Cleanup;
bmpi.bmiHeader.biSize = sizeof(BITMAPINFO);
bmpi.bmiHeader.biWidth = logoBitmap.bmWidth;
bmpi.bmiHeader.biHeight = logoBitmap.bmHeight;
bmpi.bmiHeader.biPlanes = 1;
bmpi.bmiHeader.biBitCount = 32;
bmpi.bmiHeader.biCompression = BI_RGB;
bmpi.bmiHeader.biSizeImage = 4 * logoBitmap.bmWidth * logoBitmap.bmHeight;
/* Create a premultiplied bitmap */
hAlphaLogo = CreateDIBSection(hDC, &bmpi, DIB_RGB_COLORS, (PVOID*)&pBits, 0, 0);
if (!hAlphaLogo)
goto Cleanup;
SelectObject(hDCLogo, hLogo);
SelectObject(hDCMask, hMask);
for (line = logoBitmap.bmHeight - 1; line >= 0; line--)
{
for (column = 0; column < logoBitmap.bmWidth; column++)
{
COLORREF alpha = GetPixel(hDCMask, column, line) & 0xFF;
COLORREF Color = GetPixel(hDCLogo, column, line);
DWORD r, g, b;
r = GetRValue(Color) * alpha / 255;
g = GetGValue(Color) * alpha / 255;
b = GetBValue(Color) * alpha / 255;
*pBits++ = b | (g << 8) | (r << 16) | (alpha << 24);
}
}
pImgInfo->hBitmap = hAlphaLogo;
pImgInfo->cxSource = logoBitmap.bmWidth;
pImgInfo->cySource = logoBitmap.bmHeight;
pImgInfo->iBits = logoBitmap.bmBitsPixel;
pImgInfo->iPlanes = logoBitmap.bmPlanes;
}
Cleanup:
if (hMask != NULL) DeleteObject(hMask);
if (hLogo != NULL) DeleteObject(hLogo);
if (hDCMask != NULL) DeleteDC(hDCMask);
if (hDCLogo != NULL) DeleteDC(hDCLogo);
if (hDC != NULL) ReleaseDC(hwndDlg, hDC);
}
LRESULT CALLBACK RosImageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_PAINT:
{
PAINTSTRUCT PS;
HDC hdcMem, hdc;
LONG left;
hdc = wParam != 0 ? (HDC)wParam : BeginPaint(hwnd, &PS);
GetClientRect(hwnd, &PS.rcPaint);
/* Position image in center of dialog */
left = (PS.rcPaint.right - pImgInfo->cxSource) / 2;
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL)
{
SelectObject(hdcMem, pImgInfo->hBitmap);
/* FIXME: We should not rely on AlphaBlend and we should not import MSIMG32 solely for that function */
AlphaBlend(hdc, left, PS.rcPaint.top, pImgInfo->cxSource, pImgInfo->cySource, hdcMem, 0, 0, pImgInfo->cxSource, pImgInfo->cySource, BlendFunc);
DeleteDC(hdcMem);
}
if (wParam == 0)
EndPaint(hwnd,&PS);
break;
}
}
return TRUE;
}
static VOID SetRegTextData(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID)
{
LPTSTR lpBuf = NULL;
DWORD BufSize = 0;
DWORD Type;
if (RegQueryValueEx(hKey, Value, NULL, &Type, NULL, &BufSize) == ERROR_SUCCESS)
{
lpBuf = HeapAlloc(GetProcessHeap(), 0, BufSize);
if (!lpBuf)
return;
if (RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)lpBuf, &BufSize) == ERROR_SUCCESS)
SetDlgItemText(hwnd, uID, lpBuf);
HeapFree(GetProcessHeap(), 0, lpBuf);
}
}
static INT SetProcNameString(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID1, UINT uID2)
{
LPTSTR lpBuf = NULL;
DWORD BufSize = 0;
DWORD Type;
INT Ret = 0;
TCHAR szBuf[31];
TCHAR* szLastSpace;
INT LastSpace = 0;
if (RegQueryValueEx(hKey, Value, NULL, &Type, NULL, &BufSize) == ERROR_SUCCESS)
{
lpBuf = HeapAlloc(GetProcessHeap(), 0, BufSize);
if (!lpBuf)
return 0;
if (RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)lpBuf, &BufSize) == ERROR_SUCCESS)
{
if (BufSize > ((30 + 1) * sizeof(TCHAR)))
{
/* Wrap the Processor Name String like XP does: *
* - Take the first 30 characters and look for the last space. *
* Then wrap the string after this space. *
* - If no space is found, wrap the string after character 30. *
* *
* For example the Processor Name String of a Pentium 4 is right-aligned. *
* With this wrapping the first line looks centered. */
_tcsncpy(szBuf, lpBuf, 30);
szBuf[30] = 0;
szLastSpace = _tcsrchr(szBuf, ' ');
if (szLastSpace == 0)
{
LastSpace = 30;
}
else
{
LastSpace = (szLastSpace - szBuf);
szBuf[LastSpace] = 0;
}
_tcsncpy(szBuf, lpBuf, LastSpace);
SetDlgItemText(hwnd, uID1, szBuf);
SetDlgItemText(hwnd, uID2, lpBuf+LastSpace+1);
/* Return the number of used lines */
Ret = 2;
}
else
{
SetDlgItemText(hwnd, uID1, lpBuf);
Ret = 1;
}
}
HeapFree(GetProcessHeap(), 0, lpBuf);
}
return Ret;
}
static VOID MakeFloatValueString(DOUBLE* dFloatValue, LPTSTR szOutput, LPTSTR szAppend)
{
TCHAR szDecimalSeparator[4];
/* Get the decimal separator for the current locale */
if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, szDecimalSeparator, sizeof(szDecimalSeparator) / sizeof(TCHAR)) > 0)
{
UCHAR uDecimals;
UINT uIntegral;
/* Show the value with two decimals */
uIntegral = (UINT)*dFloatValue;
uDecimals = (UCHAR)((UINT)(*dFloatValue * 100) - uIntegral * 100);
wsprintf(szOutput, _T("%u%s%02u %s"), uIntegral, szDecimalSeparator, uDecimals, szAppend);
}
}
static VOID SetProcSpeed(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID)
{
TCHAR szBuf[64], szHz[16];
DWORD BufSize = sizeof(DWORD);
DWORD Type = REG_SZ;
PROCESSOR_POWER_INFORMATION ppi;
ZeroMemory(&ppi, sizeof(ppi));
if ((CallNtPowerInformation(ProcessorInformation,
NULL,
0,
(PVOID)&ppi,
sizeof(ppi)) == STATUS_SUCCESS &&
ppi.CurrentMhz != 0) || RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)&ppi.CurrentMhz, &BufSize) == ERROR_SUCCESS)
{
if (ppi.CurrentMhz < 1000)
{
if (!LoadString(hApplet, IDS_MEGAHERTZ, szHz, _countof(szHz)))
{
return;
}
StringCchPrintf(szBuf, _countof(szBuf), _T("%lu %s"), ppi.CurrentMhz, szHz);
}
else
{
double flt = ppi.CurrentMhz / 1000.0;
if (!LoadString(hApplet, IDS_GIGAHERTZ, szHz, _countof(szHz)))
{
return;
}
MakeFloatValueString(&flt, szBuf, szHz);
}
SetDlgItemText(hwnd, uID, szBuf);
}
}
static VOID GetSystemInformation(HWND hwnd)
{
HKEY hKey;
TCHAR SysKey[] = _T("HARDWARE\\DESCRIPTION\\System");
TCHAR ProcKey[] = _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
MEMORYSTATUSEX MemStat;
TCHAR Buf[32];
WCHAR SMBiosName[96];
INT CurMachineLine = IDC_MACHINELINE1;
/*
* Get hardware device name or motherboard name
* using information from raw SMBIOS data
*/
if (GetSystemName(SMBiosName, _countof(SMBiosName)))
{
SetDlgItemText(hwnd, CurMachineLine, SMBiosName);
CurMachineLine++;
}
else
{
/* If SMBIOS is not available, use System Identifier */
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, SysKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
SetRegTextData(hwnd, hKey, _T("Identifier"), CurMachineLine);
CurMachineLine++;
RegCloseKey(hKey);
}
}
/*
* Get Processor information
* although undocumented, this information is being pulled
* directly out of the registry instead of via setupapi as it
* contains all the info we need, and should remain static
*/
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, ProcKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
INT PrevMachineLine;
SetRegTextData(hwnd, hKey, _T("VendorIdentifier"), CurMachineLine);
CurMachineLine++;
PrevMachineLine = CurMachineLine;
CurMachineLine += SetProcNameString(hwnd,
hKey,
_T("ProcessorNameString"),
CurMachineLine,
CurMachineLine + 1);
if (CurMachineLine == PrevMachineLine)
{
/* TODO: Try obtaining CPU name from WMI (i.e. CIM_Processor) */
/* Brand String is not available, use Identifier instead */
CurMachineLine += SetProcNameString(hwnd,
hKey,
_T("Identifier"),
CurMachineLine,
CurMachineLine + 1);
}
SetProcSpeed(hwnd, hKey, _T("~MHz"), CurMachineLine);
CurMachineLine++;
RegCloseKey(hKey);
}
/* Get total physical RAM */
MemStat.dwLength = sizeof(MemStat);
if (GlobalMemoryStatusEx(&MemStat))
{
TCHAR szStr[32];
double dTotalPhys;
if (MemStat.ullTotalPhys > 1024 * 1024 * 1024)
{
UINT i = 0;
static const UINT uStrId[] = { IDS_GIGABYTE, IDS_TERABYTE, IDS_PETABYTE};
// We're dealing with GBs or more
MemStat.ullTotalPhys /= 1024 * 1024;
if (MemStat.ullTotalPhys > 1024 * 1024)
{
// We're dealing with TBs or more
MemStat.ullTotalPhys /= 1024;
i++;
if (MemStat.ullTotalPhys > 1024 * 1024)
{
// We're dealing with PBs or more
MemStat.ullTotalPhys /= 1024;
i++;
dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
}
else
{
dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
}
}
else
{
dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
}
LoadString(hApplet, uStrId[i], szStr, sizeof(szStr) / sizeof(TCHAR));
MakeFloatValueString(&dTotalPhys, Buf, szStr);
}
else
{
// We're dealing with MBs, don't show any decimals
LoadString(hApplet, IDS_MEGABYTE, szStr, sizeof(szStr) / sizeof(TCHAR));
wsprintf(Buf, _T("%u %s"), (UINT)MemStat.ullTotalPhys / 1024 / 1024, szStr);
}
SetDlgItemText(hwnd, CurMachineLine, Buf);
}
}
static VOID GetSystemVersion(HWND hwnd)
{
HWND hRosVersion;
SIZE_T lenStr, lenVersion;
PCWSTR pwszVersion = L" " TEXT(KERNEL_VERSION_RC);
PWSTR pwszStr;
lenVersion = wcslen(pwszVersion);
if (lenVersion == 0)
{
return;
}
hRosVersion = GetDlgItem(hwnd, IDC_ROSVERSION);
if (!hRosVersion)
{
return;
}
lenStr = GetWindowTextLengthW(hRosVersion);
lenStr += lenVersion + 1;
pwszStr = HeapAlloc(GetProcessHeap(), 0, lenStr * sizeof(WCHAR));
if (!pwszStr)
{
return;
}
GetWindowText(hRosVersion, pwszStr, lenStr);
StringCchCatW(pwszStr, lenStr, pwszVersion);
SetWindowText(hRosVersion, pwszStr);
HeapFree(GetProcessHeap(), 0, pwszStr);
}
ULONGLONG GetSecondsQPC(VOID)
{
LARGE_INTEGER Counter, Frequency;
QueryPerformanceCounter(&Counter);
QueryPerformanceFrequency(&Frequency);
return Counter.QuadPart / Frequency.QuadPart;
}
ULONGLONG GetSeconds(VOID)
{
ULONGLONG (WINAPI * pGetTickCount64)(VOID);
ULONGLONG Ticks64;
HMODULE hModule = GetModuleHandleW(L"kernel32.dll");
pGetTickCount64 = (PVOID)GetProcAddress(hModule, "GetTickCount64");
if (pGetTickCount64)
{
return pGetTickCount64() / 1000;
}
hModule = LoadLibraryW(L"kernel32_vista.dll");
if (!hModule)
{
return GetSecondsQPC();
}
pGetTickCount64 = (PVOID)GetProcAddress(hModule, "GetTickCount64");
if (pGetTickCount64)
{
Ticks64 = pGetTickCount64() / 1000;
}
else
{
Ticks64 = GetSecondsQPC();
}
FreeLibrary(hModule);
return Ticks64;
}
VOID GetSystemUptime(HWND hwnd)
{
HWND hRosUptime;
WCHAR szBuf[64], szStr[64];
ULONG cSeconds;
hRosUptime = GetDlgItem(hwnd, IDC_UPTIME);
if (!hRosUptime)
{
return;
}
if (!LoadStringW(hApplet, IDS_UPTIME_FORMAT, szStr, _countof(szStr)))
{
return;
}
cSeconds = GetSeconds();
StringCchPrintfW(szBuf, _countof(szBuf), szStr,
cSeconds / (60*60*24),
(cSeconds / (60*60)) % 24,
(cSeconds / 60) % 60,
cSeconds % 60);
SetWindowTextW(hRosUptime, szBuf);
}
/* Property page dialog callback */
INT_PTR CALLBACK GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
UNREFERENCED_PARAMETER(wParam);
switch (uMsg)
{
case WM_INITDIALOG:
pImgInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IMGINFO));
if (pImgInfo == NULL)
{
EndDialog(hwndDlg, 0);
return FALSE;
}
InitLogo(hwndDlg);
SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ROSIMG), GWLP_WNDPROC, (LONG_PTR)RosImageProc);
GetSystemInformation(hwndDlg);
GetSystemVersion(hwndDlg);
GetSystemUptime(hwndDlg);
break;
case WM_DESTROY:
HeapFree(GetProcessHeap(), 0, pImgInfo);
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDC_LICENCE)
{
DialogBox(hApplet, MAKEINTRESOURCE(IDD_LICENCE), hwndDlg, LicenceDlgProc);
return TRUE;
}
break;
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
if (lpDrawItem->CtlID == IDC_ROSIMG)
{
HDC hdcMem;
LONG left;
/* Position image in centre of dialog */
left = (lpDrawItem->rcItem.right - pImgInfo->cxSource) / 2;
hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
if (hdcMem != NULL)
{
SelectObject(hdcMem, pImgInfo->hBitmap);
BitBlt(lpDrawItem->hDC,
left,
lpDrawItem->rcItem.top,
lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
hdcMem,
0,
0,
SRCCOPY);
DeleteDC(hdcMem);
}
}
return TRUE;
}
case WM_NOTIFY:
{
NMHDR *nmhdr = (NMHDR *)lParam;
if (nmhdr->idFrom == IDC_ROSHOMEPAGE_LINK && nmhdr->code == NM_CLICK)
{
PNMLINK nml = (PNMLINK)nmhdr;
ShellExecuteW(hwndDlg, L"open", nml->item.szUrl, NULL, NULL, SW_SHOWNORMAL);
}
break;
}
}
return FALSE;
}