处理统一资源定位符

统一资源定位符 (URL) 是位于 Internet 上的资源的位置和访问方法的紧凑表示形式。 每个 URL 由一个 (HTTP、HTTPS 或 FTP) 的方案以及一个特定于方案的字符串组成。 此字符串还可以包含目录路径、搜索字符串或资源名称的组合。 WinINet 函数提供创建、合并、分解和规范 URL 的功能。 有关 URL 的详细信息,请参阅统一资源定位符上的 RFC-1738 (URL) 。

URL 函数以面向任务的方式运行。 未验证提供给函数的 URL 的内容和格式。 调用应用程序应跟踪这些函数的使用情况,以确保数据采用预期格式。 例如,不使用标志时, InternetCanonicalizeUrl 函数会将字符“%”转换为转义序列“%25”。 如果在规范化的 URL 上使用 InternetCanonicalizeUrl ,则转义序列“%25”将转换为转义序列“%2525”,这将无法正常工作。

什么是规范化 URL?

所有 URL 的格式必须遵循接受的语法和语义,才能通过 Internet 访问资源。 规范化是设置 URL 格式以遵循此接受的语法和语义的过程。

必须编码的字符包括 US-ASCII 编码字符集中没有相应图形字符的任何字符 (十六进制 80-FF,这些字符在 US-ASCII 编码的字符集中不使用,以及十六进制 00-1F 和 7F,这些字符是控制字符) 、空格、“%” ((用于对其他字符) 进行编码)和不安全字符 (<, >、“、#、{、}、|、\、^、~、[、]和') 。

使用 WinINet 函数处理 URL

下表汇总了 URL 函数。

函数 说明
InternetCanonicalizeUrl 规范化 URL。
InternetCombineUrl 组合基 URL 和相对 URL。
InternetCrackUrl 将 URL 字符串分析为组件。
InternetCreateUrl 从组件创建 URL 字符串。
InternetOpenUrl 开始检索 FTP、HTTP 或 HTTPS 资源。

 

规范化 URL

规范化 URL 是将 URL(可能包含不安全字符(如空格、保留字符等)转换为接受格式的过程。

InternetCanonicalizeUrl 函数可用于规范 URL。 此函数非常面向任务,因此应用程序应仔细跟踪其使用情况。 InternetCanonicalizeUrl 不验证传递给它的 URL 是否已规范化,并且它返回的 URL 是否有效。

以下五个标志控制 InternetCanonicalizeUrl 如何处理特定 URL。 标志可以组合使用。 如果未使用标志,则函数默认对 URL 进行编码。

含义
ICU_BROWSER_MODE 不要对“#”或“?”后面的字符进行编码或解码,也不要删除“?”后面的尾随空格。 如果未指定此值,则会对整个 URL 进行编码,并删除尾随空格。
ICU_DECODE 在分析 URL 之前,将所有 %XX 序列转换为字符,包括转义序列。
ICU_ENCODE_SPACES_ONLY 仅对空格进行编码。
ICU_NO_ENCODE 不要将不安全字符转换为转义序列。
ICU_NO_META 请勿删除 (元序列,例如“.”和“.”。从 URL ) 。

 

ICU_DECODE标志应仅用于规范化的 URL,因为它假定所有 %XX 序列都是转义代码,并将其转换为代码指示的字符。 如果 URL 中包含不属于转义代码的“%”符号,ICU_DECODE仍将其视为一个。 此特征可能导致 InternetCanonicalizeUrl 创建无效 URL。

若要使用 InternetCanonicalizeUrl 返回完全解码的 URL,必须指定ICU_DECODE和ICU_NO_ENCODE标志。 此设置假定传递给 InternetCanonicalizeUrl 的 URL 以前已规范化。

组合基 URL 和相对 URL

相对 URL 是相对于绝对基 URL 的资源位置的紧凑表示形式。 分析程序必须知道基 URL,通常包括方案、网络位置和 URL 路径的一部分。 应用程序可以调用 InternetCombineUrl ,将相对 URL 与其基 URL 组合在一起。 InternetCombineUrl 还规范化生成的 URL。

破解 URL

InternetCrackUrl 函数将 URL 分隔为其组件部分,并返回传递给函数的 URL_COMPONENTS 结构所指示的组件。

构成 URL_COMPONENTS 结构的组件包括方案编号、主机名、端口号、用户名、密码、URL 路径以及其他 (信息,例如搜索参数) 。 除方案和端口号外,每个组件都有一个保存信息的字符串成员,以及一个包含字符串成员长度的成员。 方案和端口号只有一个用于存储相应值的成员;它们都是在对 InternetCrackUrl 的所有成功调用中返回的。

若要获取 URL_COMPONENTS 结构中特定组件的值,存储该组件的字符串长度的成员必须设置为非零值。 字符串成员可以是缓冲区的地址,也可以是 NULL

如果指针成员包含缓冲区的地址,则字符串长度成员必须包含该缓冲区的大小。 InternetCrackUrl 以字符串的形式返回组件信息,并将字符串长度存储在字符串长度成员中。

如果指针成员为 NULL,则可以将字符串长度成员设置为任何非零值。 InternetCrackUrl 存储包含组件信息的 URL 字符串的第一个字符的地址,并将字符串长度设置为与组件相关的 URL 字符串剩余部分的字符数。

所有设置为 NULL 的指针成员,且非零长度成员点指向 URL 字符串中的相应起始点。 必须使用存储在 length 成员中的长度来确定单个组件信息的末尾。

若要正确完成 URL_COMPONENTS 结构的初始化,必须将 dwStructSize 成员设置为 URL_COMPONENTS 结构的大小(以字节为单位)。

以下示例在编辑框中返回 URL 的组件,IDC_PreOpen1,并将组件返回到列表框,IDC_PreOpenList。 为了仅显示单个组件的信息,此函数会立即复制字符串中组件信息之后的字符,并暂时将其替换为 NULL

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include <wininet.h>
#include <stdlib.h>

#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "user32.lib")

#define  CRACKER_BUFFER_SIZE           MAX_PATH

// For sample source code implementing the InternetErrorOut( ) 
// function referenced below, see the "Handling Errors" topic  
// under "Using WinInet"
extern BOOL WINAPI InternetErrorOut( HWND hWnd, DWORD dwError,
                                     LPCTSTR szFailingFunctionName );

// Forward declaration of listUrlPart helper functions:
BOOL listURLpart( HWND hDlg, int nListBoxID, 
                  LPTSTR szPartName, LPTSTR part, DWORD partLength );
BOOL listURLpart( HWND hDlg, int nListBoxID, 
                  LPTSTR szPartName, int partValue );

// Static list describing the URL Scheme types 
// enumerated in INTERNET_SCHEME:
TCHAR* schemeType[] =
{
  TEXT( "[Partial URL]" ),                //  0
  TEXT( "[Unknown scheme]" ),             //  1
  TEXT( "[Default scheme]" ),             //  2
  TEXT( "FTP" ),                          //  3
  TEXT( "Gopher" ),                       //  4
  TEXT( "HTTP" ),                         //  5
  TEXT( "HTTPS" ),                        //  6
  TEXT( "File" ),                         //  7
  TEXT( "News" ),                         //  8
  TEXT( "MailTo" ),                       //  9
  TEXT( "Socks" ),                        // 10
  TEXT( "JavaScript" ),                   // 11
  TEXT( "VBScript" )                      // 12
};
#define  CRACKER_SCHEME_TYPE_ARRAY_SIZE      13

BOOL WINAPI Cracker( HWND hDlg, int nURLtextBoxId, int nListBoxId )
{
   int i, j;
   TCHAR* failedFunctionName;
   TCHAR URL_buffer[CRACKER_BUFFER_SIZE];

   URL_COMPONENTS URLparts;

   URLparts.dwStructSize = sizeof( URLparts );

   // The following elements determine which components are displayed
   URLparts.dwSchemeLength    = 1;
   URLparts.dwHostNameLength  = 1;
   URLparts.dwUserNameLength  = 1;
   URLparts.dwPasswordLength  = 1;
   URLparts.dwUrlPathLength   = 1;
   URLparts.dwExtraInfoLength = 1;

   URLparts.lpszScheme     = NULL;
   URLparts.lpszHostName   = NULL;
   URLparts.lpszUserName   = NULL;
   URLparts.lpszPassword   = NULL;
   URLparts.lpszUrlPath    = NULL;
   URLparts.lpszExtraInfo  = NULL;

   SendDlgItemMessage( hDlg, nListBoxId, LB_RESETCONTENT, 0, 0 );
   if( !GetDlgItemText( hDlg, nURLtextBoxId, 
                        URL_buffer, CRACKER_BUFFER_SIZE ) )
   {
       failedFunctionName = TEXT( "GetDlgItemText" );
       goto CrackerError_01;
   }

   if( FAILED( StringCchLength( URL_buffer, CRACKER_BUFFER_SIZE, 
                                (size_t*) &i ) ) )
   {
       failedFunctionName = TEXT( "StringCchLength" );
       goto CrackerError_01;
   }

   if( !InternetCrackUrl( URL_buffer, (DWORD)_tcslen( URL_buffer ), 0, 
                          &URLparts ) )
   {
       failedFunctionName = TEXT( "InternetCrackUrl" );
       goto CrackerError_01;
   }

   failedFunctionName = TEXT( "listURLpart" );

   i = URLparts.nScheme + 2;
   if( ( i >= 0 ) && ( i < CRACKER_SCHEME_TYPE_ARRAY_SIZE ) )
   {
       StringCchLength( schemeType[i], 
                        CRACKER_BUFFER_SIZE, 
                        (size_t*) &j );
       if( !listURLpart( hDlg, nListBoxId, 
                         TEXT("Scheme type"), 
                         schemeType[i], j ))
           goto CrackerError_01;
   }

   if( !listURLpart( hDlg, nListBoxId, TEXT( "Scheme text" ), 
                     URLparts.lpszScheme, 
                     URLparts.dwSchemeLength ) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "Host name" ), 
                     URLparts.lpszHostName, 
                     URLparts.dwHostNameLength) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "Port number" ), 
                     (int) URLparts.nPort ) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "User name" ), 
                     URLparts.lpszUserName, 
                     URLparts.dwUserNameLength) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "Password" ), 
                     URLparts.lpszPassword, 
                     URLparts.dwPasswordLength) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "Path" ), 
                     URLparts.lpszUrlPath, 
                     URLparts.dwUrlPathLength) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "Extra information"), 
                     URLparts.lpszExtraInfo, 
                     URLparts.dwExtraInfoLength))
           goto CrackerError_01;

   return( TRUE );

CrackerError_01:
// For sample source code of the InternetErrorOut( ) function 
// referenced below, see the "Handling Errors" 
// topic under "Using WinInet"
   InternetErrorOut( hDlg, GetLastError( ), failedFunctionName );
   return FALSE;
}

// listURLpart( ) helper function for string parts
BOOL listURLpart( HWND hDlg, int nListBoxId, 
                  LPTSTR szPartName, LPTSTR part, DWORD partLength )
{
  TCHAR outputBuffer[CRACKER_BUFFER_SIZE];
  LPTSTR nextStart;
  size_t nextSize;

  if( partLength == 0 )  // Just skip empty ones
    return( TRUE );

  if( FAILED( StringCchCopyEx( outputBuffer, 
                              (size_t) CRACKER_BUFFER_SIZE,
                               szPartName, &nextStart, 
                               &nextSize, 0 ) ) ||
      FAILED( StringCchCopyEx( nextStart, nextSize, TEXT( ": " ), 
                               &nextStart, &nextSize, 0 ) ) ||
      FAILED( StringCchCopyNEx( nextStart, nextSize, part, 
                                (size_t) partLength,
                                &nextStart, &nextSize, 0 ) ) )
    return( FALSE );

  *nextStart = 0;
  if( SendDlgItemMessage( hDlg, nListBoxId, LB_ADDSTRING, 0, 
                          (LPARAM)outputBuffer ) < 0 )
    return( FALSE );
  return( TRUE );
}

// listURLpart( ) helper function for numeric parts
BOOL listURLpart( HWND hDlg, int nListBoxId, 
                  LPTSTR szPartName, int partValue )
{
  TCHAR outputBuffer[CRACKER_BUFFER_SIZE];

  if( FAILED( StringCchPrintf( outputBuffer, 
                               (size_t) CRACKER_BUFFER_SIZE,
                               TEXT( "%s: %d" ), szPartName, 
                               partValue ) ) ||
      ( SendDlgItemMessage( hDlg, nListBoxId, LB_ADDSTRING, 0, 
                            (LPARAM)outputBuffer ) < 0 ) )
    return( FALSE );
  return( TRUE );
}

创建 URL

InternetCreateUrl 函数使用 URL_COMPONENTS 结构中的信息来创建统一资源定位符。

构成 URL_COMPONENTS 结构的组件包括方案、主机名、端口号、用户名、密码、URL 路径和其他 (信息,例如搜索参数) 。 除端口号外,每个组件都有一个保存信息的字符串成员,以及一个包含字符串成员长度的成员。

对于每个必需的组件,指针成员应包含保存信息的缓冲区的地址。 如果指针成员包含以零结尾的字符串的地址,则应将长度成员设置为零;如果指针成员包含不以零结尾的字符串的地址,则长度成员应设置为字符串长度。 任何不需要的组件的指针成员必须为 NULL

直接访问 URL

可以使用 InternetOpenUrlInternetReadFileInternetFindNextFile 函数直接访问 Internet 上的 FTP 和 HTTP 资源。 InternetOpenUrl 在传递给函数的 URL 处打开与资源的连接。 建立此连接时,有两个可能的步骤。 首先,如果资源是文件, InternetReadFile 可以下载它;其次,如果资源是目录, InternetFindNextFile 可以枚举目录 (的文件,除非使用 CERN 代理) 。 有关 InternetReadFile 的详细信息,请参阅 读取文件。 有关 InternetFindNextFile 的详细信息,请参阅 查找下一个文件

对于需要通过 CERN 代理运行的应用程序,可以使用 InternetOpenUrl 访问 FTP 目录和文件。 FTP 请求被打包为类似于 HTTP 请求,CERN 代理会接受该请求。

InternetOpenUrl 使用 InternetOpen 函数创建的 HINTERNET 句柄和资源 URL。 URL 必须包括方案 (http:、ftp:、file: [for a local file]或 https: [for hypertext protocol secure]) 和网络位置 ((如 www.microsoft.com) )。 URL 还可以包含路径 (例如 /isapi/gomscom.asp?TARGET=/windows/feature/) 和资源名称 (例如default.htm) 。 对于 HTTP 或 HTTPS 请求,可以包含其他标头。

InternetQueryDataAvailableInternetFindNextFileInternetReadFileInternetSetFilePointer (HTTP 或 HTTPS URL 仅) 才能使用 InternetOpenUrl 创建的句柄下载资源。

下图说明了要用于每个函数的句柄。

要与函数一起使用的句柄

InternetOpenUrl 使用 InternetOpen 创建的根 HINTERNET 句柄。 InternetOpenUrl 创建的 HINTERNET 句柄可由 InternetQueryDataAvailableInternetReadFileInternetFindNextFile (未在此处显示) 使用,InternetSetFilePointer (HTTP 或 HTTPS URL 仅) 。

有关详细信息,请参阅 HINTERNET 句柄

注意

WinINet 不支持服务器实现。 此外,不应从服务使用它。 对于服务器实现或服务,请使用 Microsoft Windows HTTP Services (WinHTTP)