如何使用 WinINet 查找网页是不是存在

Posted

技术标签:

【中文标题】如何使用 WinINet 查找网页是不是存在【英文标题】:How can I find if a webpage exists using WinINet如何使用 WinINet 查找网页是否存在 【发布时间】:2012-11-26 12:33:31 【问题描述】:

我正在尝试连接并确保我们设计的仪器提供的网络服务器上存在各种页面。我正在尝试使用 WinInet 命令通过 C++ Win32 执行此操作。

很高兴我已通过 HTTP 正确连接到网络服务器:

hInternet = InternetOpen("Test", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0/*INTERNET_FLAG_ASYNC*/);
hhttp = InternetConnect(hInternet, "192.168.111.222", INTERNET_DEFAULT_HTTP_PORT, "admin", "admin", INTERNET_SERVICE_HTTP, 0, 0);

我相信我必须打开一个请求。

hHttpRequest = HttpOpenRequest(hhttp, "GET", "galogo.png", NULL, "192.168.111.222", lplpszAcceptTypes, INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE, 0);

然后发送请求。

HttpSendRequest(hHttpRequest, NULL, 0, NULL, 0)

注意:192.168.111.222 是运行网络服务器的单元的地址,galogo.png 是显示在主页上的图像。另请注意,我在每个语句之间进行错误检查,因此如果我断开以太网连接,那么我确实会失败。

最初我确实尝试连接到 home.html 页面,但这总是通过,所以我想我应该尝试获取图像,但我可能缺乏知识。其他示例似乎随后会流式传输数据,但我不确定是否需要这样做。

我看到的大多数示例似乎都以这种格式显示 HtppSendRequest,我不太了解标头等。也许在这里我出错了。

【问题讨论】:

Google“HTTP HEAD 请求”(非常重要:确保确保包含该搜索的 HTTP 部分)。我想它可能会给你一两个提示。 看 google HEAD 只是 GET 的一种变体,它只检索标头。我还没有尝试实施它,但这不会让我在同一条船上吗? 【参考方案1】:

HttpQueryInfo 函数将给出与请求相关的标头信息,您可以从中提取 HTTP 状态代码。

您可以使用更高级别的 WinINet 函数更轻松地获得结果。如果 HTTP 状态代码正常,我会建议包含 InternetOpen、InternetOpenUrl、HttpQueryInfo 的序列,然后重复调用 InternetReadFile。

这个 Delphi 代码(来自 Delphi 7,所以在 Unicode 之前)似乎可以完成这项工作:-

function GetUrlContent(const Agent, Url: string): string;
var
  NetHandle: HINTERNET;
  UrlHandle: HINTERNET;
  Buffer: array [0..1024] of Char;
  BytesRead: DWORD;
  Dummy: DWORD;
  BufLen: DWORD;
  HttpStatus: string;
begin
  Result := '';
  NetHandle := InternetOpen(PChar(Agent), INTERNET_OPEN_TYPE_PRECONFIG,
    nil, nil, 0);

  if Assigned(NetHandle) then
  begin
    UrlHandle := InternetOpenUrl(NetHandle, PChar(Url), nil, 0,
      INTERNET_FLAG_RELOAD, 0);

    if Assigned(UrlHandle) then
    // UrlHandle valid?  Proceed with download.
    try
      BufLen := Length(Buffer);
      Dummy := 0;
      // only get the file if the HTTP status code is 200
      if HttpQueryInfo(UrlHandle, HTTP_QUERY_STATUS_CODE, @Buffer[0], BufLen, Dummy) then
      begin
        HttpStatus := Buffer;
        if HttpStatus = '200' then
        begin
          FillChar(Buffer, SizeOf(Buffer), 0);
          repeat
            Result := Result + Buffer;
            FillChar(Buffer, SizeOf(Buffer), 0);
            InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
          until BytesRead = 0;
        end
        else begin
          raise Exception.CreateFmt('HTTP status code %s', [HttpStatus]);
        end;
      end
      else begin
        raise Exception.Create('Unable to read HTTP status code');
      end;
    finally
      InternetCloseHandle(UrlHandle);
    end
    else begin
      // UrlHandle is not valid.  Raise an exception.
      raise Exception.CreateFmt('Cannot open URL %s', [Url]);
    end;
    InternetCloseHandle(NetHandle);
  end
  else begin
    // NetHandle is not valid.  Raise an exception.
    raise Exception.Create('Unable to initialize WinINet');
  end;
end;

【讨论】:

感谢您的信息。我正在尝试使用 InternetOpenUrl 进行连接,并且假设我需要发送用户名和密码。显然我可以使用标题或通过 url 字符串来做到这一点。我写过: hhttp = InternetOpenUrl(hInternet, "http:////admin:admin@192.168.111.222//home.html", NULL, 0, INTERNET_FLAG_RELOAD, 0);既不返回句柄也不返回错误? 当我寻找 InternetOpenUrl 和用户名/密码时,一般的共识是使用 InternetConnect(因为这样可以指定它们)。【参考方案2】:

所以,结合使用 cURL 和 Wireshark,我终于成功了。我犯了一些根本性的错误,但基本上是在正确的轨道上。

首先打开连接,如前所述连接,确保它不是ASYNC(这会导致一些重叠的IO错误):

hInternet = InternetOpen("Test", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0/*INTERNET_FLAG_ASYNC*/);

hhttp = InternetConnect(hInternet, "192.168.111.222", INTERNET_DEFAULT_HTTP_PORT, "admin", "admin", INTERNET_SERVICE_HTTP, 0, 0);

我需要创建请求然后发送它。我只需要指定页面,因为请求将包含连接详细信息。

hHttpRequest = HttpOpenRequest(hhttp, "GET", "home.html", NULL, NULL, lplpszAcceptTypes, INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE, 0);

HttpSendRequest(hHttpRequest, NULL, 0, NULL, 0);

然后使用 HttpQueryInfo 函数检索状态并转换回整数。确保您从请求而不是连接发送句柄。

//These are defined earlier
DWORD statCharLen = 0;
char statChar[256]="";    
statCharLen = sizeof(statChar);

HttpQueryInfo(hHttpRequest, HTTP_QUERY_STATUS_CODE, statChar, &statCharLen, NULL);

最后关闭连接:

InternetCloseHandle(hInternet)

谢谢

【讨论】:

如您所见,我从尝试访问图像转移到网页(这就是我所需要的),但两者都有效。【参考方案3】:

以下是简单的步骤: 1-打开连接 2-连接 3- 打开请求 4-发送请求 5-读取文件 6- 保存文件(为 png 或 jpg) 7-关闭手柄 代码如下:

#include <iostream>
#include <string>
#include <Windows.h>
#include <wininet.h>
#pragma comment(lib, "wininet")

using namespace std;
void download(string domain,string url,string filepath)

    //Step 1:
    HINTERNET hIntSession = InternetOpenA("MyApp", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
    //Step 2:
    HINTERNET hHttpSession = InternetConnectA(hIntSession, domain.c_str(), 80, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL);
    //Step 3:
    HINTERNET hHttpRequest = HttpOpenRequestA( hHttpSession, "GET",url.c_str(),0, 0, 0, INTERNET_FLAG_RELOAD, 0);
    TCHAR* szHeaders = L"";
    CHAR szReq[1024] = "";
    //Step 4:
    if( !HttpSendRequest(hHttpRequest, szHeaders, wcslen(szHeaders), szReq, strlen(szReq))) 
        DWORD dwErr = GetLastError();
        cout<<"error "<<dwErr<<endl;
        /// handle error
    
    TCHAR szBuffer[1025];
    DWORD dwRead=0;
    FILE *f;
    f=fopen(filepath.c_str(),"wb");
    //Step 5 & 6:
    while(InternetReadFile(hHttpRequest,szBuffer, 1024, &dwRead) && dwRead)
    
        fwrite(szBuffer,sizeof(BYTE),1024,f);
        dwRead=0;
    
    fclose(f);
    //Step 7:
    InternetCloseHandle(hHttpRequest);
    InternetCloseHandle(hHttpSession);
    InternetCloseHandle(hIntSession);

int main()

    download("www.stacktoheap.com","images/***.png","C:\\Example\\example.png");

【讨论】:

以上是关于如何使用 WinINet 查找网页是不是存在的主要内容,如果未能解决你的问题,请参考以下文章

在 wininet 中使用 InternetConnect() API 后,如何判断我是不是仍然连接?

如何检查网页是不是存在。 jQuery 和/或 PHP

如何以编程方式清除 MSIE/WinInet 缓存?

使用“wininet”和“windows.h”时如何解决“IServiceProvider”不明确?

C++ wininet,连接weblogin,如何设置cookies?

如何以编程方式清除 WinInet SSL 状态(是不是有 Windows API 调用)?