Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
11 KiB

  1. //
  2. // machinfo.cpp - SHGetMachineInfo and related functions
  3. //
  4. //
  5. #include "priv.h"
  6. #include <dbt.h>
  7. #include <cfgmgr32.h>
  8. #include <batclass.h>
  9. const GUID GUID_DEVICE_BATTERY = { 0x72631e54L, 0x78A4, 0x11d0,
  10. { 0xbc, 0xf7, 0x00, 0xaa, 0x00, 0xb7, 0xb3, 0x2a } };
  11. /*****************************************************************************
  12. *
  13. * DOCK STATE - Win95, Win98, and WinNT all do this differently (yuck)
  14. *
  15. *****************************************************************************/
  16. C_ASSERT(GMID_NOTDOCKABLE == CM_HWPI_NOT_DOCKABLE);
  17. C_ASSERT(GMID_UNDOCKED == CM_HWPI_UNDOCKED);
  18. C_ASSERT(GMID_DOCKED == CM_HWPI_DOCKED);
  19. DWORD GetDockedStateNT()
  20. {
  21. HW_PROFILE_INFO hpi;
  22. DWORD Result = GMID_NOTDOCKABLE; // assume the worst
  23. if (GetCurrentHwProfile(&hpi)) {
  24. Result = hpi.dwDockInfo & (DOCKINFO_UNDOCKED | DOCKINFO_DOCKED);
  25. // Wackiness: If the machine does not support docking, then
  26. // NT returns >both< flags set. Go figure.
  27. if (Result == (DOCKINFO_UNDOCKED | DOCKINFO_DOCKED)) {
  28. Result = GMID_NOTDOCKABLE;
  29. }
  30. } else {
  31. TraceMsg(DM_WARNING, "GetDockedStateNT: GetCurrentHwProfile failed");
  32. }
  33. return Result;
  34. }
  35. //
  36. // Platforms that do not support Win95/Win98 can just call the NT version.
  37. //
  38. #define GetDockedState() GetDockedStateNT()
  39. /*****************************************************************************
  40. *
  41. * BATTERY STATE - Once again, Win95 and Win98 and NT all do it differently
  42. *
  43. *****************************************************************************/
  44. //
  45. // Values for SYSTEM_POWER_STATUS.ACLineStatus
  46. //
  47. #define SPSAC_OFFLINE 0
  48. #define SPSAC_ONLINE 1
  49. //
  50. // Values for SYSTEM_POWER_STATUS.BatteryFlag
  51. //
  52. #define SPSBF_NOBATTERY 128
  53. //
  54. // So many ways to detect batteries, so little time...
  55. //
  56. DWORD GetBatteryState()
  57. {
  58. //
  59. // Since GMIB_HASBATTERY is cumulative (any battery turns it on)
  60. // and GMIB_ONBATTERY is subtractive (any AC turns it off), the
  61. // state you have to start in before you find a battery is
  62. // GMIB_HASBATTERY off and GMIB_ONBATTERY on.
  63. //
  64. // dwResult & GMIB_ONBATTERY means we have yet to find AC power.
  65. // dwResult & GMIB_HASBATTERY means we have found a non-UPS battery.
  66. //
  67. DWORD dwResult = GMIB_ONBATTERY;
  68. //------------------------------------------------------------------
  69. //
  70. // First try - IOCTL_BATTERY_QUERY_INFORMATION
  71. //
  72. //------------------------------------------------------------------
  73. //
  74. // Windows 98 and Windows 2000 support IOCTL_BATTERY_QUERY_INFORMATION,
  75. // which lets us enumerate the batteries and ask each one for information.
  76. // Except that on Windows 98, we can enumerate only ACPI batteries.
  77. // We still have to use VPOWERD to enumerate APM batteries.
  78. // FEATURE -- deal with Win98 APM batteries
  79. HDEVINFO hdev = SetupDiGetClassDevs(&GUID_DEVICE_BATTERY, 0, 0,
  80. DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
  81. if (hdev != INVALID_HANDLE_VALUE) {
  82. SP_DEVICE_INTERFACE_DATA did;
  83. did.cbSize = sizeof(did);
  84. // Stop at 100 batteries so we don't go haywire
  85. for (int idev = 0; idev < 100; idev++) {
  86. // Pre-set the error code because our DLLLOAD wrapper doesn't
  87. // and Windows NT 4 supports SetupDiGetClassDevs but not
  88. // SetupDiEnumDeviceInterfaces (go figure).
  89. SetLastError(ERROR_NO_MORE_ITEMS);
  90. if (SetupDiEnumDeviceInterfaces(hdev, 0, &GUID_DEVICE_BATTERY, idev, &did)) {
  91. DWORD cbRequired = 0;
  92. /*
  93. * Ask for the required size then allocate it then fill it.
  94. *
  95. * Sigh. Windows NT and Windows 98 implement
  96. * SetupDiGetDeviceInterfaceDetail differently if you are
  97. * querying for the buffer size.
  98. *
  99. * Windows 98 returns FALSE, and GetLastError() returns
  100. * ERROR_INSUFFICIENT_BUFFER.
  101. *
  102. * Windows NT returns TRUE.
  103. *
  104. * So we allow the cases either where the call succeeds or
  105. * the call fails with ERROR_INSUFFICIENT_BUFFER.
  106. */
  107. if (SetupDiGetDeviceInterfaceDetail(hdev, &did, 0, 0, &cbRequired, 0) ||
  108. GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
  109. PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd;
  110. pdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, cbRequired);
  111. if (pdidd) {
  112. pdidd->cbSize = sizeof(*pdidd);
  113. if (SetupDiGetDeviceInterfaceDetail(hdev, &did, pdidd, cbRequired, &cbRequired, 0)) {
  114. /*
  115. * Finally enumerated a battery. Ask it for information.
  116. */
  117. HANDLE hBattery = CreateFile(pdidd->DevicePath,
  118. GENERIC_READ | GENERIC_WRITE,
  119. FILE_SHARE_READ | FILE_SHARE_WRITE,
  120. NULL, OPEN_EXISTING,
  121. FILE_ATTRIBUTE_NORMAL, NULL);
  122. if (hBattery != INVALID_HANDLE_VALUE) {
  123. /*
  124. * Now you have to ask the battery for its tag.
  125. */
  126. BATTERY_QUERY_INFORMATION bqi;
  127. DWORD dwWait = 0;
  128. DWORD dwOut;
  129. bqi.BatteryTag = 0;
  130. if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_TAG,
  131. &dwWait, sizeof(dwWait),
  132. &bqi.BatteryTag, sizeof(bqi.BatteryTag),
  133. &dwOut, NULL) && bqi.BatteryTag) {
  134. /*
  135. * With the tag, you can query the battery info.
  136. */
  137. BATTERY_INFORMATION bi;
  138. bqi.InformationLevel = BatteryInformation;
  139. bqi.AtRate = 0;
  140. if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION,
  141. &bqi, sizeof(bqi),
  142. &bi, sizeof(bi),
  143. &dwOut, NULL)) {
  144. // Only system batteries count
  145. if (bi.Capabilities & BATTERY_SYSTEM_BATTERY) {
  146. if (!(bi.Capabilities & BATTERY_IS_SHORT_TERM)) {
  147. dwResult |= GMIB_HASBATTERY;
  148. }
  149. /*
  150. * And then query the battery status.
  151. */
  152. BATTERY_WAIT_STATUS bws;
  153. BATTERY_STATUS bs;
  154. ZeroMemory(&bws, sizeof(bws));
  155. bws.BatteryTag = bqi.BatteryTag;
  156. if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS,
  157. &bws, sizeof(bws),
  158. &bs, sizeof(bs),
  159. &dwOut, NULL)) {
  160. if (bs.PowerState & BATTERY_POWER_ON_LINE) {
  161. dwResult &= ~GMIB_ONBATTERY;
  162. }
  163. }
  164. }
  165. }
  166. }
  167. CloseHandle(hBattery);
  168. }
  169. }
  170. LocalFree(pdidd);
  171. }
  172. }
  173. } else {
  174. // Enumeration failed - perhaps we're out of items
  175. if (GetLastError() == ERROR_NO_MORE_ITEMS)
  176. break;
  177. }
  178. }
  179. SetupDiDestroyDeviceInfoList(hdev);
  180. }
  181. //
  182. // Final cleanup: If we didn't find a battery, then presume that we
  183. // are on AC power.
  184. //
  185. if (!(dwResult & GMIB_HASBATTERY))
  186. dwResult &= ~GMIB_ONBATTERY;
  187. return dwResult;
  188. }
  189. /*****************************************************************************
  190. *
  191. * TERMINAL SERVER CLIENT
  192. *
  193. * This is particularly gruesome because Terminal Server for NT4 SP3 goes
  194. * to extraordinary lengths to prevent you from detecting it. Even the
  195. * semi-documented NtCurrentPeb()->SessionId trick doesn't work on NT4 SP3.
  196. * So we have to go to the totally undocumented winsta.dll to find out.
  197. *
  198. *****************************************************************************/
  199. BOOL IsTSClient(void)
  200. {
  201. // NT5 has a new system metric to detect this
  202. return GetSystemMetrics(SM_REMOTESESSION);
  203. }
  204. /*****************************************************************************
  205. *
  206. * SHGetMachineInfo
  207. *
  208. *****************************************************************************/
  209. //
  210. // SHGetMachineInfo
  211. //
  212. // Given an index, returns some info about that index. See shlwapi.w
  213. // for documentation on the flags available.
  214. //
  215. STDAPI_(DWORD_PTR) SHGetMachineInfo(UINT gmi)
  216. {
  217. switch (gmi) {
  218. case GMI_DOCKSTATE:
  219. return GetDockedState();
  220. case GMI_BATTERYSTATE:
  221. return GetBatteryState();
  222. //
  223. // It smell like a laptop if it has a battery or if it can be docked.
  224. //
  225. case GMI_LAPTOP:
  226. return (GetBatteryState() & GMIB_HASBATTERY) ||
  227. (GetDockedState() != GMID_NOTDOCKABLE);
  228. case GMI_TSCLIENT:
  229. return IsTSClient();
  230. }
  231. TraceMsg(DM_WARNING, "SHGetMachineInfo: Unknown info query %d", gmi);
  232. return 0;
  233. }