/****************************************************************************** Copyright (c) 2000 Microsoft Corporation Module Name: HyperLinks.cpp Abstract: This file contains the implementation of the HyperLinks library. Revision History: Davide Massarenti (Dmassare) 11/28/2000 created ******************************************************************************/ #include "stdafx.h" //////////////////////////////////////////////////////////////////////////////// static const DATE l_TIME_hour = 1.0 / 24.0; static const DATE l_TIME_minute = l_TIME_hour / 60.0; static const DATE l_TIME_second = l_TIME_hour / 60.0; static const DATE l_TIME_timeout = l_TIME_minute * 5.0; static const WCHAR l_szMS_ITS [] = L"ms-its:"; static const WCHAR l_szMSITSTORE [] = L"mk:@MSITStore:"; static const WCHAR l_szITS [] = L"its:"; static const WCHAR l_szHOMEPAGE [] = L"hcp://services/centers/homepage"; static const WCHAR l_szSUPPORT [] = L"hcp://services/centers/support"; static const WCHAR l_szOPTIONS [] = L"hcp://services/centers/options"; static const WCHAR l_szUPDATE [] = L"hcp://services/centers/update"; static const WCHAR l_szCOMPAT [] = L"hcp://services/centers/compat"; static const WCHAR l_szTOOLS [] = L"hcp://services/centers/tools"; static const WCHAR l_szERRMSG [] = L"hcp://services/centers/errmsg"; static const WCHAR l_szSEARCH [] = L"hcp://services/search"; static const WCHAR l_szINDEX [] = L"hcp://services/index"; static const WCHAR l_szSUBSITE [] = L"hcp://services/subsite"; static const WCHAR l_szFULLWINDOW [] = L"hcp://services/layout/fullwindow"; static const WCHAR l_szCONTENTONLY[] = L"hcp://services/layout/contentonly"; static const WCHAR l_szKIOSK [] = L"hcp://services/layout/kiosk"; static const WCHAR l_szXML [] = L"hcp://services/layout/xml"; static const WCHAR l_szREDIRECT [] = L"hcp://services/redirect"; static const WCHAR l_szHCP [] = L"hcp://"; static const WCHAR l_szHCP_redir [] = L"hcp:"; static const WCHAR l_szAPPLICATION[] = L"app:"; static const WCHAR l_szRESOURCE [] = L"res://"; ////////////////////////////////////////////////////////////////////////////////// typedef enum { QFT_TEXT , QFT_URL , QFT_TAXONOMY , QFT_APPLICATION, } QueryFieldType; struct QueryField { LPCWSTR szName; QueryFieldType qft; bool fOptional; }; struct Pattern { LPCWSTR szTxt; size_t iLen; /* -1 for complete match */ HyperLinks::Format fmt; const QueryField* rgQueryFields; size_t iQueryFields; }; //////////////////// static const QueryField l_rgTopic [] = { { L"topic" , QFT_URL , false } }; static const QueryField l_rgTopicOpt[] = { { L"topic" , QFT_URL , true } }; static const QueryField l_rgSEARCH [] = { { L"query" , QFT_TEXT , false } , { L"topic" , QFT_URL , true } }; static const QueryField l_rgINDEX [] = { { L"scope" , QFT_APPLICATION, true } , { L"select" , QFT_APPLICATION, true } , { L"topic" , QFT_URL , true } }; static const QueryField l_rgSUBSITE [] = { { L"node" , QFT_TAXONOMY , false } , { L"select" , QFT_TAXONOMY , true } , { L"topic" , QFT_URL , true } }; static const QueryField l_rgXML [] = { { L"definition" , QFT_URL , false } , { L"topic" , QFT_URL , true } }; static const QueryField l_rgREDIRECT[] = { { L"online" , QFT_URL , false } , { L"offline" , QFT_URL , false } }; static const QueryField l_rgAPP [] = { { L"topic" , QFT_URL , true } }; static const Pattern l_rgPattern[] = { { l_szMS_ITS , MAXSTRLEN( l_szMS_ITS ), HyperLinks::FMT_MSITS }, { l_szMSITSTORE , MAXSTRLEN( l_szMSITSTORE ), HyperLinks::FMT_MSITS }, { l_szITS , MAXSTRLEN( l_szITS ), HyperLinks::FMT_MSITS }, //////////////////// { l_szHOMEPAGE , -1 , HyperLinks::FMT_CENTER_HOMEPAGE }, { l_szSUPPORT , -1 , HyperLinks::FMT_CENTER_SUPPORT , l_rgTopicOpt, ARRAYSIZE(l_rgTopicOpt ) }, { l_szOPTIONS , -1 , HyperLinks::FMT_CENTER_OPTIONS , l_rgTopicOpt, ARRAYSIZE(l_rgTopicOpt ) }, { l_szUPDATE , -1 , HyperLinks::FMT_CENTER_UPDATE }, { l_szCOMPAT , -1 , HyperLinks::FMT_CENTER_COMPAT }, { l_szTOOLS , -1 , HyperLinks::FMT_CENTER_TOOLS , l_rgTopicOpt, ARRAYSIZE(l_rgTopicOpt ) }, { l_szERRMSG , -1 , HyperLinks::FMT_CENTER_ERRMSG }, { l_szSEARCH , -1 , HyperLinks::FMT_SEARCH , l_rgSEARCH , ARRAYSIZE(l_rgSEARCH ) }, { l_szINDEX , -1 , HyperLinks::FMT_INDEX , l_rgINDEX , ARRAYSIZE(l_rgINDEX ) }, { l_szSUBSITE , -1 , HyperLinks::FMT_SUBSITE , l_rgSUBSITE , ARRAYSIZE(l_rgSUBSITE ) }, { l_szFULLWINDOW , -1 , HyperLinks::FMT_LAYOUT_FULLWINDOW , l_rgTopic , ARRAYSIZE(l_rgTopic ) }, { l_szCONTENTONLY, -1 , HyperLinks::FMT_LAYOUT_CONTENTONLY, l_rgTopic , ARRAYSIZE(l_rgTopic ) }, { l_szKIOSK , -1 , HyperLinks::FMT_LAYOUT_KIOSK , l_rgTopic , ARRAYSIZE(l_rgTopic ) }, { l_szXML , -1 , HyperLinks::FMT_LAYOUT_XML , l_rgXML , ARRAYSIZE(l_rgXML ) }, { l_szREDIRECT , -1 , HyperLinks::FMT_REDIRECT , l_rgREDIRECT, ARRAYSIZE(l_rgREDIRECT ) }, { l_szHCP , MAXSTRLEN( l_szHCP ), HyperLinks::FMT_HCP }, { l_szHCP_redir , MAXSTRLEN( l_szHCP_redir ), HyperLinks::FMT_HCP_REDIR }, //////////////////// { l_szAPPLICATION, MAXSTRLEN( l_szAPPLICATION ), HyperLinks::FMT_APPLICATION , l_rgAPP , ARRAYSIZE(l_rgAPP ) }, //////////////////// { l_szRESOURCE , MAXSTRLEN( l_szRESOURCE ), HyperLinks::FMT_RESOURCE }, //////////////////// { NULL } }; HyperLinks::ParsedUrl::ParsedUrl() { // MPC::wstring m_strURL; m_fmt = HyperLinks::FMT_INVALID; // Format m_fmt; m_state = HyperLinks::STATE_INVALID; // State m_state; m_dLastChecked = 0; // DATE m_dLastChecked; m_fBackground = true; // bool m_fBackground; // // MPC::wstring m_strBasePart; // MPC::WStringLookup m_mapQuery; // m_hcpRedir = false; // bool m_hcpRedir; } HRESULT HyperLinks::ParsedUrl::Initialize( /*[in]*/ LPCWSTR szURL ) { __HCP_FUNC_ENTRY( "HyperLinks::ParsedUrl::Initialize" ); HRESULT hr; SANITIZEWSTR(szURL); m_strURL = szURL; m_fmt = HyperLinks::FMT_INVALID; m_state = HyperLinks::STATE_NOTPROCESSED; MPC::HTML::ParseHREF( m_strURL.c_str(), m_strBasePart, m_mapQuery ); if(m_strBasePart.size() == 0) { m_state = HyperLinks::STATE_MALFORMED; } else if(MPC::MSITS::IsCHM( m_strBasePart.c_str() )) { m_fmt = HyperLinks::FMT_MSITS; } else { CComBSTR bstrURL( m_strBasePart.c_str() ); if(bstrURL) ::CharLowerW( bstrURL ); szURL = bstrURL; for(const Pattern* ptr=l_rgPattern; ptr->szTxt; ptr++) { int iCmp; if(ptr->iLen) iCmp = wcsncmp( szURL, ptr->szTxt, ptr->iLen ); else iCmp = wcscmp ( szURL, ptr->szTxt ); if(iCmp == 0) { const QueryField* field = ptr->rgQueryFields; for(size_t i=0; iiQueryFields; i++, field++) { CComBSTR bstrValue; if(GetQueryField( field->szName, bstrValue ) == false) { if(field->fOptional == false) { m_state = HyperLinks::STATE_MALFORMED; break; } } else { if(field->qft == QFT_TEXT) { ; } else if(field->qft == QFT_URL) { if(FAILED(IsValid( bstrValue ))) { m_state = HyperLinks::STATE_MALFORMED; break; } } else if(field->qft == QFT_TAXONOMY) { /* TO DO */ } else if(field->qft == QFT_APPLICATION) { /* TO DO */ } } } m_fmt = ptr->fmt; break; } } // // If the URL begins with HCP://, check if it's valid HCP or is HCP redirection // if(m_fmt == HyperLinks::FMT_HCP) { if (CHCPProtocol::IsHCPRedirection(szURL)) { // Treat as HCP redirection m_fmt = HyperLinks::FMT_HCP_REDIR; m_hcpRedir = true; __MPC_SET_ERROR_AND_EXIT(hr, Initialize( m_strURL.c_str() + MAXSTRLEN( l_szHCP ) )); } } // // If the URL begins with HCP: but not HCP://, it's a protocol redirection, so recurse. // if(m_fmt == HyperLinks::FMT_HCP_REDIR) { m_hcpRedir = true; __MPC_SET_ERROR_AND_EXIT(hr, Initialize( m_strURL.c_str() + MAXSTRLEN( l_szHCP_redir ) )); } if(m_fmt == HyperLinks::FMT_INVALID) // Still not resolved... { MPC::URL url; INTERNET_SCHEME scheme; if(SUCCEEDED(url.put_URL ( szURL )) && SUCCEEDED(url.get_Scheme( scheme )) ) { switch(scheme) { case INTERNET_SCHEME_UNKNOWN : m_fmt = HyperLinks::FMT_INTERNET_UNKNOWN ; break; case INTERNET_SCHEME_FTP : m_fmt = HyperLinks::FMT_INTERNET_FTP ; break; case INTERNET_SCHEME_GOPHER : m_fmt = HyperLinks::FMT_INTERNET_GOPHER ; break; case INTERNET_SCHEME_HTTP : m_fmt = HyperLinks::FMT_INTERNET_HTTP ; break; case INTERNET_SCHEME_HTTPS : m_fmt = HyperLinks::FMT_INTERNET_HTTPS ; break; case INTERNET_SCHEME_FILE : m_fmt = HyperLinks::FMT_INTERNET_FILE ; break; case INTERNET_SCHEME_NEWS : m_fmt = HyperLinks::FMT_INTERNET_NEWS ; break; case INTERNET_SCHEME_MAILTO : m_fmt = HyperLinks::FMT_INTERNET_MAILTO ; break; case INTERNET_SCHEME_SOCKS : m_fmt = HyperLinks::FMT_INTERNET_SOCKS ; break; case INTERNET_SCHEME_JAVASCRIPT: m_fmt = HyperLinks::FMT_INTERNET_JAVASCRIPT; break; case INTERNET_SCHEME_VBSCRIPT : m_fmt = HyperLinks::FMT_INTERNET_VBSCRIPT ; break; default : m_state = HyperLinks::STATE_MALFORMED ; break; } } } } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } bool HyperLinks::ParsedUrl::IsLocal() { switch(m_fmt) { case HyperLinks::FMT_INTERNET_UNKNOWN : case HyperLinks::FMT_INTERNET_FTP : case HyperLinks::FMT_INTERNET_GOPHER : case HyperLinks::FMT_INTERNET_HTTP : case HyperLinks::FMT_INTERNET_HTTPS : case HyperLinks::FMT_INTERNET_FILE : case HyperLinks::FMT_INTERNET_NEWS : case HyperLinks::FMT_INTERNET_MAILTO : case HyperLinks::FMT_INTERNET_SOCKS : case HyperLinks::FMT_INTERNET_JAVASCRIPT: case HyperLinks::FMT_INTERNET_VBSCRIPT : return false; case HyperLinks::FMT_MSITS: // // Make sure it's not on a network share! // { CComBSTR bstrStorageName; CComBSTR bstrFilePath; if(MPC::MSITS::IsCHM( m_strURL.c_str(), &bstrStorageName, &bstrFilePath )) { if(wcsncmp( L"\\\\", SAFEBSTR(bstrStorageName), 2 ) == 0) { return false; } } } break; } return true; } HyperLinks::State HyperLinks::ParsedUrl::CheckState( /*[in/out]*/ bool& fFirstWinInetUse ) { HRESULT hr; State state = HyperLinks::STATE_NOTFOUND; LPCWSTR szURL = m_strURL.c_str(); LPCWSTR szEnd; CComBSTR bstrURL; // // Skip the bookmark sign. // if((szEnd = wcschr( szURL, '#' ))) { bstrURL.Attach( ::SysAllocStringLen( szURL, (szEnd - szURL) ) ); szURL = SAFEBSTR( bstrURL ); } switch(m_fmt) { case HyperLinks::FMT_INVALID : state = HyperLinks::STATE_MALFORMED; break; case HyperLinks::FMT_INTERNET_FTP : case HyperLinks::FMT_INTERNET_GOPHER : case HyperLinks::FMT_INTERNET_HTTP : case HyperLinks::FMT_INTERNET_HTTPS : case HyperLinks::FMT_INTERNET_FILE : case HyperLinks::FMT_INTERNET_NEWS : while(1) { DWORD dwTimeout = m_fBackground ? HC_TIMEOUT_LINKCHECKER_BACKGROUND : HC_TIMEOUT_LINKCHECKER_FOREGROUND; if(SUCCEEDED(hr = MPC::Connectivity::DestinationReachable( szURL, dwTimeout ))) { state = HyperLinks::STATE_ALIVE; } else if(hr == E_INVALIDARG) // Unsupported protocol, assume the link is OK. { state = HyperLinks::STATE_ALIVE; } else if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { state = HyperLinks::STATE_NOTFOUND; } else if(hr == HRESULT_FROM_WIN32(ERROR_INTERNET_DISCONNECTED)) { state = HyperLinks::STATE_OFFLINE; } else { // // If it's the first time through, it could be that WinINET couldn't activate the proxy code in time... // if(fFirstWinInetUse == true) { fFirstWinInetUse = false; continue; } if(SUCCEEDED(MPC::Connectivity::NetworkAlive( dwTimeout ))) { state = HyperLinks::STATE_UNREACHABLE; } else { state = HyperLinks::STATE_OFFLINE; } } fFirstWinInetUse = false; break; } break; case HyperLinks::FMT_INTERNET_UNKNOWN : case HyperLinks::FMT_INTERNET_MAILTO : case HyperLinks::FMT_INTERNET_SOCKS : case HyperLinks::FMT_INTERNET_JAVASCRIPT: case HyperLinks::FMT_INTERNET_VBSCRIPT : state = HyperLinks::STATE_ALIVE; break; case HyperLinks::FMT_HCP : { CComPtr obj; state = HyperLinks::STATE_NOTFOUND; if(SUCCEEDED(CHCPProtocol::CreateInstance( &obj ))) { if(SUCCEEDED(obj->Start( szURL, NULL, NULL, 0, NULL ))) { state = HyperLinks::STATE_ALIVE; } } } break; case HyperLinks::FMT_MSITS : { MPC::wstring strUrlModified; MPC::wstring strUrlModified2; CComBSTR bstrStorageName; CComBSTR bstrFilePath; CComPtr stream; state = HyperLinks::STATE_NOTFOUND; CPCHWrapProtocolInfo::NormalizeUrl( szURL , strUrlModified , /*fReverse*/true ); CPCHWrapProtocolInfo::NormalizeUrl( strUrlModified.c_str(), strUrlModified2, /*fReverse*/false ); if(MPC::MSITS::IsCHM( strUrlModified2.c_str(), &bstrStorageName, &bstrFilePath )) { if(SUCCEEDED(MPC::MSITS::OpenAsStream( bstrStorageName, bstrFilePath, &stream ))) { state = HyperLinks::STATE_ALIVE; } } } break; case HyperLinks::FMT_CENTER_HOMEPAGE : case HyperLinks::FMT_CENTER_SUPPORT : case HyperLinks::FMT_CENTER_OPTIONS : case HyperLinks::FMT_CENTER_UPDATE : case HyperLinks::FMT_CENTER_COMPAT : case HyperLinks::FMT_CENTER_TOOLS : case HyperLinks::FMT_CENTER_ERRMSG : state = HyperLinks::STATE_ALIVE; break; case HyperLinks::FMT_SEARCH : case HyperLinks::FMT_INDEX : case HyperLinks::FMT_SUBSITE : state = HyperLinks::STATE_ALIVE; break; case HyperLinks::FMT_LAYOUT_FULLWINDOW : case HyperLinks::FMT_LAYOUT_CONTENTONLY : case HyperLinks::FMT_LAYOUT_KIOSK : case HyperLinks::FMT_LAYOUT_XML : state = HyperLinks::STATE_ALIVE; break; case HyperLinks::FMT_REDIRECT : state = HyperLinks::STATE_ALIVE; break; case HyperLinks::FMT_APPLICATION : state = HyperLinks::STATE_ALIVE; break; case HyperLinks::FMT_RESOURCE : state = HyperLinks::STATE_UNREACHABLE; break; } m_dLastChecked = MPC::GetLocalTime(); return state; } bool HyperLinks::ParsedUrl::IsOkToProceed() { switch(m_state) { case HyperLinks::STATE_NOTPROCESSED: case HyperLinks::STATE_CHECKING : case HyperLinks::STATE_ALIVE : return true; } return false; } bool HyperLinks::ParsedUrl::HasQueryField( /*[in]*/ LPCWSTR szField ) { MPC::WStringLookupIter it = m_mapQuery.find( szField ); return (it != m_mapQuery.end()); } bool HyperLinks::ParsedUrl::GetQueryField( /*[in]*/ LPCWSTR szField, /*[in]*/ CComBSTR& bstrValue ) { MPC::WStringLookupIter it = m_mapQuery.find( szField ); if(it != m_mapQuery.end()) { bstrValue = it->second.c_str(); return true; } bstrValue.Empty(); return false; } //////////////////////////////////////////////////////////////////////////////// HyperLinks::UrlHandle::UrlHandle() { m_main = NULL; // Lookup* m_main; m_pu = NULL; // ParsedUrl* m_pu; } HyperLinks::UrlHandle::~UrlHandle() { Release(); } void HyperLinks::UrlHandle::Attach( /*[in]*/ Lookup* main , /*[in]*/ ParsedUrl* pu ) { Release(); m_main = main; if(main) main->Lock(); m_pu = pu; } void HyperLinks::UrlHandle::Release() { if(m_main) m_main->Unlock(); m_main = NULL; m_pu = NULL; } //////////////////////////////////////////////////////////////////////////////// HyperLinks::Lookup::Lookup() { // PendingUrlList m_lst; // UrlMap m_map; } HyperLinks::Lookup::~Lookup() { Thread_Wait(); } //////////////////// HyperLinks::Lookup* HyperLinks::Lookup::s_GLOBAL( NULL ); HRESULT HyperLinks::Lookup::InitializeSystem() { if(s_GLOBAL == NULL) { s_GLOBAL = new HyperLinks::Lookup; } return s_GLOBAL ? S_OK : E_OUTOFMEMORY; } void HyperLinks::Lookup::FinalizeSystem() { if(s_GLOBAL) { delete s_GLOBAL; s_GLOBAL = NULL; } } //////////////////// HRESULT HyperLinks::Lookup::RunChecker() { __HCP_FUNC_ENTRY( "HyperLinks::Lookup::RunChecker" ); HRESULT hr; MPC::SmartLock<_ThreadModel> lock( this ); bool fFirstWinInetUse = true; Thread_SignalMain(); while(Thread_IsAborted() == false) { bool fSleep = true; ParsedUrl* urlBest = NULL; PendingUrlIter it; // // Look for the first query store not ready and execute it. // for(it = m_lst.begin(); it != m_lst.end();) { ParsedUrl* url = *it; if(url->m_state != HyperLinks::STATE_NOTPROCESSED) { m_lst.erase( it ); it = m_lst.begin(); urlBest = NULL; } else { if(url->m_fBackground == false) { urlBest = url; break; } urlBest = url; it++; } } if(urlBest) { State state = HyperLinks::STATE_NOTFOUND; // // Remove this query from the pending list. // for(it = m_lst.begin(); it != m_lst.end(); ) { if(*it == urlBest) { m_lst.erase( it ); it = m_lst.begin(); } else { it++; } } DebugLog( L"%%%%%%%%%%%%%%%%%%%% CHECKING %s\n", urlBest->m_strURL.c_str() ); urlBest->m_state = HyperLinks::STATE_CHECKING; lock = NULL; __MPC_PROTECT( state = urlBest->CheckState( fFirstWinInetUse ) ); lock = this; urlBest->m_state = state; Thread_SignalMain(); fSleep = false; } if(fSleep) { lock = NULL; Thread_WaitForEvents( NULL, INFINITE ); lock = this; } } hr = S_OK; Thread_Abort(); __HCP_FUNC_EXIT(hr); } HRESULT HyperLinks::Lookup::CreateItem( /*[in ]*/ LPCWSTR szURL , /*[out]*/ ParsedUrl*& pu ) { __HCP_FUNC_ENTRY( "HyperLinks::Lookup::CreateItem" ); HRESULT hr; UrlIter it; MPC::wstringUC strURL( SAFEWSTR( szURL ) ); it = m_map.find( strURL ); if(it == m_map.end()) { pu = &(m_map[ strURL ]); __MPC_EXIT_IF_METHOD_FAILS(hr, pu->Initialize( szURL )); } else { pu = &it->second; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////// HRESULT HyperLinks::Lookup::Queue( /*[in]*/ LPCWSTR szURL ) { UrlHandle uh; return Get( szURL, uh ); } HRESULT HyperLinks::Lookup::Get( /*[in]*/ LPCWSTR szURL , /*[in]*/ UrlHandle& uh , /*[in]*/ DWORD dwWaitForCheck , /*[in]*/ bool fForce ) { __HCP_FUNC_ENTRY( "HyperLinks::Lookup::Get" ); HRESULT hr; MPC::SmartLock<_ThreadModel> lock( this ); ParsedUrl* pu = NULL; uh.Release(); //////////////////////////////////////////////////////////////////////////////// if(Thread_IsRunning() == false) { lock = NULL; __MPC_EXIT_IF_METHOD_FAILS(hr, Thread_Start( this, RunChecker, NULL )); Thread_WaitNotificationFromWorker( INFINITE, /*fNoMessagePump*/true ); lock = this; } //////////////////////////////////////////////////////////////////////////////// __MPC_EXIT_IF_METHOD_FAILS(hr, CreateItem( szURL, pu )); if(fForce) { pu->m_state = HyperLinks::STATE_NOTPROCESSED; } switch(pu->m_state) { case HyperLinks::STATE_ALIVE : case HyperLinks::STATE_NOTFOUND : case HyperLinks::STATE_UNREACHABLE: case HyperLinks::STATE_OFFLINE : if(dwWaitForCheck) { // // Make sure the state is not stale. // DATE dNow = MPC::GetLocalTime(); if((dNow - pu->m_dLastChecked) >= l_TIME_timeout) { pu->m_state = HyperLinks::STATE_NOTPROCESSED; } } break; } if(pu->m_state == HyperLinks::STATE_NOTPROCESSED) { bool fQueue = false; bool fWait = false; if(dwWaitForCheck) { // // Elevate the URL to "important". // pu->m_fBackground = false; fWait = true; if(pu->IsLocal() == false) { fQueue = true; } } else { fQueue = true; } if(fQueue) { m_lst.push_back( pu ); Thread_Signal(); } if(fWait) { if(pu->IsLocal()) { bool fFirstWinInetUse = false; pu->m_state = pu->CheckState( fFirstWinInetUse ); } else { int iRetry = 5; dwWaitForCheck /= iRetry; while(pu->m_state == HyperLinks::STATE_NOTPROCESSED || pu->m_state == HyperLinks::STATE_CHECKING ) { DWORD dwRes; lock = NULL; dwRes = Thread_WaitNotificationFromWorker( dwWaitForCheck, /*fNoMessagePump*/true ); lock = this; if(iRetry-- == 0) { __MPC_SET_ERROR_AND_EXIT(hr, S_FALSE); } } } } } hr = S_OK; __HCP_FUNC_CLEANUP; if(pu) uh.Attach( this, pu ); __HCP_FUNC_EXIT(hr); } //////////////////////////////////////////////////////////////////////////////// HRESULT HyperLinks::IsValid( /*[in]*/ LPCWSTR szURL ) { __HCP_FUNC_ENTRY( "HyperLinks::IsValid" ); HRESULT hr; ParsedUrl pu; __MPC_EXIT_IF_METHOD_FAILS(hr, pu.Initialize( szURL )); switch(pu.m_state) { case HyperLinks::STATE_INVALID : case HyperLinks::STATE_MALFORMED: __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL); } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); }