使用InternetOpenUrl挂起的一个解决方案

在某个同事留下的网络操作代码中,如果用户试图从网络打开一个实际并不存在的URL, 走到InternetOpenUrl这步,应用程序就一命呜呼了。这个API并没有按照文档上说的那样返回一个NULL给文件HANDLE,而是完全停止响应。他尝试使用InternetSetOption来设置超时的时限,代码里这样写

DWORD n = 10000;
InternetSetOption(p->Internet, INTERNET_OPTION_CONNECT_TIMEOUT, &n, sizeof(n));

但没有实际效果。从后文可以猜测,InternetOpenUrl再去调用了网络的IO处理,在IO处理中被卡死,罪过似乎并不在InternetOpenUrl和wininet.dll这层上.

于是他在前面某次工作记录里说已经解决了这个问题,然后又被测试组骂回来说没效果. 我搜到了开源代码里一个很好的算法, 自己测试了下,的确有效。让我们来看下它的算法,确实很精妙. 整个算法的核心是用异步方式来处理wininet.dll提供的那些函数


具体实现代码如下:

(1) 首先, 在InternetOpen时修改了参数
   它用的是  InternetOpen(T("MyApp"), p->AccessType, NULL,NULL, INTERNET_FLAG_ASYNC);  
   而会卡死的情况下用的是 InternetOpen(T("AmoiPlayer"),INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
   前面几个参数不是影响该性能的关键,关键点在最后一个参数上: INTERNET_FLAG_ASYNC. 在文档里对这个参数的解释是 Makes only asynchronous requests on handles descended from the handle returned from this function.  写得有些晦涩. 实际效果就是如果在InternetOpen中设置为INTERNET_FLAG_ASYNC, 那么在InternetOpenUrl时就会立刻返回,  如果为NULL就必须有具体的返回值才肯RETRUN.

(2) 注册一个CALLBACK函数  InternetSetStatusCallback(p->Internet, InternetCallback)
      因为我们OpenURL操作会立刻返回了,那么什么时候能得到有效或无效的HANDLE呢? 这就需要一个CALLBACK来做这件事情.  这里当然有必要看下这个CALLBACK函数里做了什么

static   void  CALLBACK InternetCallback(HINTERNET hInternet,
                               DWORD dwContext,
                               DWORD dwInternetStatus,
                               LPVOID lpvStatusInformation,
                               DWORD dwStatusInformationLength)
{
    
if  (dwInternetStatus  ==  INTERNET_STATUS_REQUEST_COMPLETE)
    {
        INTERNET_ASYNC_RESULT 
* Result  =  (INTERNET_ASYNC_RESULT * )lpvStatusInformation;
        http 
* =  (http * )dwContext;
        p
-> CompleteResult  =  Result -> dwResult;
        SetEvent(p
-> Complete);
    }
}


当等到INTERNET_STATUS_REQUEST_COMPLETE通知的时候, 把RESULT获取下来放在p->CompleteResult里面,然后SetEvent. 也就是说系统CALLBACK线程和其他什么线程进行通信了

(3) 在InternetOpenUrl的时候代码如下:

Result  =  (DWORD)InternetOpenUrl(p -> Internet, URL, s, (DWORD) - 1 , INTERNET_FLAG_NO_CACHE_WRITE | Secure, (DWORD)p);

p
-> Handle  =  (HANDLE)Pending(p, Result ,  30 );

if  (Result  ||  GetLastError()  !=  ERROR_IO_PENDING)
{
  p
-> Handle  =  Result;
}
else

  p
-> Pending  =   1 ;
  
if  ( WaitForSingleObject(p -> Complete, 300000 ==  WAIT_OBJECT_0 )
  {
    p
-> Pending  = 0 ;
    p
-> Handle   =  p -> CompleteResult;
  }
  
else
  {
    p
-> Handle  =  NULL;
  }
}

 
代码中把流程图中的左边线程, 从调用API触发CALLBACK开始, 到底端取得HANDEL这块算法封装为一个函数, 可以被其他对wininet.dll接口函数的操作共用, 如InternetReadFile等.

 

文章转自:

http://xjchilli.blog.163.com/blog/static/453477392010031102413517/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值