//======================================================================= // // Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved. // // File: cdm.cpp // // Description: // // Functions exported by CDM // // CloseCDMContext // DetFilesDownloaded // DownloadGetUpdatedFiles // DownloadIsInternetAvailable // DownloadUpdatedFiles // FindMatchingDriver // LogDriverNotFound // OpenCDMContext // OpenCDMContextEx // QueryDetectionFiles // //======================================================================= #include #include #include #include #include #include #include #include #include #include static BOOL g_fCloseConnection /* FALSE */; static HMODULE g_hEngineModule /* = NULL */; static PFN_InternalDetFilesDownloaded g_pfnDetFilesDownloaded /* = NULL */; static PFN_InternalDownloadGetUpdatedFiles g_pfnDownloadGetUpdatedFiles /* = NULL */; static PFN_InternalDownloadUpdatedFiles g_pfnDownloadUpdatedFiles /* = NULL */; static PFN_InternalFindMatchingDriver g_pfnFindMatchingDriver /* = NULL */; static PFN_InternalLogDriverNotFound g_pfnLogDriverNotFound /* = NULL */; static PFN_InternalQueryDetectionFiles g_pfnQueryDetectionFiles /* = NULL */; static PFN_InternalSetGlobalOfflineFlag g_pfnSetGlobalOfflineFlag /* = NULL */; static PFN_SetOperationMode g_pfnSetOperationMode /* = NULL */; static HMODULE g_hCtlModule /* = NULL */; static long g_lLoadEngineRefCount /* = 0 */; static PFN_LoadIUEngine g_pfnCtlLoadIUEngine /* = NULL */; static PFN_UnLoadIUEngine g_pfnCtlUnLoadIUEngine /* = NULL */; static CRITICAL_SECTION g_cs; BOOL g_fInitCS; const TCHAR szOpenCDMContextFirst[] = _T("Must OpenCDMContext first!"); // // constant for SetOperationMode() API (BUILD util won't allow building iuctl.idl from cdm dir) // const LONG UPDATE_COMMAND_CANCEL = 0x00000004; BOOL APIENTRY DllMain( HINSTANCE hInstance, DWORD ul_reason_for_call, LPVOID /*lpReserved*/ ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hInstance); g_fInitCS = SafeInitializeCriticalSection(&g_cs); // // Initialize free logging // InitFreeLogging(_T("CDM")); LogMessage("Starting"); if (!g_fInitCS) { LogError(E_FAIL, "InitializeCriticalSection"); return FALSE; } break; case DLL_PROCESS_DETACH: // // Shutdown free logging // LogMessage("Shutting down"); TermFreeLogging(); if (g_fInitCS) { DeleteCriticalSection(&g_cs); } break; } return TRUE; } void UnLoadCtlAndEngine(void) { LOG_Block("UnLoadCtlAndEngine"); EnterCriticalSection(&g_cs); if (0 != g_lLoadEngineRefCount) { g_lLoadEngineRefCount--; } if (0 == g_lLoadEngineRefCount) { if(NULL != g_hEngineModule) { // // Call UnLoadIUEngine // g_pfnCtlUnLoadIUEngine(g_hEngineModule); g_hEngineModule = NULL; g_pfnDetFilesDownloaded = NULL; g_pfnDownloadGetUpdatedFiles = NULL; g_pfnDownloadUpdatedFiles = NULL; g_pfnFindMatchingDriver = NULL; g_pfnLogDriverNotFound = NULL; g_pfnQueryDetectionFiles = NULL; g_pfnSetGlobalOfflineFlag = NULL; g_pfnSetOperationMode = NULL; } if (NULL != g_hCtlModule) { // // Unload the iuctl.dll // FreeLibrary(g_hCtlModule); g_hCtlModule = NULL; g_pfnCtlLoadIUEngine = NULL; g_pfnCtlUnLoadIUEngine = NULL; } if (g_fCloseConnection) { // // We dialed for the user - now disconnect // if (!InternetAutodialHangup(0)) { LOG_ErrorMsg(E_FAIL); SetLastError(E_FAIL); } g_fCloseConnection = FALSE; } } LeaveCriticalSection(&g_cs); } BOOL LoadCtlAndEngine(BOOL fConnectIfNotConnected) { LOG_Block("LoadCtlAndEngine"); BOOL fRet = FALSE; HRESULT hr; DWORD dwFlags; BOOL fConnected = InternetGetConnectedState(&dwFlags, 0); LOG_Driver(_T("fConnectIfNotConnected param is %s"), fConnectIfNotConnected ? _T("TRUE") : _T("FALSE")); LOG_Driver(_T("fConnected = %s, dwFlags from InternetGetConnectedState = 0x%08x"), fConnected ? _T("TRUE") : _T("FALSE"), dwFlags); EnterCriticalSection(&g_cs); // start touching globals if (fConnectIfNotConnected) { if (!fConnected) { if ((INTERNET_CONNECTION_MODEM & dwFlags) && !(INTERNET_CONNECTION_OFFLINE & dwFlags)) { // // If we are not already connected to the internet and // the system is configured to use a modem attempt a connection. // DWORD dwErr; if (ERROR_SUCCESS == (dwErr = InternetAttemptConnect(0))) { LOG_Driver(_T("auto-dial succeeded")); // // The auto-dial worked, we need to disconnect later // g_fCloseConnection = TRUE; fConnected = TRUE; } else { // // Bail with error since we are required to be online // LOG_Driver(_T("auto-dial failed")); LOG_ErrorMsg(dwErr); SetLastError(dwErr); goto CleanUp; } } else { // // We can't connect because we aren't configured for a modem or user set IE offline mode // LOG_ErrorMsg(ERROR_GEN_FAILURE); SetLastError(ERROR_GEN_FAILURE); goto CleanUp; } } } // // Now that we are connected (only required if TRUE == fConnectIfNotConnected) // if (NULL != g_hEngineModule) { LOG_Driver(_T("IUEngine is already loaded")); // // Bump the ref count and return TRUE // g_lLoadEngineRefCount++; fRet = TRUE; goto CleanUp; } // // This extra lock on wininet.dll is required to prevent a TerminateThread call from // WININET!AUTO_PROXY_DLLS::FreeAutoProxyInfo during FreeLibrary of CDM.DLL. // // We don't ever free the returned handle, but will fail the call if it returns NULL // if (NULL == LoadLibraryFromSystemDir(_T("wininet.dll"))) { LOG_ErrorMsg(GetLastError()); goto CleanUp; } // // Load iuctl.dll and get the [Un]LoadIUEngine function pointers // if (NULL == (g_hCtlModule = LoadLibraryFromSystemDir(_T("iuctl.dll")))) { LOG_ErrorMsg(GetLastError()); goto CleanUp; } if (NULL == (g_pfnCtlLoadIUEngine = (PFN_LoadIUEngine) GetProcAddress(g_hCtlModule, "LoadIUEngine"))) { LOG_ErrorMsg(GetLastError()); goto CleanUp; } if (NULL == (g_pfnCtlUnLoadIUEngine = (PFN_UnLoadIUEngine) GetProcAddress(g_hCtlModule, "UnLoadIUEngine"))) { LOG_ErrorMsg(GetLastError()); goto CleanUp; } // // Now we can call LoadIUEngine() // if (NULL == (g_hEngineModule = g_pfnCtlLoadIUEngine(TRUE, !fConnected))) { LOG_ErrorMsg(GetLastError()); goto CleanUp; } g_pfnDetFilesDownloaded = (PFN_InternalDetFilesDownloaded) GetProcAddress(g_hEngineModule, "InternalDetFilesDownloaded"); g_pfnDownloadGetUpdatedFiles = (PFN_InternalDownloadGetUpdatedFiles) GetProcAddress(g_hEngineModule, "InternalDownloadGetUpdatedFiles"); g_pfnDownloadUpdatedFiles = (PFN_InternalDownloadUpdatedFiles) GetProcAddress(g_hEngineModule, "InternalDownloadUpdatedFiles"); g_pfnFindMatchingDriver = (PFN_InternalFindMatchingDriver) GetProcAddress(g_hEngineModule, "InternalFindMatchingDriver"); g_pfnLogDriverNotFound = (PFN_InternalLogDriverNotFound) GetProcAddress(g_hEngineModule, "InternalLogDriverNotFound"); g_pfnQueryDetectionFiles = (PFN_InternalQueryDetectionFiles) GetProcAddress(g_hEngineModule, "InternalQueryDetectionFiles"); g_pfnSetGlobalOfflineFlag = (PFN_InternalSetGlobalOfflineFlag) GetProcAddress(g_hEngineModule, "InternalSetGlobalOfflineFlag"); g_pfnSetOperationMode = (PFN_SetOperationMode) GetProcAddress(g_hEngineModule, "EngSetOperationMode"); if (NULL == g_pfnDetFilesDownloaded || NULL == g_pfnDownloadGetUpdatedFiles || NULL == g_pfnDownloadUpdatedFiles || NULL == g_pfnFindMatchingDriver || NULL == g_pfnLogDriverNotFound || NULL == g_pfnQueryDetectionFiles || NULL == g_pfnSetGlobalOfflineFlag || NULL == g_pfnSetOperationMode ) { LOG_Driver(_T("GetProcAddress on IUEngine failed")); LOG_ErrorMsg(ERROR_CALL_NOT_IMPLEMENTED); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); } else { fRet = TRUE; g_lLoadEngineRefCount++; // Set Global Offline Flag - checked by XML Classes to disable Validation (schemas are on the net) g_pfnSetGlobalOfflineFlag(!fConnected); } // goto CleanUp; CleanUp: if (FALSE == fRet) { UnLoadCtlAndEngine(); } LeaveCriticalSection(&g_cs); return fRet; } //This API closes the internet connection opened with the OpenCDMContext() API. //If CDM did not open the internet connection this API simply returns. The CDM //context handle must have been the same handle that was returned from //the OpenCDMContext() API. // //This call cannot fail. If the pConnection handle is invalid this function //simply ignores it. VOID WINAPI CloseCDMContext ( IN HANDLE /* hConnection */ // Obsolete handle returned by OpenCDMContext. ) { LOG_Block("CloseCDMContext"); // // This is the only spot we unload engine (but note exceptions in // DownloadGetUpdatedFiles). // // Doesn't use COM // UnLoadCtlAndEngine(); } void WINAPI DetFilesDownloaded( IN HANDLE hConnection ) { LOG_Block("DetFilesDownloaded"); HRESULT hr; if (g_pfnDetFilesDownloaded) { if (SUCCEEDED(hr = CoInitialize(0))) { g_pfnDetFilesDownloaded(hConnection); CoUninitialize(); } else { LOG_ErrorMsg(hr); } } else { LOG_Error(szOpenCDMContextFirst); } } //Win 98 entry point //This function allows Windows 98 to call the same entry points as NT. //The function returns TRUE if the download succeeds and FALSE if it //does not. BOOL DownloadGetUpdatedFiles( IN PDOWNLOADINFOWIN98 pDownloadInfoWin98, //The win98 download info structure is //slightly different that the NT version //so this function handles conversion. IN OUT LPTSTR lpDownloadPath, //returned Download path to the downloaded //cab files. IN UINT uSize //size of passed in download path buffer. ) { LOG_Block("DownloadGetUpdatedFiles"); if (1 == IsWindowsUpdateUserAccessDisabled()) { LOG_ErrorMsg(ERROR_SERVICE_DISABLED); return FALSE; } // // Special case - we need to load and unloadengine since historically we haven't required an // OpenCDMContext[Ex] call before calling this function and CloseCDMContext after. // HRESULT hr; BOOL fRet; if (LoadCtlAndEngine(TRUE)) { if (SUCCEEDED(hr = CoInitialize(0))) { fRet = g_pfnDownloadGetUpdatedFiles(pDownloadInfoWin98, lpDownloadPath, uSize); CoUninitialize(); } else { LOG_ErrorMsg(hr); fRet = FALSE; } UnLoadCtlAndEngine(); return fRet; } else { return FALSE; } } //This function determines if this client can connect to the internet. BOOL DownloadIsInternetAvailable(void) { LOG_Block("DownloadIsInternetAvailable"); if (1 == IsWindowsUpdateUserAccessDisabled()) { LOG_ErrorMsg(ERROR_SERVICE_DISABLED); return FALSE; } // // We don't care about the current online state, just if we have a // connection configured (returned in dwFlags). // DWORD dwFlags; (void) InternetGetConnectedState(&dwFlags, 0); if ( ( (INTERNET_CONNECTION_CONFIGURED & dwFlags) || (INTERNET_CONNECTION_LAN & dwFlags) || (INTERNET_CONNECTION_MODEM & dwFlags) || (INTERNET_RAS_INSTALLED & dwFlags) || (INTERNET_CONNECTION_PROXY & dwFlags) ) && !(INTERNET_CONNECTION_OFFLINE & dwFlags) ) { LOG_Driver(_T("Returning TRUE: InternetGetConnectedState returned 0x%08x in dwFlags"), dwFlags); return TRUE; } else { LOG_Driver(_T("Returning FALSE: InternetGetConnectedState returned 0x%08x in dwFlags"), dwFlags); return FALSE; } } //This function downloads the specified CDM package. The hConnection handle must have //been returned from the OpenCDMContext() API. // //This function Returns TRUE if download is successful GetLastError() will return //the error code indicating the reason that the call failed. BOOL WINAPI DownloadUpdatedFiles( IN HANDLE hConnection, //Connection handle from OpenCDMContext() API. IN HWND hwnd, //Window handle for call context IN PDOWNLOADINFO pDownloadInfo, //download information structure describing //package to be read from server OUT LPWSTR lpDownloadPath, //local computer directory location of the //downloaded files IN UINT uSize, //size of the download path buffer. If this //buffer is to small to contain the complete //path and file name no file will be downloaded. //The PUINT puReguiredSize parameter can be checked //to determine the size of buffer necessary to //perform the download. OUT PUINT puRequiredSize //required lpDownloadPath buffer size. This //parameter is filled in with the minimum size //that is required to place the complete path //file name of the downloaded file. If this //parameter is NULL no size is returned. ) { LOG_Block("DownloadUpdatedFiles"); HRESULT hr; BOOL fRet; if (g_pfnDownloadUpdatedFiles) { if (SUCCEEDED(hr = CoInitialize(0))) { fRet = g_pfnDownloadUpdatedFiles(hConnection, hwnd, pDownloadInfo, lpDownloadPath, uSize, puRequiredSize); CoUninitialize(); return fRet; } else { LOG_ErrorMsg(hr); return FALSE; } } else { LOG_Error(szOpenCDMContextFirst); return FALSE; } } BOOL WINAPI FindMatchingDriver( IN HANDLE hConnection, IN PDOWNLOADINFO pDownloadInfo, OUT PWUDRIVERINFO pWuDriverInfo ) { LOG_Block("FindMatchingDriver"); HRESULT hr; BOOL fRet; if (g_pfnFindMatchingDriver) { if (SUCCEEDED(hr = CoInitialize(0))) { fRet = g_pfnFindMatchingDriver(hConnection, pDownloadInfo, pWuDriverInfo); CoUninitialize(); return fRet; } else { LOG_ErrorMsg(hr); return FALSE; } } else { LOG_Error(szOpenCDMContextFirst); return FALSE; } } // supports offline logging // hConnection NOT used at all // no network connection or osdet.dll needed for languauge, SKU, platform detection void WINAPI LogDriverNotFound( IN HANDLE hConnection, IN LPCWSTR lpDeviceInstanceID, IN DWORD dwFlags ) { LOG_Block("LogDriverNotFound"); HRESULT hr; if (g_pfnLogDriverNotFound) { if (SUCCEEDED(hr = CoInitialize(0))) { g_pfnLogDriverNotFound(hConnection, lpDeviceInstanceID, dwFlags); CoUninitialize(); } else { LOG_ErrorMsg(hr); } } else { LOG_Error(szOpenCDMContextFirst); } } HANDLE WINAPI OpenCDMContext( IN HWND /* hwnd */ //Window handle to use for any UI that needs to be presented (not used) ) { LOG_Block("OpenCDMContext"); return OpenCDMContextEx(TRUE); } HANDLE WINAPI OpenCDMContextEx( IN BOOL fConnectIfNotConnected ) { LOG_Block("OpenCDMContextEx"); // // Don't open a context if we are disabled (0 and -1 OK) // Other functions will fail because their g_pfnXxxxx == NULL. // if (1 == IsWindowsUpdateUserAccessDisabled()) { LOG_ErrorMsg(ERROR_SERVICE_DISABLED); SetLastError(ERROR_SERVICE_DISABLED); return NULL; } // // Doesn't use COM // if (LoadCtlAndEngine(fConnectIfNotConnected)) { // // This is an obsolete function that just loads the engine (which may do autodial to connect). // We just return non-NULL g_lLoadEngineRefCount to keep existing clients happy, but never use it. // return LongToHandle(g_lLoadEngineRefCount); } else { return NULL; } } int WINAPI QueryDetectionFiles( IN HANDLE hConnection, IN void* pCallbackParam, IN PFN_QueryDetectionFilesCallback pCallback ) { LOG_Block("QueryDetectionFiles"); HRESULT hr; int nRet; if (g_pfnQueryDetectionFiles) { if (SUCCEEDED(hr = CoInitialize(0))) { nRet = g_pfnQueryDetectionFiles(hConnection, pCallbackParam, pCallback); CoUninitialize(); return nRet; } else { LOG_ErrorMsg(hr); return 0; } } else { LOG_Error(szOpenCDMContextFirst); return 0; } } // // 502965 Windows Error Reporting bucket 2096553: Hang following NEWDEV.DLL!CancelDriverSearch // // Provide API to allow clients to cancel synchronous calls into CDM by calling this function // asynchronously from a second thread. // HRESULT WINAPI CancelCDMOperation(void) { LOG_Block("CancelCDMOperation"); if (g_pfnSetOperationMode) { return g_pfnSetOperationMode(NULL, NULL, UPDATE_COMMAND_CANCEL); } else { LOG_ErrorMsg(E_ACCESSDENIED); return E_ACCESSDENIED; } }