在用InternetOpenUrl下载网页的时候,一般网页打不开的时候该函数都会正常返回,但是偶尔会出现网页打不开的时候,程序就一直停在那里,导致本来计划好的下载任务都没有正常进行下去,代码如下:
网页下载类的主要代码:
ibol CMyWeb3::Get( iHInternet hSession, char *psUrl, iuint flag )
{
m_ps[0] = '\0';
m_len = 0;
iHInternet hFile = InternetOpenUrlA( hSession, psUrl, inull, 0, flag, 1);
if( hFile==inull )
{
P( "InternetOpenUrlA fail" );
return 0;
}
ReadData( hFile );
InternetCloseHandle( hFile );
if( m_len<=0 )
{
P( " m_len<=0 " );
return 0;
}
return 1;
}
void CMyWeb3::ReadData( iHInternet hFile )
{
int len = 0;
int ci = 0;
while( ci<30 )
{
m_len = 0;
if( !InternetReadFile( hFile, (void*)(&m_ps[len]), 1024, &m_len) )
{
P_Err();
ci ;
Sleep( 100 );
continue;
}
if( m_len<=0 )
{
ci ;
Sleep( 100 );
continue;
}
len = len m_len;
}
if( len>0 )
{
m_ps[len] = '\0';
m_len = len;
}
else
{
m_len = 0;
m_ps[0] = '\0';
}
}
具体操作类:
class CDoNr
{
public:
iSystemTime m_tm;
private:
int m_NrId;
char *m_ps;
int m_len;
CMyDb m_db;
CMyWeb3 m_web;
iHInternet m_hSession;
public:
void Get( int id );
void Init();
private:
ibol Get1( char *psWz, char *psBt );
public:
CDoNr();
~CDoNr();
};
CDoNr::CDoNr()
{
m_len = 8192 * 2000;
m_ps = new char[m_len];
m_hSession = inull;
}
CDoNr::~CDoNr()
{
Del_Zu( m_ps );
if( m_hSession!=inull ) InternetCloseHandle( m_hSession );
}
void CDoNr::Init()
{
::GetLocalTime( &m_tm );
m_hSession = InternetOpenA( "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)", iPre_Config_Internet_Access, inull, inull, 0);
char s[1024];
sprintf_s( s, 1024, "%s\\Dt\\db.mdb", g_can.m_dir );
m_db.OpenAccess( s );
}
void CDoNr::Get( int id )
{
//从数据库中读取网页
char wz[1024], bt1[1024], bt2[1024], s[1024];
int min = id * 10000, max = (id 1) * 10000;
sprintf_s( s, 1024, "Select * from Nr where DownOk=0 and IDME>=%d and IDME<=%d ", min, max );
m_db.Open( s );
int shu = m_db.m_set.m_set->RecordCount;
int ge = 0;
while( !m_db.adoEOF() )
{
if( g_can.m_stop )
{
P("暂停退出 1");
break;
}
::GetLocalTime( &m_tm );
m_db.Get( "wz", wz, 1024 ); //获取网址
m_db.Get( "ming", bt1, 1024 );
m_db.Get( "IDME", m_NrId );
P1("------- %d & %d / %d------", ge , m_NrId, shu );
if( Get1( wz, bt2 ) )
{
m_db.Put( "DownOk", 1 );
m_db.Put( "ming", bt1 );
m_db.Update();
}
m_db.MoveNext();
}
m_db.Close();
P("已成功完成");
}
//
ibol CDoNr::Get1( char *psWz, char *psBt )
{
char s[1024];
if( !m_web.Get( m_hSession, psWz ) )
{
P("下载失败");
return 0;
}
.....
return 1;
}
看一下InternetOpenUrl的函数说明:InternetOpenUrl:Opens a resource specified by a complete FTP, Gopher, or HTTP URL.打开 Url,读取数据
hInternet :[in] Handle to the current Internet session. The handle must have been returned by a previous call to InternetOpen. 当前的 Internet 会话句柄。句柄必须由前期的 InternetOpen 调用返回
lpszUrl :[in] Pointer to a null-terminated string variable that specifies the URL to begin reading. Only URLs beginning with ftp:, gopher:, http:, or https: are supported. 一个空字符结束的字符串变量的指针,指定读取的网址。只有以ftp:, gopher:, http:, 或者 https: 开头的网址被支持
lpszHeaders :[in] Pointer to a null-terminated string that specifies the headers to be sent to the HTTP server. (For more information, see the description of the lpszHeaders parameter in the HttpSendRequest function.) 一个空字符结束的字符串变量的指针,指定发送到HTTP服务器的头信息。欲了解更多信息,请参阅HttpSendRequest函数里lpszHeaders参数的说明
dwHeadersLength:[in] Size of the additional headers, in TCHARs. If this parameter is -1L and lpszHeaders is not NULL, lpszHeaders is assumed to be zero-terminated (ASCIIZ) and the length is calculated.
dwContext :[in] Pointer to a variable that specifies the application-defined value that is passed, along with the returned handle, to any callback functions. 一个指向一个应用程序定义的值,将随着返回的句柄,一起传递给回调函数
Returns a valid handle to the FTP, Gopher, or HTTP URL if the connection is successfully established, or NULL if the connection fails. To retrieve a specific error message, call GetLastError. To determine why access to the service was denied, call InternetGetLastResponseInfo.如果已成功建立到FTP,Gopher,或HTTP URL的连接,返回一个有效的句柄,如果连接失败返回NULL。要检索特定的错误讯息,请GetLastError 。要确定为什么对服务器的访问被拒绝,请调用InternetGetLastResponseInfo
Call InternetCanonicalizeUrl first if the URL being used contains a relative URL and a base URL separated by blank spaces.先调用InternetCanonicalizeUrl,如果正在使用的网址包含一个相对URL和一个空格分隔的基础URL
This is a general function that an application can use to retrieve data over any of the protocols that WinINet supports. This function is especially useful when the application does not need to access the particulars of a protocol, but only requires the data corresponding to a URL. The InternetOpenUrl function parses the URL string, establishes a connection to the server, and prepares to download the data identified by the URL. The application can then use InternetReadFile (for files) or InternetFindNextFile (for directories) to retrieve the URL data. It is not necessary to call InternetConnect before InternetOpenUrl.这是一个通用的函数,可用于使用任何WinINet支持的协议检索数据。这个函数在应用程序并不需要指定特定的协议,只需要相应的URL的数据时,特别有用。InternetOpenUrl函数解析URL字符串,建立连接到服务器,并准备下载的指定URL的数据。该应用程序可以用InternetReadFile (对文件)或InternetFindNextFile (对目录)来检索URL的数据。没有必要在InternetOpenUrl 前调用InternetConnect
InternetOpenUrl disables Gopher on ports less than 1024, except for port 70—the standard Gopher port—and port 105—typically used for Central Services Organization (CSO) name searches.InternetOpenUrl在少于1024端口上禁用Gopher,除了70——标准的Gopher端口和105——通常用于中央服务组织(民间组织)的名字搜索
After the calling application has finished using the HINTERNET handle returned by InternetOpenUrl, it must be closed using the InternetCloseHandle function.在使用完InternetOpenUrl返回的HINTERNET句柄后,必须使用InternetCloseHandle函数关闭它。
注意 :当工作在异步模式(InternetOpen的dwFlags参数指定INTERNET_FLAG_ASYNC)和dwContext参数是零 (INTERNET_NO_CALLBACK),使用InternetSetStatusCallback函数设置的回调,将不被引用,但是,将仍然在异步模式执行回调。
好像没有对解决这个问题有帮助的信息。
为了解决InternetOpenUrl卡住的问题,尝试了设置超时退出:
int tout = 3 * 60 * 1000;
InternetSetOptionA( m_hSession, iInternet_Option_Connect_Timeout, (void *)&tout, sizeof(int) );
InternetSetOptionA( m_hSession, iInternet_Option_Send_Timeout, (void *)&tout, sizeof(int) );
InternetSetOptionA( m_hSession, iInternet_Option_Receive_Timeout, (void *)&tout, sizeof(int) );
但是依然没有解决问题。
所以只好设置了用多线程的方式解决,具体解决方式就是:用线程执行下载网页的操作,然后在每次下载好一个网页后更新一下对应的时间,设置一个定时器,查询该时间是否超过几分钟(比如超过7分钟)那就说明InternetOpenUrl卡住了,使用以下代码
TerminateThread( m_h[a], 0 ); //中断线程
::WaitForSingleObject( m_h[a], iInfInite ); //等待线程结束
先强行中断线程,然后等待线程结束后,再重开一个线程
贴上代码
线程类:
#define dShu 4
class CXian
{
public:
private:
iHandle m_h[4];
iSystemTime m_tm;
public:
void Begin();
void Check();
void Tui();
private:
public:
CXian();
~CXian();
};
CXian::CXian()
{
for(int a=0; a<dShu; a ) m_h[a] = inull;
}
CXian::~CXian()
{
}
iuint iStdCall Proc2( void *p )
{
int zhi = (int)p;
g_DoNr[zhi].Get( zhi );
return 1;
}
void CXian::Begin()
{
for(int a=0; a<dShu; a )
{
g_DoNr[a].Init(); //独立初始化一下
m_h[a] = CreateThread( inull, 0, Proc2, (void*)a, 0, inull );
}
}
void CXian::Check()
{
::GetLocalTime( &m_tm );
int c = 0;
for(int a=0; a<dShu; a )
{
c = DiffMinute( &g_DoNr[a].m_tm, &m_tm );
if( c>7 )
{
TerminateThread( m_h[a], 0 ); //中断线程
::WaitForSingleObject( m_h[a], iInfInite ); //等待线程结束
//重启线程
m_h[a] = CreateThread( inull, 0, Proc2, (void*)a, 0, inull );
}
}
}
void CXian::Tui()
{
for(int a=0; a<dShu; a )
{
::WaitForSingleObject( m_h[a], iInfInite );
}
exit(0);
}
线程开始代码:
g_Xian.Begin();
SetTimer( g_hwnd, 1, 5 * 30 * 1000, TmProc ); //设置2.5分钟一个定时器
定时器响应函数
void iStdCall TmProc(iHWnd hwnd, iuint msg, iuint id, iuint dwTime)
{
PostMessage( g_hwnd ,dMyMsg1, 0, 0 );
}
在消息响应中增加
case dMyMsg1:
g_Xian.Check();
break;
通过以上操作完美的解决了InternetOpenUrl卡住的问题