Source code of Windows XP (NT5)
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.

618 lines
16 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: update.c
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "newdevp.h"
  11. #define INSTALL_UI_TIMERID 1423
  12. PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo={0};
  13. BOOL
  14. RegistryDeviceName(
  15. DEVINST DevInst,
  16. PTCHAR Buffer,
  17. DWORD cbBuffer
  18. )
  19. {
  20. ULONG ulSize;
  21. CONFIGRET ConfigRet;
  22. //
  23. // Try the registry for FRIENDLYNAME
  24. //
  25. ulSize = cbBuffer;
  26. ConfigRet = CM_Get_DevNode_Registry_Property(DevInst,
  27. CM_DRP_FRIENDLYNAME,
  28. NULL,
  29. Buffer,
  30. &ulSize,
  31. 0);
  32. if (ConfigRet == CR_SUCCESS && *Buffer) {
  33. return TRUE;
  34. }
  35. //
  36. // Try the registry for DEVICEDESC
  37. //
  38. ulSize = cbBuffer;
  39. ConfigRet = CM_Get_DevNode_Registry_Property(DevInst,
  40. CM_DRP_DEVICEDESC,
  41. NULL,
  42. Buffer,
  43. &ulSize,
  44. 0);
  45. if (ConfigRet == CR_SUCCESS && *Buffer) {
  46. return TRUE;
  47. }
  48. return FALSE;
  49. }
  50. //
  51. // returns TRUE if we were able to find a reasonable name
  52. // (something besides unknown device).
  53. //
  54. void
  55. SetDriverDescription(
  56. HWND hDlg,
  57. int iControl,
  58. PNEWDEVWIZ NewDevWiz
  59. )
  60. {
  61. CONFIGRET ConfigRet;
  62. ULONG ulSize;
  63. PTCHAR FriendlyName;
  64. PTCHAR Location;
  65. SP_DRVINFO_DATA DriverInfoData;
  66. //
  67. // If there is a selected driver use its driver description,
  68. // since this is what the user is going to install.
  69. //
  70. DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
  71. if (SetupDiGetSelectedDriver(NewDevWiz->hDeviceInfo,
  72. &NewDevWiz->DeviceInfoData,
  73. &DriverInfoData
  74. )
  75. &&
  76. *DriverInfoData.Description) {
  77. wcscpy(NewDevWiz->DriverDescription, DriverInfoData.Description);
  78. SetDlgItemText(hDlg, iControl, NewDevWiz->DriverDescription);
  79. return;
  80. }
  81. FriendlyName = BuildFriendlyName(NewDevWiz->DeviceInfoData.DevInst, FALSE, NULL);
  82. if (FriendlyName) {
  83. SetDlgItemText(hDlg, iControl, FriendlyName);
  84. LocalFree(FriendlyName);
  85. return;
  86. }
  87. SetDlgItemText(hDlg, iControl, szUnknown);
  88. return;
  89. }
  90. /*
  91. * Intializes\Updates the global ProcessDeviceMapInfo which is used
  92. * by GetNextDriveByType().
  93. *
  94. * WARNING: NOT multithread safe!
  95. */
  96. BOOL
  97. IntializeDeviceMapInfo(
  98. void
  99. )
  100. {
  101. NTSTATUS Status;
  102. Status = NtQueryInformationProcess(NtCurrentProcess(),
  103. ProcessDeviceMap,
  104. &ProcessDeviceMapInfo.Query,
  105. sizeof(ProcessDeviceMapInfo.Query),
  106. NULL
  107. );
  108. if (!NT_SUCCESS(Status)) {
  109. RtlZeroMemory(&ProcessDeviceMapInfo, sizeof(ProcessDeviceMapInfo));
  110. return FALSE;
  111. }
  112. return TRUE;
  113. }
  114. UINT
  115. GetNextDriveByType(
  116. UINT DriveType,
  117. UINT DriveNumber
  118. )
  119. /*++
  120. Routine Description:
  121. Inspects each drive starting from DriveNumber in ascending order to find the
  122. first drive of the specified DriveType from the global ProcessDeviceMapInfo.
  123. The ProcessDeviceMapInfo must have been intialized and may need refreshing before
  124. invoking this function. Invoke IntializeDeviceMapInfo to initialize or update
  125. the DeviceMapInfo.
  126. Arguments:
  127. DriveType - DriveType as defined in winbase, GetDriveType().
  128. DriveNumber - Starting DriveNumber, 1 based.
  129. Return Value:
  130. DriveNumber - if nonzero Drive found, 1 based.
  131. --*/
  132. {
  133. //
  134. // OneBased DriveNumber to ZeroBased.
  135. //
  136. DriveNumber--;
  137. while (DriveNumber < 26) {
  138. if ((ProcessDeviceMapInfo.Query.DriveMap & (1<< DriveNumber)) &&
  139. ProcessDeviceMapInfo.Query.DriveType[DriveNumber] == DriveType) {
  140. return DriveNumber+1; // return 1 based DriveNumber found.
  141. }
  142. DriveNumber++;
  143. }
  144. return 0;
  145. }
  146. //
  147. // This function takes a fully qualified path name and returns a pointer to
  148. // the begining of the path portion.
  149. //
  150. // e.g. \\server\pathpart
  151. // d:\pathpart
  152. //
  153. // This function will always return a valid pointer, provided
  154. // a valid pointer was passed in. If the caller passes in a badly
  155. // formed pathname or an unknown format, where path ends up pointing is
  156. // unknown, except that it will be someplace within the string.
  157. //
  158. WCHAR *
  159. GetPathPart(
  160. WCHAR *Path
  161. )
  162. {
  163. //
  164. // We assume that we are being passed a fully qualified path name
  165. //
  166. //
  167. // check for UNC path.
  168. //
  169. if (*Path == L'\\') {
  170. Path += 2; // skip double backslash
  171. }
  172. //
  173. // if UNC Path points to beg of server name
  174. // if drive letter format, points to the drive letter
  175. //
  176. while (*Path && *Path++ != L'\\') {
  177. ;
  178. }
  179. return Path;
  180. }
  181. void
  182. InstallSilentChildSiblings(
  183. HWND hwndParent,
  184. PNEWDEVWIZ NewDevWiz,
  185. DEVINST DeviceInstance,
  186. BOOL ReinstallAll
  187. )
  188. {
  189. CONFIGRET ConfigRet;
  190. DEVINST ChildDeviceInstance;
  191. ULONG Ulong, ulValue;
  192. BOOL NeedsInstall, IsSilent;
  193. do {
  194. //
  195. // If this device instance needs installing and is silent then install it,
  196. // and its children.
  197. //
  198. IsSilent = FALSE;
  199. if (!ReinstallAll) {
  200. Ulong = sizeof(ulValue);
  201. ConfigRet = CM_Get_DevNode_Registry_Property_Ex(DeviceInstance,
  202. CM_DRP_CAPABILITIES,
  203. NULL,
  204. (PVOID)&ulValue,
  205. &Ulong,
  206. 0,
  207. NULL
  208. );
  209. if (ConfigRet == CR_SUCCESS && (ulValue & CM_DEVCAP_SILENTINSTALL)) {
  210. IsSilent = TRUE;
  211. }
  212. }
  213. if (IsSilent || ReinstallAll) {
  214. Ulong = sizeof(ulValue);
  215. ConfigRet = CM_Get_DevNode_Registry_Property_Ex(DeviceInstance,
  216. CM_DRP_CONFIGFLAGS,
  217. NULL,
  218. (PVOID)&ulValue,
  219. &Ulong,
  220. 0,
  221. NULL
  222. );
  223. if (ConfigRet == CR_SUCCESS && (ulValue & CONFIGFLAG_FINISH_INSTALL)) {
  224. NeedsInstall = TRUE;
  225. } else {
  226. ConfigRet = CM_Get_DevNode_Status(&Ulong,
  227. &ulValue,
  228. DeviceInstance,
  229. 0
  230. );
  231. NeedsInstall = ConfigRet == CR_SUCCESS &&
  232. (ulValue == CM_PROB_REINSTALL ||
  233. ulValue == CM_PROB_NOT_CONFIGURED
  234. );
  235. }
  236. if (NeedsInstall) {
  237. TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
  238. ConfigRet = CM_Get_Device_ID(DeviceInstance,
  239. DeviceInstanceId,
  240. sizeof(DeviceInstanceId)/sizeof(TCHAR),
  241. 0
  242. );
  243. if (ConfigRet == CR_SUCCESS) {
  244. if (InstallDevInst(hwndParent,
  245. DeviceInstanceId,
  246. FALSE, // only for found new.
  247. &Ulong
  248. )) {
  249. NewDevWiz->Reboot |= Ulong;
  250. }
  251. //
  252. // If this devinst has children, then recurse to install them as well.
  253. //
  254. ConfigRet = CM_Get_Child_Ex(&ChildDeviceInstance,
  255. DeviceInstance,
  256. 0,
  257. NULL
  258. );
  259. if (ConfigRet == CR_SUCCESS) {
  260. InstallSilentChildSiblings(hwndParent, NewDevWiz, ChildDeviceInstance, ReinstallAll);
  261. }
  262. }
  263. }
  264. }
  265. //
  266. // Next sibling ...
  267. //
  268. ConfigRet = CM_Get_Sibling_Ex(&DeviceInstance,
  269. DeviceInstance,
  270. 0,
  271. NULL
  272. );
  273. } while (ConfigRet == CR_SUCCESS);
  274. }
  275. void
  276. InstallSilentChilds(
  277. HWND hwndParent,
  278. PNEWDEVWIZ NewDevWiz
  279. )
  280. {
  281. CONFIGRET ConfigRet;
  282. DEVINST ChildDeviceInstance;
  283. ConfigRet = CM_Get_Child_Ex(&ChildDeviceInstance,
  284. NewDevWiz->DeviceInfoData.DevInst,
  285. 0,
  286. NULL
  287. );
  288. if (ConfigRet == CR_SUCCESS) {
  289. InstallSilentChildSiblings(hwndParent, NewDevWiz, ChildDeviceInstance, FALSE);
  290. }
  291. }
  292. void
  293. SendMessageToUpdateBalloonInfo(
  294. PTSTR DeviceDesc
  295. )
  296. {
  297. HWND hBalloonInfoWnd;
  298. COPYDATASTRUCT cds;
  299. hBalloonInfoWnd = FindWindow(NEWDEV_CLASS_NAME, NULL);
  300. if (hBalloonInfoWnd) {
  301. cds.dwData = 0;
  302. cds.cbData = (lstrlen(DeviceDesc) + 1) * sizeof(TCHAR);
  303. cds.lpData = DeviceDesc;
  304. SendMessage(hBalloonInfoWnd, WM_COPYDATA, 0, (LPARAM)&cds);
  305. }
  306. }
  307. void
  308. UpdateBalloonInfo(
  309. HWND hWnd,
  310. PTSTR DeviceDesc OPTIONAL,
  311. DEVINST DevInst OPTIONAL,
  312. HICON hNewDevIcon,
  313. BOOL bPlaySound
  314. )
  315. {
  316. PTCHAR FriendlyName = NULL;
  317. NOTIFYICONDATA nid = { sizeof(nid), hWnd, 0 };
  318. nid.uID = 1;
  319. if (DeviceDesc || DevInst) {
  320. if (DeviceDesc) {
  321. //
  322. // First use the DeviceDesc string that is passed into this API
  323. //
  324. lstrcpy(nid.szInfo, DeviceDesc);
  325. } else if ((FriendlyName = BuildFriendlyName(DevInst, TRUE, NULL)) != NULL) {
  326. //
  327. // If no DeviceDesc string was passed in then use the DevInst to get
  328. // the Device's FriendlyName or DeviceDesc property
  329. //
  330. lstrcpy(nid.szInfo, FriendlyName);
  331. LocalFree(FriendlyName);
  332. } else {
  333. //
  334. // If we could not get a friendly name for the device or no device was specified
  335. // so just display the Searching... text.
  336. //
  337. LoadString(hNewDev, IDS_NEWSEARCH, nid.szInfo, SIZECHARS(nid.szInfo));
  338. }
  339. nid.uFlags = NIF_INFO;
  340. nid.uTimeout = 60000;
  341. nid.dwInfoFlags = NIIF_INFO | (bPlaySound ? 0 : NIIF_NOSOUND);
  342. LoadString(hNewDev, IDS_FOUNDNEWHARDWARE, nid.szInfoTitle, SIZECHARS(nid.szInfoTitle));
  343. Shell_NotifyIcon(NIM_MODIFY, &nid);
  344. }
  345. }
  346. LRESULT CALLBACK
  347. BalloonInfoProc(
  348. HWND hWnd,
  349. UINT message,
  350. WPARAM wParam,
  351. LPARAM lParam
  352. )
  353. {
  354. static HICON hNewDevIcon = NULL;
  355. static BOOL bCanExit;
  356. NOTIFYICONDATA nid;
  357. switch (message) {
  358. case WM_CREATE:
  359. hNewDevIcon = LoadImage(hNewDev,
  360. MAKEINTRESOURCE(IDI_NEWDEVICEICON),
  361. IMAGE_ICON,
  362. GetSystemMetrics(SM_CXSMICON),
  363. GetSystemMetrics(SM_CYSMICON),
  364. 0
  365. );
  366. ZeroMemory(&nid, sizeof(nid));
  367. nid.cbSize = sizeof(nid);
  368. nid.hWnd = hWnd;
  369. nid.uID = 1;
  370. nid.hIcon = hNewDevIcon;
  371. nid.uVersion = NOTIFYICON_VERSION;
  372. Shell_NotifyIcon(NIM_SETVERSION, &nid);
  373. nid.uFlags = NIF_ICON;
  374. Shell_NotifyIcon(NIM_ADD, &nid);
  375. //
  376. // We want the tray icon to be displayed for at least 3 seconds otherwise it flashes too
  377. // quickly and a user can't see it.
  378. //
  379. bCanExit = FALSE;
  380. SetTimer(hWnd, INSTALL_UI_TIMERID, 3000, NULL);
  381. break;
  382. case WM_DESTROY: {
  383. ZeroMemory(&nid, sizeof(nid));
  384. nid.cbSize = sizeof(nid);
  385. nid.hWnd = hWnd;
  386. nid.uID = 1;
  387. Shell_NotifyIcon(NIM_DELETE, &nid);
  388. if (hNewDevIcon) {
  389. DestroyIcon(hNewDevIcon);
  390. }
  391. break;
  392. }
  393. case WM_TIMER:
  394. if (INSTALL_UI_TIMERID == wParam) {
  395. //
  396. // At this point the tray icon has been displayed for at least 10 seconds so we can
  397. // exit whenever we are finished. If bCanExit is already TRUE then we have
  398. // already been asked to exit so just do a DestroyWindow at this point, otherwise
  399. // set bCanExit to TRUE so we can exit when we are finished installing devices.
  400. //
  401. if (bCanExit) {
  402. DestroyWindow(hWnd);
  403. } else {
  404. KillTimer(hWnd, INSTALL_UI_TIMERID);
  405. bCanExit = TRUE;
  406. }
  407. }
  408. break;
  409. case WUM_UPDATEUI:
  410. if (wParam & TIP_HIDE_BALLOON) {
  411. //
  412. // Hide the balloon.
  413. //
  414. NOTIFYICONDATA nid = { sizeof(nid), hWnd, 0 };
  415. nid.uID = 1;
  416. nid.uFlags = NIF_INFO;
  417. nid.uTimeout = 0;
  418. nid.dwInfoFlags = NIIF_INFO;
  419. Shell_NotifyIcon(NIM_MODIFY, &nid);
  420. } else if (wParam & TIP_LPARAM_IS_DEVICEINSTANCEID) {
  421. //
  422. // The lParam is a DeviceInstanceID. Convert it to a devnode
  423. // and then call UpdateBalloonInfo.
  424. //
  425. DEVINST DevInst = 0;
  426. if (lParam &&
  427. (CM_Locate_DevNode(&DevInst,
  428. (PTSTR)lParam,
  429. CM_LOCATE_DEVNODE_NORMAL
  430. ) == CR_SUCCESS)) {
  431. UpdateBalloonInfo(hWnd,
  432. NULL,
  433. DevInst,
  434. hNewDevIcon,
  435. (wParam & TIP_PLAY_SOUND) ? TRUE : FALSE
  436. );
  437. }
  438. } else {
  439. //
  440. // The lParam is plain text (device description). Send it directly
  441. // to UpdateBalloonInfo.
  442. //
  443. UpdateBalloonInfo(hWnd,
  444. (PTSTR)lParam,
  445. 0,
  446. hNewDevIcon,
  447. (wParam & TIP_PLAY_SOUND) ? TRUE : FALSE
  448. );
  449. }
  450. break;
  451. case WM_COPYDATA:
  452. {
  453. //
  454. // This is the case where we needed to launch another instance of newdev.dll with Admin
  455. // credentials to do the actuall device install. In order for it to update the UI it
  456. // will send the main newdev.dll a WM_COPYDATA message which will contain the string
  457. // to display in the balloon tooltip.
  458. //
  459. PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT)lParam;
  460. if (pcds && pcds->lpData) {
  461. //
  462. // We assume that the lParam is plain text since the main newdev.dll updated the balloon
  463. // initially with the DeviceDesc.
  464. //
  465. UpdateBalloonInfo(hWnd, (PTSTR)pcds->lpData, 0, hNewDevIcon, FALSE);
  466. }
  467. break;
  468. }
  469. case WUM_EXIT:
  470. if (bCanExit) {
  471. DestroyWindow(hWnd);
  472. } else {
  473. ShowWindow(hWnd, SW_SHOW);
  474. bCanExit = TRUE;
  475. }
  476. break;
  477. default:
  478. break;
  479. }
  480. return DefWindowProc(hWnd, message, wParam, lParam);
  481. }