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.

667 lines
16 KiB

  1. //=======================================================================
  2. //
  3. // Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
  4. //
  5. // File: cdm.cpp
  6. //
  7. // Description:
  8. //
  9. // Functions exported by CDM
  10. //
  11. // CloseCDMContext
  12. // DetFilesDownloaded
  13. // DownloadGetUpdatedFiles
  14. // DownloadIsInternetAvailable
  15. // DownloadUpdatedFiles
  16. // FindMatchingDriver
  17. // LogDriverNotFound
  18. // OpenCDMContext
  19. // OpenCDMContextEx
  20. // QueryDetectionFiles
  21. //
  22. //=======================================================================
  23. #include <objbase.h>
  24. #include <winbase.h>
  25. #include <tchar.h>
  26. #include <logging.h>
  27. #include <iucommon.h>
  28. #include <loadengine.h>
  29. #include <osdet.h>
  30. #include <iu.h>
  31. #include <wininet.h>
  32. #include <wusafefn.h>
  33. static BOOL g_fCloseConnection /* FALSE */;
  34. static HMODULE g_hEngineModule /* = NULL */;
  35. static PFN_InternalDetFilesDownloaded g_pfnDetFilesDownloaded /* = NULL */;
  36. static PFN_InternalDownloadGetUpdatedFiles g_pfnDownloadGetUpdatedFiles /* = NULL */;
  37. static PFN_InternalDownloadUpdatedFiles g_pfnDownloadUpdatedFiles /* = NULL */;
  38. static PFN_InternalFindMatchingDriver g_pfnFindMatchingDriver /* = NULL */;
  39. static PFN_InternalLogDriverNotFound g_pfnLogDriverNotFound /* = NULL */;
  40. static PFN_InternalQueryDetectionFiles g_pfnQueryDetectionFiles /* = NULL */;
  41. static PFN_InternalSetGlobalOfflineFlag g_pfnSetGlobalOfflineFlag /* = NULL */;
  42. static PFN_SetOperationMode g_pfnSetOperationMode /* = NULL */;
  43. static HMODULE g_hCtlModule /* = NULL */;
  44. static long g_lLoadEngineRefCount /* = 0 */;
  45. static PFN_LoadIUEngine g_pfnCtlLoadIUEngine /* = NULL */;
  46. static PFN_UnLoadIUEngine g_pfnCtlUnLoadIUEngine /* = NULL */;
  47. static CRITICAL_SECTION g_cs;
  48. BOOL g_fInitCS;
  49. const TCHAR szOpenCDMContextFirst[] = _T("Must OpenCDMContext first!");
  50. //
  51. // constant for SetOperationMode() API (BUILD util won't allow building iuctl.idl from cdm dir)
  52. //
  53. const LONG UPDATE_COMMAND_CANCEL = 0x00000004;
  54. BOOL APIENTRY DllMain(
  55. HINSTANCE hInstance,
  56. DWORD ul_reason_for_call,
  57. LPVOID /*lpReserved*/
  58. )
  59. {
  60. switch (ul_reason_for_call)
  61. {
  62. case DLL_PROCESS_ATTACH:
  63. DisableThreadLibraryCalls(hInstance);
  64. g_fInitCS = SafeInitializeCriticalSection(&g_cs);
  65. //
  66. // Initialize free logging
  67. //
  68. InitFreeLogging(_T("CDM"));
  69. LogMessage("Starting");
  70. if (!g_fInitCS)
  71. {
  72. LogError(E_FAIL, "InitializeCriticalSection");
  73. return FALSE;
  74. }
  75. break;
  76. case DLL_PROCESS_DETACH:
  77. //
  78. // Shutdown free logging
  79. //
  80. LogMessage("Shutting down");
  81. TermFreeLogging();
  82. if (g_fInitCS)
  83. {
  84. DeleteCriticalSection(&g_cs);
  85. }
  86. break;
  87. }
  88. return TRUE;
  89. }
  90. void UnLoadCtlAndEngine(void)
  91. {
  92. LOG_Block("UnLoadCtlAndEngine");
  93. EnterCriticalSection(&g_cs);
  94. if (0 != g_lLoadEngineRefCount)
  95. {
  96. g_lLoadEngineRefCount--;
  97. }
  98. if (0 == g_lLoadEngineRefCount)
  99. {
  100. if(NULL != g_hEngineModule)
  101. {
  102. //
  103. // Call UnLoadIUEngine
  104. //
  105. g_pfnCtlUnLoadIUEngine(g_hEngineModule);
  106. g_hEngineModule = NULL;
  107. g_pfnDetFilesDownloaded = NULL;
  108. g_pfnDownloadGetUpdatedFiles = NULL;
  109. g_pfnDownloadUpdatedFiles = NULL;
  110. g_pfnFindMatchingDriver = NULL;
  111. g_pfnLogDriverNotFound = NULL;
  112. g_pfnQueryDetectionFiles = NULL;
  113. g_pfnSetGlobalOfflineFlag = NULL;
  114. g_pfnSetOperationMode = NULL;
  115. }
  116. if (NULL != g_hCtlModule)
  117. {
  118. //
  119. // Unload the iuctl.dll
  120. //
  121. FreeLibrary(g_hCtlModule);
  122. g_hCtlModule = NULL;
  123. g_pfnCtlLoadIUEngine = NULL;
  124. g_pfnCtlUnLoadIUEngine = NULL;
  125. }
  126. if (g_fCloseConnection)
  127. {
  128. //
  129. // We dialed for the user - now disconnect
  130. //
  131. if (!InternetAutodialHangup(0))
  132. {
  133. LOG_ErrorMsg(E_FAIL);
  134. SetLastError(E_FAIL);
  135. }
  136. g_fCloseConnection = FALSE;
  137. }
  138. }
  139. LeaveCriticalSection(&g_cs);
  140. }
  141. BOOL LoadCtlAndEngine(BOOL fConnectIfNotConnected)
  142. {
  143. LOG_Block("LoadCtlAndEngine");
  144. BOOL fRet = FALSE;
  145. HRESULT hr;
  146. DWORD dwFlags;
  147. BOOL fConnected = InternetGetConnectedState(&dwFlags, 0);
  148. LOG_Driver(_T("fConnectIfNotConnected param is %s"), fConnectIfNotConnected ? _T("TRUE") : _T("FALSE"));
  149. LOG_Driver(_T("fConnected = %s, dwFlags from InternetGetConnectedState = 0x%08x"), fConnected ? _T("TRUE") : _T("FALSE"), dwFlags);
  150. EnterCriticalSection(&g_cs); // start touching globals
  151. if (fConnectIfNotConnected)
  152. {
  153. if (!fConnected)
  154. {
  155. if ((INTERNET_CONNECTION_MODEM & dwFlags) && !(INTERNET_CONNECTION_OFFLINE & dwFlags))
  156. {
  157. //
  158. // If we are not already connected to the internet and
  159. // the system is configured to use a modem attempt a connection.
  160. //
  161. DWORD dwErr;
  162. if (ERROR_SUCCESS == (dwErr = InternetAttemptConnect(0)))
  163. {
  164. LOG_Driver(_T("auto-dial succeeded"));
  165. //
  166. // The auto-dial worked, we need to disconnect later
  167. //
  168. g_fCloseConnection = TRUE;
  169. fConnected = TRUE;
  170. }
  171. else
  172. {
  173. //
  174. // Bail with error since we are required to be online
  175. //
  176. LOG_Driver(_T("auto-dial failed"));
  177. LOG_ErrorMsg(dwErr);
  178. SetLastError(dwErr);
  179. goto CleanUp;
  180. }
  181. }
  182. else
  183. {
  184. //
  185. // We can't connect because we aren't configured for a modem or user set IE offline mode
  186. //
  187. LOG_ErrorMsg(ERROR_GEN_FAILURE);
  188. SetLastError(ERROR_GEN_FAILURE);
  189. goto CleanUp;
  190. }
  191. }
  192. }
  193. //
  194. // Now that we are connected (only required if TRUE == fConnectIfNotConnected)
  195. //
  196. if (NULL != g_hEngineModule)
  197. {
  198. LOG_Driver(_T("IUEngine is already loaded"));
  199. //
  200. // Bump the ref count and return TRUE
  201. //
  202. g_lLoadEngineRefCount++;
  203. fRet = TRUE;
  204. goto CleanUp;
  205. }
  206. //
  207. // This extra lock on wininet.dll is required to prevent a TerminateThread call from
  208. // WININET!AUTO_PROXY_DLLS::FreeAutoProxyInfo during FreeLibrary of CDM.DLL.
  209. //
  210. // We don't ever free the returned handle, but will fail the call if it returns NULL
  211. //
  212. if (NULL == LoadLibraryFromSystemDir(_T("wininet.dll")))
  213. {
  214. LOG_ErrorMsg(GetLastError());
  215. goto CleanUp;
  216. }
  217. //
  218. // Load iuctl.dll and get the [Un]LoadIUEngine function pointers
  219. //
  220. if (NULL == (g_hCtlModule = LoadLibraryFromSystemDir(_T("iuctl.dll"))))
  221. {
  222. LOG_ErrorMsg(GetLastError());
  223. goto CleanUp;
  224. }
  225. if (NULL == (g_pfnCtlLoadIUEngine = (PFN_LoadIUEngine) GetProcAddress(g_hCtlModule, "LoadIUEngine")))
  226. {
  227. LOG_ErrorMsg(GetLastError());
  228. goto CleanUp;
  229. }
  230. if (NULL == (g_pfnCtlUnLoadIUEngine = (PFN_UnLoadIUEngine) GetProcAddress(g_hCtlModule, "UnLoadIUEngine")))
  231. {
  232. LOG_ErrorMsg(GetLastError());
  233. goto CleanUp;
  234. }
  235. //
  236. // Now we can call LoadIUEngine()
  237. //
  238. if (NULL == (g_hEngineModule = g_pfnCtlLoadIUEngine(TRUE, !fConnected)))
  239. {
  240. LOG_ErrorMsg(GetLastError());
  241. goto CleanUp;
  242. }
  243. g_pfnDetFilesDownloaded = (PFN_InternalDetFilesDownloaded) GetProcAddress(g_hEngineModule, "InternalDetFilesDownloaded");
  244. g_pfnDownloadGetUpdatedFiles = (PFN_InternalDownloadGetUpdatedFiles) GetProcAddress(g_hEngineModule, "InternalDownloadGetUpdatedFiles");
  245. g_pfnDownloadUpdatedFiles = (PFN_InternalDownloadUpdatedFiles) GetProcAddress(g_hEngineModule, "InternalDownloadUpdatedFiles");
  246. g_pfnFindMatchingDriver = (PFN_InternalFindMatchingDriver) GetProcAddress(g_hEngineModule, "InternalFindMatchingDriver");
  247. g_pfnLogDriverNotFound = (PFN_InternalLogDriverNotFound) GetProcAddress(g_hEngineModule, "InternalLogDriverNotFound");
  248. g_pfnQueryDetectionFiles = (PFN_InternalQueryDetectionFiles) GetProcAddress(g_hEngineModule, "InternalQueryDetectionFiles");
  249. g_pfnSetGlobalOfflineFlag = (PFN_InternalSetGlobalOfflineFlag) GetProcAddress(g_hEngineModule, "InternalSetGlobalOfflineFlag");
  250. g_pfnSetOperationMode = (PFN_SetOperationMode) GetProcAddress(g_hEngineModule, "EngSetOperationMode");
  251. if (NULL == g_pfnDetFilesDownloaded ||
  252. NULL == g_pfnDownloadGetUpdatedFiles ||
  253. NULL == g_pfnDownloadUpdatedFiles ||
  254. NULL == g_pfnFindMatchingDriver ||
  255. NULL == g_pfnLogDriverNotFound ||
  256. NULL == g_pfnQueryDetectionFiles ||
  257. NULL == g_pfnSetGlobalOfflineFlag ||
  258. NULL == g_pfnSetOperationMode )
  259. {
  260. LOG_Driver(_T("GetProcAddress on IUEngine failed"));
  261. LOG_ErrorMsg(ERROR_CALL_NOT_IMPLEMENTED);
  262. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  263. }
  264. else
  265. {
  266. fRet = TRUE;
  267. g_lLoadEngineRefCount++;
  268. // Set Global Offline Flag - checked by XML Classes to disable Validation (schemas are on the net)
  269. g_pfnSetGlobalOfflineFlag(!fConnected);
  270. }
  271. // goto CleanUp;
  272. CleanUp:
  273. if (FALSE == fRet)
  274. {
  275. UnLoadCtlAndEngine();
  276. }
  277. LeaveCriticalSection(&g_cs);
  278. return fRet;
  279. }
  280. //This API closes the internet connection opened with the OpenCDMContext() API.
  281. //If CDM did not open the internet connection this API simply returns. The CDM
  282. //context handle must have been the same handle that was returned from
  283. //the OpenCDMContext() API.
  284. //
  285. //This call cannot fail. If the pConnection handle is invalid this function
  286. //simply ignores it.
  287. VOID WINAPI CloseCDMContext (
  288. IN HANDLE /* hConnection */ // Obsolete handle returned by OpenCDMContext.
  289. )
  290. {
  291. LOG_Block("CloseCDMContext");
  292. //
  293. // This is the only spot we unload engine (but note exceptions in
  294. // DownloadGetUpdatedFiles).
  295. //
  296. // Doesn't use COM
  297. //
  298. UnLoadCtlAndEngine();
  299. }
  300. void WINAPI DetFilesDownloaded(
  301. IN HANDLE hConnection
  302. )
  303. {
  304. LOG_Block("DetFilesDownloaded");
  305. HRESULT hr;
  306. if (g_pfnDetFilesDownloaded)
  307. {
  308. if (SUCCEEDED(hr = CoInitialize(0)))
  309. {
  310. g_pfnDetFilesDownloaded(hConnection);
  311. CoUninitialize();
  312. }
  313. else
  314. {
  315. LOG_ErrorMsg(hr);
  316. }
  317. }
  318. else
  319. {
  320. LOG_Error(szOpenCDMContextFirst);
  321. }
  322. }
  323. //Win 98 entry point
  324. //This function allows Windows 98 to call the same entry points as NT.
  325. //The function returns TRUE if the download succeeds and FALSE if it
  326. //does not.
  327. BOOL DownloadGetUpdatedFiles(
  328. IN PDOWNLOADINFOWIN98 pDownloadInfoWin98, //The win98 download info structure is
  329. //slightly different that the NT version
  330. //so this function handles conversion.
  331. IN OUT LPTSTR lpDownloadPath, //returned Download path to the downloaded
  332. //cab files.
  333. IN UINT uSize //size of passed in download path buffer.
  334. )
  335. {
  336. LOG_Block("DownloadGetUpdatedFiles");
  337. if (1 == IsWindowsUpdateUserAccessDisabled())
  338. {
  339. LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
  340. return FALSE;
  341. }
  342. //
  343. // Special case - we need to load and unloadengine since historically we haven't required an
  344. // OpenCDMContext[Ex] call before calling this function and CloseCDMContext after.
  345. //
  346. HRESULT hr;
  347. BOOL fRet;
  348. if (LoadCtlAndEngine(TRUE))
  349. {
  350. if (SUCCEEDED(hr = CoInitialize(0)))
  351. {
  352. fRet = g_pfnDownloadGetUpdatedFiles(pDownloadInfoWin98, lpDownloadPath, uSize);
  353. CoUninitialize();
  354. }
  355. else
  356. {
  357. LOG_ErrorMsg(hr);
  358. fRet = FALSE;
  359. }
  360. UnLoadCtlAndEngine();
  361. return fRet;
  362. }
  363. else
  364. {
  365. return FALSE;
  366. }
  367. }
  368. //This function determines if this client can connect to the internet.
  369. BOOL DownloadIsInternetAvailable(void)
  370. {
  371. LOG_Block("DownloadIsInternetAvailable");
  372. if (1 == IsWindowsUpdateUserAccessDisabled())
  373. {
  374. LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
  375. return FALSE;
  376. }
  377. //
  378. // We don't care about the current online state, just if we have a
  379. // connection configured (returned in dwFlags).
  380. //
  381. DWORD dwFlags;
  382. (void) InternetGetConnectedState(&dwFlags, 0);
  383. if ( ( (INTERNET_CONNECTION_CONFIGURED & dwFlags) ||
  384. (INTERNET_CONNECTION_LAN & dwFlags) ||
  385. (INTERNET_CONNECTION_MODEM & dwFlags) ||
  386. (INTERNET_RAS_INSTALLED & dwFlags) ||
  387. (INTERNET_CONNECTION_PROXY & dwFlags) )
  388. && !(INTERNET_CONNECTION_OFFLINE & dwFlags)
  389. )
  390. {
  391. LOG_Driver(_T("Returning TRUE: InternetGetConnectedState returned 0x%08x in dwFlags"), dwFlags);
  392. return TRUE;
  393. }
  394. else
  395. {
  396. LOG_Driver(_T("Returning FALSE: InternetGetConnectedState returned 0x%08x in dwFlags"), dwFlags);
  397. return FALSE;
  398. }
  399. }
  400. //This function downloads the specified CDM package. The hConnection handle must have
  401. //been returned from the OpenCDMContext() API.
  402. //
  403. //This function Returns TRUE if download is successful GetLastError() will return
  404. //the error code indicating the reason that the call failed.
  405. BOOL WINAPI DownloadUpdatedFiles(
  406. IN HANDLE hConnection, //Connection handle from OpenCDMContext() API.
  407. IN HWND hwnd, //Window handle for call context
  408. IN PDOWNLOADINFO pDownloadInfo, //download information structure describing
  409. //package to be read from server
  410. OUT LPWSTR lpDownloadPath, //local computer directory location of the
  411. //downloaded files
  412. IN UINT uSize, //size of the download path buffer. If this
  413. //buffer is to small to contain the complete
  414. //path and file name no file will be downloaded.
  415. //The PUINT puReguiredSize parameter can be checked
  416. //to determine the size of buffer necessary to
  417. //perform the download.
  418. OUT PUINT puRequiredSize //required lpDownloadPath buffer size. This
  419. //parameter is filled in with the minimum size
  420. //that is required to place the complete path
  421. //file name of the downloaded file. If this
  422. //parameter is NULL no size is returned.
  423. )
  424. {
  425. LOG_Block("DownloadUpdatedFiles");
  426. HRESULT hr;
  427. BOOL fRet;
  428. if (g_pfnDownloadUpdatedFiles)
  429. {
  430. if (SUCCEEDED(hr = CoInitialize(0)))
  431. {
  432. fRet = g_pfnDownloadUpdatedFiles(hConnection, hwnd, pDownloadInfo, lpDownloadPath, uSize, puRequiredSize);
  433. CoUninitialize();
  434. return fRet;
  435. }
  436. else
  437. {
  438. LOG_ErrorMsg(hr);
  439. return FALSE;
  440. }
  441. }
  442. else
  443. {
  444. LOG_Error(szOpenCDMContextFirst);
  445. return FALSE;
  446. }
  447. }
  448. BOOL WINAPI FindMatchingDriver(
  449. IN HANDLE hConnection,
  450. IN PDOWNLOADINFO pDownloadInfo,
  451. OUT PWUDRIVERINFO pWuDriverInfo
  452. )
  453. {
  454. LOG_Block("FindMatchingDriver");
  455. HRESULT hr;
  456. BOOL fRet;
  457. if (g_pfnFindMatchingDriver)
  458. {
  459. if (SUCCEEDED(hr = CoInitialize(0)))
  460. {
  461. fRet = g_pfnFindMatchingDriver(hConnection, pDownloadInfo, pWuDriverInfo);
  462. CoUninitialize();
  463. return fRet;
  464. }
  465. else
  466. {
  467. LOG_ErrorMsg(hr);
  468. return FALSE;
  469. }
  470. }
  471. else
  472. {
  473. LOG_Error(szOpenCDMContextFirst);
  474. return FALSE;
  475. }
  476. }
  477. // supports offline logging
  478. // hConnection NOT used at all
  479. // no network connection or osdet.dll needed for languauge, SKU, platform detection
  480. void WINAPI LogDriverNotFound(
  481. IN HANDLE hConnection,
  482. IN LPCWSTR lpDeviceInstanceID,
  483. IN DWORD dwFlags
  484. )
  485. {
  486. LOG_Block("LogDriverNotFound");
  487. HRESULT hr;
  488. if (g_pfnLogDriverNotFound)
  489. {
  490. if (SUCCEEDED(hr = CoInitialize(0)))
  491. {
  492. g_pfnLogDriverNotFound(hConnection, lpDeviceInstanceID, dwFlags);
  493. CoUninitialize();
  494. }
  495. else
  496. {
  497. LOG_ErrorMsg(hr);
  498. }
  499. }
  500. else
  501. {
  502. LOG_Error(szOpenCDMContextFirst);
  503. }
  504. }
  505. HANDLE WINAPI OpenCDMContext(
  506. IN HWND /* hwnd */ //Window handle to use for any UI that needs to be presented (not used)
  507. )
  508. {
  509. LOG_Block("OpenCDMContext");
  510. return OpenCDMContextEx(TRUE);
  511. }
  512. HANDLE WINAPI OpenCDMContextEx(
  513. IN BOOL fConnectIfNotConnected
  514. )
  515. {
  516. LOG_Block("OpenCDMContextEx");
  517. //
  518. // Don't open a context if we are disabled (0 and -1 OK)
  519. // Other functions will fail because their g_pfnXxxxx == NULL.
  520. //
  521. if (1 == IsWindowsUpdateUserAccessDisabled())
  522. {
  523. LOG_ErrorMsg(ERROR_SERVICE_DISABLED);
  524. SetLastError(ERROR_SERVICE_DISABLED);
  525. return NULL;
  526. }
  527. //
  528. // Doesn't use COM
  529. //
  530. if (LoadCtlAndEngine(fConnectIfNotConnected))
  531. {
  532. //
  533. // This is an obsolete function that just loads the engine (which may do autodial to connect).
  534. // We just return non-NULL g_lLoadEngineRefCount to keep existing clients happy, but never use it.
  535. //
  536. return LongToHandle(g_lLoadEngineRefCount);
  537. }
  538. else
  539. {
  540. return NULL;
  541. }
  542. }
  543. int WINAPI QueryDetectionFiles(
  544. IN HANDLE hConnection,
  545. IN void* pCallbackParam,
  546. IN PFN_QueryDetectionFilesCallback pCallback
  547. )
  548. {
  549. LOG_Block("QueryDetectionFiles");
  550. HRESULT hr;
  551. int nRet;
  552. if (g_pfnQueryDetectionFiles)
  553. {
  554. if (SUCCEEDED(hr = CoInitialize(0)))
  555. {
  556. nRet = g_pfnQueryDetectionFiles(hConnection, pCallbackParam, pCallback);
  557. CoUninitialize();
  558. return nRet;
  559. }
  560. else
  561. {
  562. LOG_ErrorMsg(hr);
  563. return 0;
  564. }
  565. }
  566. else
  567. {
  568. LOG_Error(szOpenCDMContextFirst);
  569. return 0;
  570. }
  571. }
  572. //
  573. // 502965 Windows Error Reporting bucket 2096553: Hang following NEWDEV.DLL!CancelDriverSearch
  574. //
  575. // Provide API to allow clients to cancel synchronous calls into CDM by calling this function
  576. // asynchronously from a second thread.
  577. //
  578. HRESULT WINAPI CancelCDMOperation(void)
  579. {
  580. LOG_Block("CancelCDMOperation");
  581. if (g_pfnSetOperationMode)
  582. {
  583. return g_pfnSetOperationMode(NULL, NULL, UPDATE_COMMAND_CANCEL);
  584. }
  585. else
  586. {
  587. LOG_ErrorMsg(E_ACCESSDENIED);
  588. return E_ACCESSDENIED;
  589. }
  590. }