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.

659 lines
16 KiB

  1. // DeviceConsole.cpp : Implementation of CDeviceConsole
  2. #include "stdafx.h"
  3. #include "DevCon2.h"
  4. #include "DeviceConsole.h"
  5. #include "Devices.h"
  6. #include "SetupClasses.h"
  7. #include "xStrings.h"
  8. #include "utils.h"
  9. #include <dbt.h>
  10. /////////////////////////////////////////////////////////////////////////////
  11. // CDeviceConsole
  12. //
  13. // a window is used for events to ensure that we dispatch the events
  14. // within the same apartment as the DeviceConsole object
  15. //
  16. LRESULT CDevConNotifyWindow::OnDeviceChange(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  17. {
  18. LRESULT ret = TRUE;
  19. bHandled = TRUE;
  20. switch(wParam) {
  21. case DBT_DEVNODES_CHANGED:
  22. //
  23. // post this so we don't block sender
  24. //
  25. PostMessage(UM_POSTGLOBALEVENT,wParam);
  26. return TRUE;
  27. default: ;
  28. }
  29. return ret;
  30. }
  31. LRESULT CDevConNotifyWindow::OnPostGlobalEvent(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  32. {
  33. LRESULT ret = TRUE;
  34. bHandled = TRUE;
  35. //
  36. // defer this global event to CDeviceConsole
  37. //
  38. if(m_pDevCon) {
  39. m_pDevCon->FireGlobalEvent(wParam);
  40. }
  41. return ret;
  42. }
  43. LRESULT CDevConNotifyWindow::OnPostEvents(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  44. {
  45. LRESULT ret = TRUE;
  46. bHandled = TRUE;
  47. //
  48. // handle all the pending events
  49. //
  50. return ret;
  51. }
  52. STDMETHODIMP CDeviceConsole::AllDevices(VARIANT flags, VARIANT machine, LPDISPATCH *pDevices)
  53. {
  54. CComVariant m;
  55. DWORD diflags = 0;
  56. HRESULT hr;
  57. LPCWSTR pMachine;
  58. HDEVINFO hDevInfo;
  59. *pDevices = NULL;
  60. hr = TranslateDeviceFlags(&flags,&diflags);
  61. if(FAILED(hr)) {
  62. return hr;
  63. }
  64. diflags |= DIGCF_ALLCLASSES;
  65. hr = GetOptionalString(&machine,m,&pMachine);
  66. if(FAILED(hr)) {
  67. return hr;
  68. }
  69. hDevInfo = SetupDiGetClassDevsEx(NULL,NULL,NULL,diflags,NULL,pMachine,NULL);
  70. if(hDevInfo == INVALID_HANDLE_VALUE) {
  71. DWORD Err = GetLastError();
  72. return HRESULT_FROM_SETUPAPI(Err);
  73. }
  74. return BuildDeviceList(hDevInfo,pDevices);
  75. }
  76. HRESULT CDeviceConsole::BuildDeviceList(HDEVINFO hDevInfo,LPDISPATCH *pDevices)
  77. {
  78. *pDevices = NULL;
  79. HRESULT hr;
  80. CComObject<CDevices> *d;
  81. hr = CComObject<CDevices>::CreateInstance(&d);
  82. if(FAILED(hr)) {
  83. SetupDiDestroyDeviceInfoList(hDevInfo);
  84. return hr;
  85. }
  86. CComPtr<IDevices> dPtr = d;
  87. hr = d->Init(hDevInfo,this);
  88. if(FAILED(hr)) {
  89. return hr;
  90. }
  91. *pDevices = dPtr.Detach();
  92. return S_OK;
  93. }
  94. STDMETHODIMP CDeviceConsole::CreateEmptyDeviceList(VARIANT machine, LPDISPATCH *pDevices)
  95. {
  96. CComVariant machine_v;
  97. LPCWSTR pMachine;
  98. HRESULT hr;
  99. HDEVINFO hDevInfo;
  100. DWORD Err;
  101. hr = GetOptionalString(&machine,machine_v,&pMachine);
  102. if(FAILED(hr)) {
  103. return hr;
  104. }
  105. hDevInfo = SetupDiCreateDeviceInfoListEx(NULL,
  106. NULL,
  107. pMachine,
  108. NULL);
  109. if(hDevInfo != INVALID_HANDLE_VALUE) {
  110. return BuildDeviceList(hDevInfo,pDevices);
  111. }
  112. Err = GetLastError();
  113. return HRESULT_FROM_SETUPAPI(Err);
  114. }
  115. typedef BOOL (WINAPI *UpdateDriverForPlugAndPlayDevicesProtoW)(HWND hwndParent,
  116. LPCWSTR HardwareId,
  117. LPCWSTR FullInfPath,
  118. DWORD InstallFlags,
  119. PBOOL bRebootRequired OPTIONAL
  120. );
  121. #define UPDATEDRIVERFORPLUGANDPLAYDEVICESW "UpdateDriverForPlugAndPlayDevicesW"
  122. STDMETHODIMP CDeviceConsole::UpdateDriver(BSTR infname, BSTR hwid, VARIANT op_flags)
  123. {
  124. HMODULE newdevMod = NULL;
  125. UpdateDriverForPlugAndPlayDevicesProtoW UpdateFnW;
  126. BOOL reboot = FALSE;
  127. DWORD flags = 0;
  128. WCHAR InfPath[MAX_PATH];
  129. HRESULT hr;
  130. CComVariant flags_v;
  131. //
  132. // op_flags are optional
  133. //
  134. if(V_VT(&op_flags)!=VT_ERROR) {
  135. hr = flags_v.ChangeType(VT_BSTR,&op_flags);
  136. if(FAILED(hr)) {
  137. return hr;
  138. }
  139. flags = V_I4(&flags_v);
  140. } else if (V_ERROR(&op_flags) != DISP_E_PARAMNOTFOUND) {
  141. hr = V_ERROR(&op_flags);
  142. return hr;
  143. }
  144. //
  145. // Inf must be a full pathname
  146. //
  147. if(GetFullPathName(infname,MAX_PATH,InfPath,NULL) >= MAX_PATH) {
  148. //
  149. // inf pathname too long
  150. //
  151. return E_INVALIDARG;
  152. }
  153. //
  154. // make use of UpdateDriverForPlugAndPlayDevices
  155. //
  156. newdevMod = LoadLibrary(TEXT("newdev.dll"));
  157. if(!newdevMod) {
  158. goto final;
  159. }
  160. UpdateFnW = (UpdateDriverForPlugAndPlayDevicesProtoW)GetProcAddress(newdevMod,UPDATEDRIVERFORPLUGANDPLAYDEVICESW);
  161. if(!UpdateFnW)
  162. {
  163. goto final;
  164. }
  165. if(!UpdateFnW(NULL,hwid,InfPath,flags,&reboot)) {
  166. DWORD Err = GetLastError();
  167. hr = HRESULT_FROM_SETUPAPI(Err);
  168. goto final;
  169. }
  170. if(reboot) {
  171. RebootRequired = VARIANT_TRUE;
  172. hr = S_FALSE;
  173. } else {
  174. hr = S_OK;
  175. }
  176. final:
  177. if(newdevMod) {
  178. FreeLibrary(newdevMod);
  179. }
  180. return hr;
  181. }
  182. STDMETHODIMP CDeviceConsole::CheckReboot()
  183. {
  184. WCHAR RebootText[MAX_PATH];
  185. WCHAR RebootCaption[MAX_PATH];
  186. if(!RebootRequired) {
  187. return S_OK;
  188. }
  189. int str = LoadString(GetModuleHandle(NULL),IDS_REBOOTREQ,RebootText,MAX_PATH);
  190. if(!str) {
  191. return E_UNEXPECTED;
  192. }
  193. str = LoadString(GetModuleHandle(NULL),IDS_REBOOTCAP,RebootCaption,MAX_PATH);
  194. if(!str) {
  195. return E_UNEXPECTED;
  196. }
  197. MessageBeep(MB_ICONSTOP);
  198. int mb = MessageBox(NULL,RebootText,RebootCaption,MB_YESNO|MB_ICONSTOP);
  199. if(mb == IDOK) {
  200. return RebootReasonHardware();
  201. }
  202. return S_FALSE;
  203. }
  204. STDMETHODIMP CDeviceConsole::RebootReasonHardware()
  205. {
  206. HANDLE Token;
  207. TOKEN_PRIVILEGES NewPrivileges;
  208. LUID Luid;
  209. //
  210. // On WinNT, need to "turn on" reboot privilege
  211. // if any of this fails, try reboot anyway
  212. //
  213. if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&Token)) {
  214. goto final;
  215. }
  216. if(!LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&Luid)) {
  217. CloseHandle(Token);
  218. goto final;
  219. }
  220. NewPrivileges.PrivilegeCount = 1;
  221. NewPrivileges.Privileges[0].Luid = Luid;
  222. NewPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  223. AdjustTokenPrivileges(
  224. Token,
  225. FALSE,
  226. &NewPrivileges,
  227. 0,
  228. NULL,
  229. NULL
  230. );
  231. CloseHandle(Token);
  232. final:
  233. //
  234. // attempt reboot - inform system that this is planned hardware install
  235. //
  236. return ExitWindowsEx(EWX_REBOOT, REASON_PLANNED_FLAG|REASON_HWINSTALL) ? S_OK : E_UNEXPECTED;
  237. }
  238. STDMETHODIMP CDeviceConsole::get_RebootRequired(VARIANT_BOOL *pVal)
  239. {
  240. *pVal = RebootRequired ? VARIANT_TRUE : VARIANT_FALSE;
  241. return S_OK;
  242. }
  243. STDMETHODIMP CDeviceConsole::put_RebootRequired(VARIANT_BOOL newVal)
  244. {
  245. RebootRequired = newVal ? VARIANT_TRUE : VARIANT_FALSE;
  246. return S_OK;
  247. }
  248. STDMETHODIMP CDeviceConsole::SetupClasses(VARIANT classList, VARIANT machine, LPDISPATCH *pDevices)
  249. {
  250. *pDevices = NULL;
  251. CComVariant m;
  252. HRESULT hr;
  253. LPCWSTR pMachine;
  254. hr = GetOptionalString(&machine,m,&pMachine);
  255. if(FAILED(hr)) {
  256. return hr;
  257. }
  258. CComObject<CSetupClasses> *d;
  259. hr = CComObject<CSetupClasses>::CreateInstance(&d);
  260. if(FAILED(hr)) {
  261. return hr;
  262. }
  263. CComPtr<ISetupClasses> dPtr = d;
  264. hr = d->Init(pMachine,this);
  265. if(FAILED(hr)) {
  266. return hr;
  267. }
  268. if(IsBlankString(&classList)) {
  269. hr = d->AllClasses();
  270. } else {
  271. hr = d->Add(classList);
  272. }
  273. if(FAILED(hr)) {
  274. return hr;
  275. }
  276. *pDevices = dPtr.Detach();
  277. return S_OK;
  278. }
  279. STDMETHODIMP CDeviceConsole::CreateEmptySetupClassList(VARIANT machine, LPDISPATCH *pResult)
  280. {
  281. *pResult = NULL;
  282. CComVariant m;
  283. HRESULT hr;
  284. LPCWSTR pMachine;
  285. hr = GetOptionalString(&machine,m,&pMachine);
  286. if(FAILED(hr)) {
  287. return hr;
  288. }
  289. CComObject<CSetupClasses> *d;
  290. hr = CComObject<CSetupClasses>::CreateInstance(&d);
  291. if(FAILED(hr)) {
  292. return hr;
  293. }
  294. CComPtr<ISetupClasses> dPtr = d;
  295. hr = d->Init(pMachine,this);
  296. if(FAILED(hr)) {
  297. return hr;
  298. }
  299. *pResult = dPtr.Detach();
  300. return S_OK;
  301. }
  302. STDMETHODIMP CDeviceConsole::DevicesBySetupClasses(VARIANT SetupClasses, VARIANT flags, VARIANT machine, LPDISPATCH *pDevices)
  303. {
  304. *pDevices = NULL;
  305. CComObject<CSetupClasses> *pClasses = NULL;
  306. CComVariant m;
  307. HRESULT hr;
  308. LPCWSTR pMachine;
  309. //
  310. // shorthand for initializing class collection
  311. // to get devices
  312. //
  313. hr = GetOptionalString(&machine,m,&pMachine);
  314. if(FAILED(hr)) {
  315. return hr;
  316. }
  317. hr = CComObject<CSetupClasses>::CreateInstance(&pClasses);
  318. if(FAILED(hr)) {
  319. return hr;
  320. }
  321. CComPtr<ISetupClasses> pClassesPtr = pClasses;
  322. hr = pClasses->Init(pMachine,this);
  323. if(FAILED(hr)) {
  324. return hr;
  325. }
  326. hr = pClasses->Add(SetupClasses);
  327. if(FAILED(hr)) {
  328. return hr;
  329. }
  330. hr = pClasses->Devices(flags,pDevices);
  331. return hr;
  332. }
  333. STDMETHODIMP CDeviceConsole::DevicesByInterfaceClasses(VARIANT InterfaceClasses, VARIANT machine, LPDISPATCH *pDevicesOut)
  334. {
  335. *pDevicesOut = NULL;
  336. //
  337. // similar to above, but for interface classes
  338. //
  339. CComObject<CStrings> *pStrings = NULL;
  340. CComObject<CDevices> *pDevices = NULL;
  341. CComVariant m;
  342. HRESULT hr;
  343. LPCWSTR pMachine;
  344. HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
  345. HDEVINFO hPrevDevInfo = NULL;
  346. DWORD Err;
  347. DWORD c;
  348. BSTR str;
  349. hr = GetOptionalString(&machine,m,&pMachine);
  350. if(FAILED(hr)) {
  351. return hr;
  352. }
  353. hr = CComObject<CStrings>::CreateInstance(&pStrings);
  354. if(FAILED(hr)) {
  355. return hr;
  356. }
  357. CComPtr<IStrings> pStringsPtr = pStrings;
  358. hr = pStrings->Add(InterfaceClasses);
  359. if(FAILED(hr)) {
  360. return hr;
  361. }
  362. for(c=0;pStrings->InternalEnum(c,&str);c++) {
  363. //
  364. // convert string to interface
  365. //
  366. GUID guid;
  367. hr = CLSIDFromString(str,&guid);
  368. if(FAILED(hr)) {
  369. return hr;
  370. }
  371. //
  372. // query present devices of interface
  373. //
  374. hDevInfo = SetupDiGetClassDevsEx(&guid,NULL,NULL,DIGCF_DEVICEINTERFACE|DIGCF_PRESENT,hPrevDevInfo,pMachine,NULL);
  375. if(hDevInfo == INVALID_HANDLE_VALUE) {
  376. Err = GetLastError();
  377. if(hPrevDevInfo) {
  378. SetupDiDestroyDeviceInfoList(hPrevDevInfo);
  379. }
  380. return HRESULT_FROM_SETUPAPI(Err);
  381. }
  382. hPrevDevInfo = hDevInfo;
  383. }
  384. if(hDevInfo == INVALID_HANDLE_VALUE) {
  385. return E_INVALIDARG;
  386. }
  387. //
  388. // now build resultant list
  389. //
  390. hr = CComObject<CDevices>::CreateInstance(&pDevices);
  391. if(FAILED(hr)) {
  392. SetupDiDestroyDeviceInfoList(hDevInfo);
  393. return hr;
  394. }
  395. CComPtr<IDevices> pDevicesPtr = pDevices;
  396. hr = pDevices->Init(hDevInfo,this);
  397. if(FAILED(hr)) {
  398. return hr;
  399. }
  400. *pDevicesOut = pDevicesPtr.Detach();
  401. return S_OK;
  402. }
  403. STDMETHODIMP CDeviceConsole::DevicesByInstanceIds(VARIANT InstanceIdList, VARIANT machine, LPDISPATCH *pDevices)
  404. {
  405. //
  406. // shorthand for CreateEmptyDeviceList followed by Add
  407. //
  408. HRESULT hr;
  409. LPDISPATCH Devices = NULL;
  410. CComQIPtr<IDevices> pIf;
  411. hr = CreateEmptyDeviceList(machine,&Devices);
  412. if(FAILED(hr)) {
  413. return hr;
  414. }
  415. pIf = Devices;
  416. if(!pIf) {
  417. return E_UNEXPECTED;
  418. }
  419. hr = pIf->Add(InstanceIdList);
  420. if(FAILED(hr)) {
  421. Devices->Release();
  422. return hr;
  423. }
  424. *pDevices = Devices;
  425. return S_OK;
  426. }
  427. STDMETHODIMP CDeviceConsole::StringList(VARIANT from, LPDISPATCH *pDest)
  428. {
  429. //
  430. // convinience only
  431. //
  432. *pDest = NULL;
  433. HRESULT hr;
  434. CComObject<CStrings> *pStrings = NULL;
  435. hr = CComObject<CStrings>::CreateInstance(&pStrings);
  436. if(FAILED(hr)) {
  437. return hr;
  438. }
  439. CComPtr<IStrings> pStringsPtr = pStrings;
  440. if(!IsNoArg(&from)) {
  441. hr = pStrings->Add(from);
  442. if(FAILED(hr)) {
  443. return hr;
  444. }
  445. }
  446. *pDest = pStringsPtr.Detach();
  447. return S_OK;
  448. }
  449. STDMETHODIMP CDeviceConsole::AttachEvent(/*[in]*/ BSTR eventName,/*[in]*/ LPDISPATCH handler,/*[out, retval]*/ VARIANT_BOOL *pOk)
  450. {
  451. *pOk = VARIANT_FALSE;
  452. if(!NotifyWindow()) {
  453. return E_OUTOFMEMORY;
  454. }
  455. return m_Events.AttachEvent(eventName,handler,pOk);
  456. }
  457. STDMETHODIMP CDeviceConsole::DetachEvent(/*[in]*/ BSTR eventName,/*[in]*/ LPDISPATCH handler,/*[out, retval]*/ VARIANT_BOOL *pOk)
  458. {
  459. return m_Events.DetachEvent(eventName,handler,pOk);
  460. }
  461. CDevConNotifyWindow *CDeviceConsole::NotifyWindow()
  462. {
  463. if(m_pNotifyWindow) {
  464. return m_pNotifyWindow;
  465. }
  466. m_pNotifyWindow = new CDevConNotifyWindow;
  467. if(!m_pNotifyWindow) {
  468. return NULL;
  469. }
  470. m_pNotifyWindow->m_pDevCon = this;
  471. RECT nil;
  472. nil.top = 0;
  473. nil.left = 0;
  474. nil.bottom = 8;
  475. nil.right = 8;
  476. if(!m_pNotifyWindow->Create(NULL,nil,NULL)) {
  477. delete m_pNotifyWindow;
  478. m_pNotifyWindow = NULL;
  479. return NULL;
  480. }
  481. return m_pNotifyWindow;
  482. }
  483. void CDeviceConsole::FireGlobalEvent(WPARAM wParam)
  484. {
  485. switch(wParam) {
  486. case DBT_DEVNODES_CHANGED:
  487. m_Events.Invoke(L"OnDeviceNodesChanged",0,NULL);
  488. break;
  489. }
  490. }
  491. HRESULT CEventsDispEntry::AttachEvent(LPDISPATCH pDisp,VARIANT_BOOL *pStatus)
  492. {
  493. try {
  494. *pStatus = VARIANT_FALSE;
  495. push_back(pDisp);
  496. *pStatus = VARIANT_TRUE;
  497. } catch(std::bad_alloc) {
  498. return E_OUTOFMEMORY;
  499. } catch(...) {
  500. return E_INVALIDARG;
  501. }
  502. return S_OK;
  503. }
  504. HRESULT CEventsDispEntry::DetachEvent(LPDISPATCH pDisp,VARIANT_BOOL *pStatus)
  505. {
  506. try {
  507. *pStatus = VARIANT_FALSE;
  508. remove(pDisp);
  509. *pStatus = VARIANT_TRUE;
  510. } catch(std::bad_alloc) {
  511. return E_OUTOFMEMORY;
  512. } catch(...) {
  513. return E_INVALIDARG;
  514. }
  515. return S_OK;
  516. }
  517. HRESULT CEventsDispEntry::Invoke(UINT argc,VARIANT *argv)
  518. {
  519. CEventsDispEntry::iterator i;
  520. for(i = begin();i != end();i++) {
  521. i->Invoke(argc,argv);
  522. }
  523. return S_OK;
  524. }
  525. CEventsDispEntry & CEventsMap::LookupNc(LPWSTR Name) throw(std::bad_alloc)
  526. {
  527. HRESULT hr;
  528. size_t len = wcslen(Name)+1;
  529. wchar_t *pBuffer = new wchar_t[len+1];
  530. if(!pBuffer) {
  531. throw std::bad_alloc();
  532. }
  533. wcscpy(pBuffer,Name);
  534. _wcsupr(pBuffer);
  535. std::wstring ind = pBuffer;
  536. delete [] pBuffer;
  537. return (*this)[ind];
  538. }
  539. HRESULT CEventsMap::AttachEvent(LPWSTR Name,LPDISPATCH pDisp,VARIANT_BOOL *pStatus)
  540. {
  541. HRESULT hr;
  542. try {
  543. *pStatus = VARIANT_FALSE;
  544. CEventsDispEntry &Ent = LookupNc(Name);
  545. hr = Ent.AttachEvent(pDisp,pStatus);
  546. } catch(std::bad_alloc) {
  547. return E_OUTOFMEMORY;
  548. } catch(...) {
  549. return E_INVALIDARG;
  550. }
  551. return hr;
  552. }
  553. HRESULT CEventsMap::DetachEvent(LPWSTR Name,LPDISPATCH pDisp,VARIANT_BOOL *pStatus)
  554. {
  555. HRESULT hr;
  556. try {
  557. *pStatus = VARIANT_FALSE;
  558. CEventsDispEntry &Ent = LookupNc(Name);
  559. hr = Ent.DetachEvent(pDisp,pStatus);
  560. } catch(std::bad_alloc) {
  561. return E_OUTOFMEMORY;
  562. } catch(...) {
  563. return E_INVALIDARG;
  564. }
  565. return hr;
  566. }
  567. HRESULT CEventsMap::Invoke(LPWSTR Name,UINT argc,VARIANT *argv)
  568. {
  569. HRESULT hr;
  570. try {
  571. CEventsDispEntry &Ent = LookupNc(Name);
  572. hr = Ent.Invoke(argc,argv);
  573. } catch(std::bad_alloc) {
  574. return E_OUTOFMEMORY;
  575. } catch(...) {
  576. return E_INVALIDARG;
  577. }
  578. return hr;
  579. }