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.

526 lines
9.7 KiB

  1. // Devices.cpp : Implementation of CDevices
  2. #include "stdafx.h"
  3. #include "DevCon2.h"
  4. #include "Devices.h"
  5. #include "Device.h"
  6. #include "DevicesEnum.h"
  7. #include "DevInfoSet.h"
  8. #include "xStrings.h"
  9. #include "utils.h"
  10. /////////////////////////////////////////////////////////////////////////////
  11. // CDevices
  12. CDevices::~CDevices()
  13. {
  14. DWORD c;
  15. if(pDevices) {
  16. for(c=0;c<Count;c++) {
  17. pDevices[c]->Release();
  18. }
  19. delete [] pDevices;
  20. }
  21. }
  22. STDMETHODIMP CDevices::get_Count(long *pVal)
  23. {
  24. *pVal = (long)Count;
  25. return S_OK;
  26. }
  27. STDMETHODIMP CDevices::Item(long Index, LPDISPATCH *ppVal)
  28. {
  29. *ppVal = NULL;
  30. DWORD i = (DWORD)Index;
  31. if(i<1 || i > Count) {
  32. return E_INVALIDARG;
  33. }
  34. i--;
  35. pDevices[i]->AddRef();
  36. *ppVal = pDevices[i];
  37. return S_OK;
  38. }
  39. STDMETHODIMP CDevices::get__NewEnum(IUnknown **ppUnk)
  40. {
  41. *ppUnk = NULL;
  42. HRESULT hr;
  43. CComObject<CDevicesEnum> *pEnum = NULL;
  44. hr = CComObject<CDevicesEnum>::CreateInstance(&pEnum);
  45. if(FAILED(hr)) {
  46. return hr;
  47. }
  48. if(!pEnum) {
  49. return E_OUTOFMEMORY;
  50. }
  51. pEnum->AddRef();
  52. if(!pEnum->CopyDevices(pDevices,Count)) {
  53. pEnum->Release();
  54. return E_OUTOFMEMORY;
  55. }
  56. *ppUnk = pEnum;
  57. return S_OK;
  58. }
  59. HRESULT CDevices::InternalAdd(LPCWSTR InstanceId)
  60. {
  61. HRESULT hr;
  62. DWORD c;
  63. HDEVINFO hDevInfo = GetDevInfoSet();
  64. if(hDevInfo == INVALID_HANDLE_VALUE) {
  65. return E_UNEXPECTED;
  66. }
  67. if(!IncreaseArraySize(1)) {
  68. return E_OUTOFMEMORY;
  69. }
  70. //
  71. // create the SP_DEVINFO_DATA holder
  72. //
  73. CComObject<CDevice> *pDevice = NULL;
  74. hr = CComObject<CDevice>::CreateInstance(&pDevice);
  75. if(FAILED(hr)) {
  76. return hr;
  77. }
  78. CComPtr<IDevice> pDevicePtr = pDevice;
  79. hr = pDevice->Init(DevInfoSet,InstanceId,DeviceConsole);
  80. if(FAILED(hr)) {
  81. return hr;
  82. }
  83. //
  84. // see if we have a device already in our list
  85. // if we have, don't add another copy
  86. //
  87. for(c=0;c<Count;c++) {
  88. if(pDevices[c]->SameAs(pDevice)) {
  89. return S_OK;
  90. }
  91. }
  92. pDevicePtr.Detach();
  93. pDevices[Count++] = pDevice;
  94. return S_OK;
  95. }
  96. STDMETHODIMP CDevices::Add(VARIANT InstanceIds)
  97. {
  98. CComObject<CStrings> *pStrings = NULL;
  99. HRESULT hr;
  100. BSTR str;
  101. DWORD c;
  102. hr = CComObject<CStrings>::CreateInstance(&pStrings);
  103. if(FAILED(hr)) {
  104. return hr;
  105. }
  106. CComPtr<IStrings> pStringsPtr = pStrings;
  107. hr = pStrings->Add(InstanceIds);
  108. if(FAILED(hr)) {
  109. return hr;
  110. }
  111. for(c=0;pStrings->InternalEnum(c,&str);c++) {
  112. //
  113. // convert string to interface
  114. //
  115. hr = InternalAdd(str);
  116. if(FAILED(hr)) {
  117. return hr;
  118. }
  119. }
  120. return S_OK;
  121. }
  122. STDMETHODIMP CDevices::Remove(VARIANT Index)
  123. {
  124. //
  125. // remove from logical list
  126. // removal from HDEVINFO based on refcounting
  127. //
  128. DWORD i;
  129. HRESULT hr;
  130. hr = GetIndex(&Index,&i);
  131. if(FAILED(hr)) {
  132. return hr;
  133. }
  134. if(i >= Count) {
  135. return E_INVALIDARG;
  136. }
  137. pDevices[i]->Release();
  138. Count--;
  139. DWORD c;
  140. for(c=i;c<Count;c++) {
  141. pDevices[c] = pDevices[c+1];
  142. }
  143. return S_OK;
  144. }
  145. HRESULT CDevices::Init(HDEVINFO hDevInfo,IDeviceConsole *pDevCon)
  146. {
  147. SP_DEVINFO_DATA DeviceInfoData;
  148. Reset();
  149. DWORD c;
  150. HRESULT hr;
  151. CComObject<CDevInfoSet> *d;
  152. hr = CComObject<CDevInfoSet>::CreateInstance(&d);
  153. if(FAILED(hr)) {
  154. SetupDiDestroyDeviceInfoList(hDevInfo);
  155. return hr;
  156. }
  157. DeviceConsole = pDevCon;
  158. DevInfoSet = d; // addref's
  159. d->Init(hDevInfo);
  160. ZeroMemory(&DeviceInfoData,sizeof(DeviceInfoData));
  161. DeviceInfoData.cbSize = sizeof(DeviceInfoData);
  162. for(c=0;SetupDiEnumDeviceInfo(hDevInfo,c,&DeviceInfoData);c++) {
  163. if(!IncreaseArraySize(1)) {
  164. Reset();
  165. return E_OUTOFMEMORY;
  166. }
  167. CComObject<CDevice> *pDevice = NULL;
  168. hr = CComObject<CDevice>::CreateInstance(&pDevice);
  169. if(FAILED(hr)) {
  170. Reset();
  171. return hr;
  172. }
  173. pDevice->AddRef();
  174. pDevices[Count++] = pDevice;
  175. hr = pDevice->Init(DevInfoSet,&DeviceInfoData,DeviceConsole);
  176. if(FAILED(hr)) {
  177. Reset();
  178. return hr;
  179. }
  180. }
  181. return Count ? S_OK : S_FALSE;
  182. }
  183. WINSETUPAPI BOOL WINAPI
  184. SetupDiEnumDeviceInfo(
  185. IN HDEVINFO DeviceInfoSet,
  186. IN DWORD MemberIndex,
  187. OUT PSP_DEVINFO_DATA DeviceInfoData
  188. );
  189. void CDevices::Reset()
  190. {
  191. DWORD c;
  192. for(c=0;c<Count;c++) {
  193. pDevices[c]->Release();
  194. }
  195. Count = 0;
  196. DevInfoSet = NULL;
  197. }
  198. BOOL CDevices::IncreaseArraySize(DWORD add)
  199. {
  200. CDevice** pNewDevices;
  201. DWORD Inc;
  202. DWORD c;
  203. if((ArraySize-Count)>=add) {
  204. return TRUE;
  205. }
  206. Inc = ArraySize + add + 32;
  207. pNewDevices = new CDevice*[Inc];
  208. if(!pNewDevices) {
  209. return FALSE;
  210. }
  211. for(c=0;c<Count;c++) {
  212. pNewDevices[c] = pDevices[c];
  213. }
  214. delete [] pDevices;
  215. pDevices = pNewDevices;
  216. ArraySize = Inc;
  217. return TRUE;
  218. }
  219. STDMETHODIMP CDevices::CreateRootDevice(VARIANT hwidParam, LPDISPATCH *pDispatch)
  220. {
  221. *pDispatch = NULL;
  222. HRESULT hr;
  223. DWORD len;
  224. DWORD Err;
  225. LPWSTR hwidlist = NULL;
  226. CComObject<CDevice> *pDevice = NULL;
  227. HDEVINFO hDevInfo;
  228. SP_DEVINFO_DATA DeviceInfoData;
  229. WCHAR name[33];
  230. DWORD c,cc;
  231. LPCWSTR lastPart;
  232. LPCWSTR hwid;
  233. CComVariant hwid_v;
  234. LPCGUID pGuid = NULL;
  235. //
  236. // prepare list if we need to
  237. //
  238. hDevInfo = GetDevInfoSet();
  239. if(hDevInfo == INVALID_HANDLE_VALUE) {
  240. return E_UNEXPECTED;
  241. }
  242. if(!IncreaseArraySize(1)) {
  243. return E_OUTOFMEMORY;
  244. }
  245. hr = GetOptionalString(&hwidParam,hwid_v,&hwid);
  246. if(FAILED(hr)) {
  247. return hr;
  248. }
  249. //
  250. // see if this devices collection is associated with a setup class
  251. //
  252. SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
  253. devInfoListDetail.cbSize = sizeof(devInfoListDetail);
  254. if(!SetupDiGetDeviceInfoListDetail(hDevInfo,&devInfoListDetail)) {
  255. DWORD Err = GetLastError();
  256. return HRESULT_FROM_SETUPAPI(Err);
  257. }
  258. if(memcmp(&devInfoListDetail.ClassGuid,&GUID_NULL,sizeof(devInfoListDetail.ClassGuid)) != 0) {
  259. //
  260. // collection is locked to a class, use that class to create device
  261. //
  262. pGuid = &devInfoListDetail.ClassGuid;
  263. } else {
  264. //
  265. // class is unknown
  266. //
  267. pGuid = &GUID_DEVCLASS_UNKNOWN;
  268. }
  269. if(hwid) {
  270. //
  271. // use hwid as basis of name
  272. // this really has no significant meaning, but helps for diagnostics
  273. // another option would be to use class name
  274. // be we don't know classname here
  275. //
  276. lastPart = wcsrchr(hwid,L'\\');
  277. if(!lastPart) {
  278. lastPart = hwid;
  279. }
  280. for(c=0,cc=0;c<16;c++) {
  281. //
  282. // ignore troublesome characters
  283. //
  284. while(lastPart[cc] &&
  285. ((lastPart[cc] == L'/')
  286. || (lastPart[cc] == L'\\')
  287. || (lastPart[cc] == L'#')
  288. || (lastPart[cc] >= 0x7f)
  289. || (lastPart[cc] <= 0x20)
  290. )) {
  291. cc++;
  292. }
  293. if(!hwid[cc]) {
  294. break;
  295. }
  296. name[c] = hwid[cc];
  297. }
  298. } else {
  299. c = 0;
  300. }
  301. if(c) {
  302. name[c] = L'0';
  303. } else {
  304. wcscpy(name,L"NONAME");
  305. }
  306. ZeroMemory(&DeviceInfoData,sizeof(DeviceInfoData));
  307. DeviceInfoData.cbSize = sizeof(DeviceInfoData);
  308. if (!SetupDiCreateDeviceInfo(hDevInfo,
  309. name,
  310. pGuid,
  311. NULL,
  312. 0,
  313. DICD_GENERATE_ID,
  314. &DeviceInfoData))
  315. {
  316. Err = GetLastError();
  317. hr = HRESULT_FROM_SETUPAPI(Err);
  318. return hr;
  319. }
  320. if(hwid && hwid[0]) {
  321. //
  322. // Add the HardwareID to the Device's HardwareID property.
  323. //
  324. len = wcslen(hwid);
  325. hwidlist = new WCHAR[len+2];
  326. if(!hwidlist) {
  327. hr = E_OUTOFMEMORY;
  328. goto final;
  329. }
  330. wcsncpy(hwidlist,hwid,len+1);
  331. hwidlist[len] = L'\0';
  332. hwidlist[len+1] = L'\0';
  333. if(!SetupDiSetDeviceRegistryProperty(hDevInfo,
  334. &DeviceInfoData,
  335. SPDRP_HARDWAREID,
  336. (LPBYTE)hwidlist,
  337. (len+2)*sizeof(TCHAR)))
  338. {
  339. Err = GetLastError();
  340. hr = HRESULT_FROM_SETUPAPI(Err);
  341. goto final;
  342. }
  343. }
  344. //
  345. // Transform the registry element into an actual devnode
  346. // in the PnP HW tree.
  347. //
  348. if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE,
  349. hDevInfo,
  350. &DeviceInfoData))
  351. {
  352. Err = GetLastError();
  353. hr = HRESULT_FROM_SETUPAPI(Err);
  354. goto final;
  355. }
  356. //
  357. // create the SP_DEVINFO_DATA holder
  358. //
  359. hr = CComObject<CDevice>::CreateInstance(&pDevice);
  360. if(FAILED(hr)) {
  361. return hr;
  362. }
  363. pDevice->AddRef();
  364. hr = pDevice->Init(DevInfoSet,&DeviceInfoData,DeviceConsole);
  365. if(FAILED(hr)) {
  366. pDevice->Release();
  367. goto final;
  368. }
  369. //
  370. // Add to list
  371. //
  372. pDevices[Count++] = pDevice;
  373. //
  374. // Return it
  375. //
  376. pDevice->AddRef();
  377. *pDispatch = pDevice;
  378. hr = S_OK;
  379. final:
  380. if(hwidlist) {
  381. delete [] hwidlist;
  382. }
  383. if(FAILED(hr)) {
  384. if(!SetupDiCallClassInstaller(DIF_REMOVE,hDevInfo,&DeviceInfoData)) {
  385. //
  386. // if we failed to delete including class/co installers
  387. // force it
  388. //
  389. SetupDiRemoveDevice(hDevInfo,&DeviceInfoData);
  390. }
  391. }
  392. return hr;
  393. }
  394. HDEVINFO CDevices::GetDevInfoSet()
  395. {
  396. ULONGLONG h;
  397. HRESULT hr;
  398. if(!DevInfoSet) {
  399. return (HDEVINFO)INVALID_HANDLE_VALUE;
  400. }
  401. hr = DevInfoSet->get_Handle(&h);
  402. if(FAILED(hr)) {
  403. return (HDEVINFO)INVALID_HANDLE_VALUE;
  404. }
  405. return (HDEVINFO)h;
  406. }
  407. HRESULT CDevices::GetIndex(LPVARIANT Index,DWORD * pAt)
  408. {
  409. CComVariant v;
  410. HRESULT hr;
  411. if(IsNumericVariant(Index)) {
  412. hr = v.ChangeType(VT_I4,Index);
  413. if(FAILED(hr)) {
  414. return DISP_E_TYPEMISMATCH;
  415. }
  416. if(V_I4(&v)<1) {
  417. return E_INVALIDARG;
  418. }
  419. *pAt = ((DWORD)V_I4(&v))-1;
  420. return S_OK;
  421. }
  422. //
  423. // user actually supplied instance id
  424. //
  425. hr = v.ChangeType(VT_BSTR,Index);
  426. if(FAILED(hr)) {
  427. return DISP_E_TYPEMISMATCH;
  428. }
  429. if(!Count) {
  430. //
  431. // cannot match anything
  432. //
  433. return E_INVALIDARG;
  434. }
  435. //
  436. // find an existing device that matches this
  437. //
  438. DWORD c;
  439. for(c=0;c<Count;c++) {
  440. if(pDevices[c]->SameAs(V_BSTR(&v))) {
  441. *pAt = c;
  442. return S_OK;
  443. }
  444. }
  445. //
  446. // none found
  447. //
  448. return E_INVALIDARG;
  449. }
  450. STDMETHODIMP CDevices::get_Machine(BSTR *pVal)
  451. {
  452. HDEVINFO hDevInfo = GetDevInfoSet();
  453. if(hDevInfo == INVALID_HANDLE_VALUE) {
  454. return E_UNEXPECTED;
  455. }
  456. SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
  457. devInfoListDetail.cbSize = sizeof(devInfoListDetail);
  458. if(!SetupDiGetDeviceInfoListDetail(hDevInfo,&devInfoListDetail)) {
  459. DWORD Err = GetLastError();
  460. return HRESULT_FROM_SETUPAPI(Err);
  461. }
  462. if((devInfoListDetail.RemoteMachineHandle == NULL) || !devInfoListDetail.RemoteMachineName[0]) {
  463. *pVal = SysAllocString(L"");
  464. if(*pVal) {
  465. return S_FALSE;
  466. }
  467. } else {
  468. *pVal = SysAllocString(devInfoListDetail.RemoteMachineName);
  469. if(*pVal) {
  470. return S_OK;
  471. }
  472. }
  473. *pVal = NULL;
  474. return E_OUTOFMEMORY;
  475. }