machinfo.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 19k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //
  2. // machinfo.cpp - SHGetMachineInfo and related functions
  3. //
  4. //
  5. #include "priv.h"
  6. #include <dbt.h>
  7. #include <cfgmgr32.h>
  8. #include <apithk.h>
  9. #ifndef UNIX
  10. #include <batclass.h>
  11. const GUID GUID_DEVICE_BATTERY = { 0x72631e54L, 0x78A4, 0x11d0,
  12.               { 0xbc, 0xf7, 0x00, 0xaa, 0x00, 0xb7, 0xb3, 0x2a } };
  13. #include <hydrawinsta.h>
  14. #endif
  15. //
  16. //  Win95 does not decorate BroadcastSystemMessage, so we can't either.
  17. //
  18. #undef BroadcastSystemMessage
  19. extern "C" {
  20. WINUSERAPI long WINAPI
  21. BroadcastSystemMessage(DWORD, LPDWORD, UINT, WPARAM, LPARAM);
  22. };
  23. /*****************************************************************************
  24.  *
  25.  *  DOCK STATE - Win95, Win98, and WinNT all do this differently (yuck)
  26.  *
  27.  *****************************************************************************/
  28. C_ASSERT(GMID_NOTDOCKABLE == CM_HWPI_NOT_DOCKABLE);
  29. C_ASSERT(GMID_UNDOCKED    == CM_HWPI_UNDOCKED);
  30. C_ASSERT(GMID_DOCKED      == CM_HWPI_DOCKED);
  31. #if defined(_X86_) && !defined(UNIX)
  32. typedef struct CMHDR {
  33.     LPVOID  pArgs;
  34.     DWORD   dwService;
  35.     DWORD   dwRet;
  36. } CMHDR, *PCMHDR;
  37. #define CONFIGMG_Get_Hardware_Profile_Info      0x00330052
  38. //
  39. //  GetDockedState95
  40. //
  41. DWORD GetDockedState95()
  42. {
  43.     struct GHWPI95 {                // Get_Hardware_Profile_Info parameter blk
  44.         CMHDR   cmhdr;
  45.         ULONG   ulIndex;
  46.         PHWPROFILEINFO_A pHWProfileInfo;
  47.         ULONG   ulFlags;
  48.         HWPROFILEINFO_A HWProfileInfo;
  49.     } *pghwpi;
  50.     HANDLE hheap;
  51.     DWORD Result = GMID_NOTDOCKABLE;    // assume the worst
  52. #define HEAP_SHARED     0x04000000      /* put heap in shared memory--undoc'd */
  53.     //
  54.     //  Win95 Configmg requires the parameter block to reside in the shared
  55.     //  heap since we're going to do a cross-process SendMessage.
  56.     //
  57.     hheap = HeapCreate(HEAP_SHARED, 1, 4096);
  58.     if (hheap) {
  59.         // Allocate parameter block in shared memory
  60.         pghwpi = (struct GHWPI95 *)HeapAlloc(hheap, HEAP_ZERO_MEMORY,
  61.                                              sizeof(*pghwpi));
  62.         if (pghwpi) {
  63.             DWORD dwRecipients = BSM_VXDS;
  64.             pghwpi->cmhdr.dwRet     = 0;
  65.             pghwpi->cmhdr.dwService = CONFIGMG_Get_Hardware_Profile_Info;
  66.             pghwpi->cmhdr.pArgs     = &pghwpi->ulIndex;
  67.             pghwpi->ulIndex         = 0xFFFFFFFF;
  68.             pghwpi->pHWProfileInfo  = &pghwpi->HWProfileInfo;
  69.             pghwpi->ulFlags         = 0;
  70.             // "Call" the service
  71.             BroadcastSystemMessage(0, &dwRecipients, WM_DEVICECHANGE,
  72.                                    DBT_CONFIGMGAPI32, (LPARAM)pghwpi);
  73.             if (pghwpi->cmhdr.dwRet == CR_SUCCESS) {
  74.                 Result = pghwpi->HWProfileInfo.HWPI_dwFlags;
  75.             } else {
  76.                 TraceMsg(DM_WARNING, "GetDockedState95: CONFIGMG did not respond");
  77.             }
  78.         }
  79.         HeapDestroy(hheap);
  80.     } else {
  81.         TraceMsg(DM_WARNING, "GetDockedState95: Unable to create shared heap");
  82.     }
  83.     return Result;
  84. }
  85. //
  86. //  On Win98, use the 32-bit interface to configmg.
  87. //
  88. CONFIGRET __cdecl
  89. CallConfigmg98(DWORD dwServiceNumber, ...)
  90. {
  91.     CONFIGRET cr;
  92.     HANDLE hCM;
  93.     hCM = CreateFileA("\\.\CONFIGMG",
  94.                       GENERIC_READ|GENERIC_WRITE,
  95.                       FILE_SHARE_READ|FILE_SHARE_WRITE,
  96.                       NULL, OPEN_EXISTING, 0, NULL);
  97.     if (hCM != INVALID_HANDLE_VALUE) {
  98.         DWORD dwRet;
  99.         // Evil hack that works only on x86.  Fortunately, this code is
  100.         // inside an #ifdef _X86_ block, so we're safe.
  101.         LPVOID pvArg = 1 + &dwServiceNumber;
  102.         if (DeviceIoControl(hCM, dwServiceNumber, &pvArg, sizeof(pvArg),
  103.                             &cr, sizeof(cr), &dwRet, 0) &&
  104.                             dwRet == sizeof(cr)) {
  105.         } else {
  106.             TraceMsg(DM_WARNING, "CallConfigmg98: CONFIGMG did not respond");
  107.             cr = CR_FAILURE;
  108.         }
  109.         CloseHandle(hCM);
  110.     } else {
  111.         TraceMsg(DM_WARNING, "CallConfigmg98: Couldn't connect to CONFIGMG");
  112.         cr = CR_FAILURE;
  113.     }
  114.     return cr;
  115. }
  116. DWORD GetDockedState98()
  117. {
  118.     CONFIGRET cr;
  119.     DWORD Result = GMID_NOTDOCKABLE;    // assume the worst
  120.     HWPROFILEINFO_A HWProfileInfo;
  121.     cr = CallConfigmg98(
  122.             0x80000000 + LOWORD(CONFIGMG_Get_Hardware_Profile_Info),
  123.             -1,                     // ulIndex, -1 means "current profile"
  124.             &HWProfileInfo,         // PHWPROFILEINFO
  125.             0);                     // ulFlags
  126.     if (cr == CR_SUCCESS) {
  127.         Result = HWProfileInfo.HWPI_dwFlags;
  128.     }
  129.     return Result;
  130. }
  131. #endif
  132. typedef BOOL (WINAPI *GETCURRENTHWPROFILEA)(LPHW_PROFILE_INFOA);
  133. DWORD GetDockedStateNT()
  134. {
  135.     HW_PROFILE_INFOA hpi;
  136.     GETCURRENTHWPROFILEA GetCurrentHwProfileA;
  137.     DWORD Result = GMID_NOTDOCKABLE;    // assume the worst
  138.     GetCurrentHwProfileA = (GETCURRENTHWPROFILEA)
  139.                             GetProcAddress(GetModuleHandle("ADVAPI32"),
  140.                             "GetCurrentHwProfileA");
  141.     if (GetCurrentHwProfileA && GetCurrentHwProfileA(&hpi)) {
  142.         Result = hpi.dwDockInfo & (DOCKINFO_UNDOCKED | DOCKINFO_DOCKED);
  143.         // Wackiness: If the machine does not support docking, then
  144.         // NT returns >both< flags set.  Go figure.
  145.         if (Result == (DOCKINFO_UNDOCKED | DOCKINFO_DOCKED)) {
  146.             Result = GMID_NOTDOCKABLE;
  147.         }
  148.     } else {
  149.         TraceMsg(DM_WARNING, "GetDockedStateNT: GetCurrentHwProfile failed");
  150.     }
  151.     return Result;
  152. }
  153. #if defined(_X86_) && !defined(UNIX)
  154. //
  155. //  Platforms that support Win95/Win98 need to do version switching
  156. //
  157. DWORD GetDockedState()
  158. {
  159.     if (g_bRunningOnNT) {
  160.         return GetDockedStateNT();
  161.     } else if (g_bRunningOnMemphis) {
  162.         return GetDockedState98();
  163.     } else {
  164.         return GetDockedState95();
  165.     }
  166. }
  167. #else
  168. //
  169. //  Platforms that do not support Win95/Win98 can just call the NT version.
  170. //
  171. #define GetDockedState()            GetDockedStateNT()
  172. #endif
  173. #ifndef UNIX
  174. /*****************************************************************************
  175.  *
  176.  *  BATTERY STATE - Once again, Win95 and Win98 and NT all do it differently
  177.  *
  178.  *****************************************************************************/
  179. //
  180. //  Values for SYSTEM_POWER_STATUS.ACLineStatus
  181. //
  182. #define SPSAC_OFFLINE       0
  183. #define SPSAC_ONLINE        1
  184. //
  185. //  Values for SYSTEM_POWER_STATUS.BatteryFlag
  186. //
  187. #define SPSBF_NOBATTERY     128
  188. //
  189. //  So many ways to detect batteries, so little time...
  190. //
  191. DWORD GetBatteryState()
  192. {
  193.     //
  194.     //  Since GMIB_HASBATTERY is cumulative (any battery turns it on)
  195.     //  and GMIB_ONBATTERY is subtractive (any AC turns it off), the
  196.     //  state you have to start in before you find a battery is
  197.     //  GMIB_HASBATTERY off and GMIB_ONBATTERY on.
  198.     //
  199.     //  dwResult & GMIB_ONBATTERY means we have yet to find AC power.
  200.     //  dwResult & GMIB_HASBATTERY means we have found a non-UPS battery.
  201.     //
  202.     DWORD dwResult = GMIB_ONBATTERY;
  203.     //------------------------------------------------------------------
  204.     //
  205.     //  First try - IOCTL_BATTERY_QUERY_INFORMATION
  206.     //
  207.     //------------------------------------------------------------------
  208.     //
  209.     //  Windows 98 and Windows 2000 support IOCTL_BATTERY_QUERY_INFORMATION,
  210.     //  which lets us enumerate the batteries and ask each one for information.
  211.     //  Except that on Windows 98, we can enumerate only ACPI batteries.
  212.     //  We still have to use VPOWERD to enumerate APM batteries.
  213.     //  BUGBUG -- deal with Win98 APM batteries
  214.     HDEVINFO hdev = SetupDiGetClassDevs(&GUID_DEVICE_BATTERY, 0, 0,
  215.                         DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
  216.     if (hdev != INVALID_HANDLE_VALUE) {
  217.         SP_DEVICE_INTERFACE_DATA did;
  218.         did.cbSize = sizeof(did);
  219.         // Stop at 100 batteries so we don't go haywire
  220.         for (int idev = 0; idev < 100; idev++) {
  221.             // Pre-set the error code because our DLLLOAD wrapper doesn't
  222.             // and Windows NT 4 supports SetupDiGetClassDevs but not
  223.             // SetupDiEnumDeviceInterfaces (go figure).
  224.             SetLastError(ERROR_NO_MORE_ITEMS);
  225.             if (SetupDiEnumDeviceInterfaces(hdev, 0, &GUID_DEVICE_BATTERY, idev, &did)) {
  226.                 DWORD cbRequired = 0;
  227.                 /*
  228.                  *  Ask for the required size then allocate it then fill it.
  229.                  *
  230.                  *  Sigh.  Windows NT and Windows 98 implement
  231.                  *  SetupDiGetDeviceInterfaceDetail differently if you are
  232.                  *  querying for the buffer size.
  233.                  *
  234.                  *  Windows 98 returns FALSE, and GetLastError() returns
  235.                  *  ERROR_INSUFFICIENT_BUFFER.
  236.                  *
  237.                  *  Windows NT returns TRUE.
  238.                  *
  239.                  *  So we allow the cases either where the call succeeds or
  240.                  *  the call fails with ERROR_INSUFFICIENT_BUFFER.
  241.                  */
  242.                 if (SetupDiGetDeviceInterfaceDetail(hdev, &did, 0, 0, &cbRequired, 0) ||
  243.                     GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
  244.                     PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd;
  245.                     pdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, cbRequired);
  246.                     if (pdidd) {
  247.                         pdidd->cbSize = sizeof(*pdidd);
  248.                         if (SetupDiGetDeviceInterfaceDetail(hdev, &did, pdidd, cbRequired, &cbRequired, 0)) {
  249.                             /*
  250.                              *  Finally enumerated a battery.  Ask it for information.
  251.                              */
  252.                             HANDLE hBattery = CreateFile(pdidd->DevicePath,
  253.                                                          GENERIC_READ | GENERIC_WRITE,
  254.                                                          FILE_SHARE_READ | FILE_SHARE_WRITE,
  255.                                                          NULL, OPEN_EXISTING,
  256.                                                          FILE_ATTRIBUTE_NORMAL, NULL);
  257.                             if (hBattery != INVALID_HANDLE_VALUE) {
  258.                                 /*
  259.                                  *  Now you have to ask the battery for its tag.
  260.                                  */
  261.                                 BATTERY_QUERY_INFORMATION bqi;
  262.                                 DWORD dwWait = 0;
  263.                                 DWORD dwOut;
  264.                                 bqi.BatteryTag = 0;
  265.                                 if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_TAG,
  266.                                                     &dwWait, sizeof(dwWait),
  267.                                                     &bqi.BatteryTag, sizeof(bqi.BatteryTag),
  268.                                                     &dwOut, NULL) && bqi.BatteryTag) {
  269.                                     /*
  270.                                      *  With the tag, you can query the battery info.
  271.                                      */
  272.                                     BATTERY_INFORMATION bi;
  273.                                     bqi.InformationLevel = BatteryInformation;
  274.                                     bqi.AtRate = 0;
  275.                                     if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION,
  276.                                                         &bqi, sizeof(bqi),
  277.                                                         &bi,  sizeof(bi),
  278.                                                         &dwOut, NULL)) {
  279.                                         // Only system batteries count
  280.                                         if (bi.Capabilities & BATTERY_SYSTEM_BATTERY)  {
  281.                                             if (!(bi.Capabilities & BATTERY_IS_SHORT_TERM)) {
  282.                                                 dwResult |= GMIB_HASBATTERY;
  283.                                             }
  284.                                             /*
  285.                                              *  And then query the battery status.
  286.                                              */
  287.                                             BATTERY_WAIT_STATUS bws;
  288.                                             BATTERY_STATUS bs;
  289.                                             ZeroMemory(&bws, sizeof(bws));
  290.                                             bws.BatteryTag = bqi.BatteryTag;
  291.                                             if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS,
  292.                                                                 &bws, sizeof(bws),
  293.                                                                 &bs,  sizeof(bs),
  294.                                                                 &dwOut, NULL)) {
  295.                                                 if (bs.PowerState & BATTERY_POWER_ON_LINE) {
  296.                                                     dwResult &= ~GMIB_ONBATTERY;
  297.                                                 }
  298.                                             }
  299.                                         }
  300.                                     }
  301.                                 }
  302.                                 CloseHandle(hBattery);
  303.                             }
  304.                         }
  305.                         LocalFree(pdidd);
  306.                     }
  307.                 }
  308.             } else {
  309.                 // Enumeration failed - perhaps we're out of items
  310.                 if (GetLastError() == ERROR_NO_MORE_ITEMS)
  311.                     break;
  312.             }
  313.         }
  314.         SetupDiDestroyDeviceInfoList(hdev);
  315.     }
  316.     //
  317.     //  On Windows NT, SetupDi tells us everything there is to know.
  318.     //  So once you get this far, you're finished.
  319.     //
  320.     if (g_bRunningOnNT) {
  321.         goto finish;
  322.     }
  323.     //------------------------------------------------------------------
  324.     //
  325.     //  Second try - GetSystemPowerStatus
  326.     //
  327.     //------------------------------------------------------------------
  328.     //
  329.     //  On Windows 9x, GetSystemPowerStatus enumerates a disjoint set of
  330.     //  batteries from SetupDi, so it's worth calling to find out.
  331.     //
  332.     SYSTEM_POWER_STATUS status;
  333.     if (GetSystemPowerStatus(&status)) {
  334.         //
  335.         //  HACKHACK:   Some APM BIOS implementations set BatteryFlag = 0
  336.         //              instead of 128 or 255 when they don't have a
  337.         //              battery, so we have to check both.
  338.         //
  339.         if (status.BatteryFlag != 0 &&
  340.             !(status.BatteryFlag & SPSBF_NOBATTERY)) {
  341.             //
  342.             // Found an APM battery.
  343.             //
  344.             dwResult |= GMIB_HASBATTERY;
  345.         }
  346.         if (status.ACLineStatus == SPSAC_ONLINE) {
  347.             dwResult &= ~GMIB_ONBATTERY;
  348.         }
  349.     }
  350. #ifdef TRY_NtPowerInformation // Hopefully the Third Try won't be necessary
  351.     SYSTEM_POWER_CAPABILITIES caps;
  352.     //------------------------------------------------------------------
  353.     //
  354.     //  Third try - NtPowerInformation
  355.     //
  356.     //------------------------------------------------------------------
  357.     //
  358.     //  NtPowerInformation is supported on Win98 and Windows 2000, but not
  359.     //  Windows 95 or NT4.
  360.     //
  361.     if (SUCCEEDED(NtPowerInformation(SystemPowerCapabilities, NULL, 0,
  362.                                      &caps, sizeof(caps)))) {
  363.         if (caps.BatteriesAreShortTerm) {
  364.             #error futz futz
  365.         }
  366.         if (caps.SystemBatteriesPresent && !fFoundUPS) {
  367.             #error futz futz
  368.         }
  369.     }
  370. #endif // TRY_NtPowerInformation
  371. finish:
  372.     //
  373.     //  Final cleanup:  If we didn't find a battery, then presume that we
  374.     //  are on AC power.
  375.     //
  376.     if (!(dwResult & GMIB_HASBATTERY))
  377.         dwResult &= ~GMIB_ONBATTERY;
  378.     return dwResult;
  379. }
  380. /*****************************************************************************
  381.  *
  382.  *  TERMINAL SERVER CLIENT
  383.  *
  384.  *  This is particularly gruesome because Terminal Server for NT4 SP3 goes
  385.  *  to extraordinary lengths to prevent you from detecting it.  Even the
  386.  *  semi-documented NtCurrentPeb()->SessionId trick doesn't work on NT4 SP3.
  387.  *  So we have to go to the totally undocumented winsta.dll to find out.
  388.  *
  389.  *****************************************************************************/
  390. BOOL g_fTSClient = -1;  // Tri-state, 0 = no, 1 = yes, -1 = don't know
  391. BOOL IsTSClientNT4(void)
  392. {
  393.     BOOL fTS = FALSE;       // Assume not
  394.     HINSTANCE hinstWinSta = LoadLibrary("winsta.dll");
  395.     if (hinstWinSta) {
  396.         PWINSTATIONQUERYINFORMATIONW WinStationQueryInformationW;
  397.         WINSTATIONINFORMATIONW wi;
  398.         WinStationQueryInformationW = (PWINSTATIONQUERYINFORMATIONW)
  399.                     GetProcAddress(hinstWinSta, "WinStationQueryInformationW");
  400.         if (WinStationQueryInformationW &&
  401.             WinStationQueryInformationW(SERVERNAME_CURRENT, LOGONID_CURRENT, WinStationInformation, &wi, sizeof(wi), NULL) &&
  402.             wi.LogonId != 0) {
  403.             fTS = TRUE;
  404.         }
  405.         FreeLibrary(hinstWinSta);
  406.     }
  407.     return fTS;
  408. }
  409. BOOL IsTSClient(void)
  410. {
  411.     if (!g_bRunningOnNT) {
  412.         // Windows 9x doesn't support Terminal Server
  413.         return FALSE;
  414.     } else if (g_bRunningOnNT5OrHigher) {
  415.         // NT5 has a new system metric to detect this
  416.         return GetSystemMetrics(SM_REMOTESESSION);
  417.     } else {
  418.         // NT4 is gross and evil.  This is slow, so cache the result.
  419.         if (g_fTSClient < 0)
  420.             g_fTSClient = IsTSClientNT4();
  421.         return g_fTSClient;
  422.     }
  423. }
  424. /*****************************************************************************
  425.  *
  426.  *  SHGetMachineInfo
  427.  *
  428.  *****************************************************************************/
  429. //
  430. //  SHGetMachineInfo
  431. //
  432. //  Given an index, returns some info about that index.  See shlwapi.w
  433. //  for documentation on the flags available.
  434. //
  435. STDAPI_(DWORD_PTR) SHGetMachineInfo(UINT gmi)
  436. {
  437.     switch (gmi) {
  438.     case GMI_DOCKSTATE:
  439.         return GetDockedState();
  440.     case GMI_BATTERYSTATE:
  441.         return GetBatteryState();
  442.     //
  443.     //  It smell like a laptop if it has a battery or if it can be docked.
  444.     //
  445.     case GMI_LAPTOP:
  446.         return (GetBatteryState() & GMIB_HASBATTERY) ||
  447.                (GetDockedState() != GMID_NOTDOCKABLE);
  448.     case GMI_TSCLIENT:
  449.         return IsTSClient();
  450.     }
  451.     TraceMsg(DM_WARNING, "SHGetMachineInfo: Unknown info query %d", gmi);
  452.     return 0;
  453. }
  454. #else
  455. STDAPI_(DWORD_PTR) SHGetMachineInfo(UINT gmi)
  456. {
  457.     // IEUNIX : Stubbed out this api to resolve undefind symbol in linking.
  458.     TraceMsg(DM_WARNING, "SHGetMachineInfo: Unknown info query %d", gmi);
  459.     return 0;
  460. }
  461. #endif