處理統一資源定位器

統一資源定位器 (URL) 是位於網際網路之資源的位置和存取方法的精簡標記法。 每個 URL 都包含一個配置 (HTTP、HTTPS 或 FTP) 和配置特定的字串。 此字串也可以包含目錄路徑、搜尋字串或資源名稱的組合。 WinINet 函式可讓您建立、合併、細分和標準 URL。 如需 URL 的詳細資訊,請參閱統一資源定位器上的 RFC-1738 (URL) 。

URL 函式會以工作導向的方式運作。 未驗證提供給函式之 URL 的內容和格式。 呼叫的應用程式應該追蹤這些函式的使用,以確保資料的格式是預期的。 例如, InternetCanonicalizeUrl 函式會在不使用旗標時,將字元 「%」 轉換成逸出序列 「%25」。 如果 InternetCanonicalizeUrl 用於標準化 URL 上,則逸出序列 「%25」 會轉換成逸出序列 「%2525」,無法正常運作。

什麼是標準 URL?

所有 URL 的格式都必須遵循接受的語法和語意,才能透過網際網路存取資源。 標準化是格式化 URL 以遵循這個接受的語法和語意的程式。

必須編碼的字元包含 US-ASCII 編碼字元集中沒有任何對應圖形字元的任何字元, (十六進位 80-FF,這些字元不會用於 US-ASCII 編碼字元集,以及十六進位 00-1F 和 7F,這些字元是控制字元) 、空格、「%」 (,用來編碼其他字元) ,以及不安全字元 (、 <>、「、#、{、}、|、\、^、~、[、]和 ') 。

使用 WinINet 函式來處理 URL

下表摘要說明 URL 函式。

函式 描述
InternetCanonicalizeUrl 標準化 URL。
InternetCombineUrl 結合基底和相對 URL。
InternetCrackUrl 將 URL 字串剖析為元件。
InternetCreateUrl 從元件建立 URL 字串。
InternetOpenUrl 開始擷取 FTP、HTTP 或 HTTPS 資源。

 

標準 URL

標準化 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 路徑的配置、網路位置和部分。 應用程式可以呼叫 InternetCombineUrl ,將相對 URL 與其基底 URL 結合。 InternetCombineUrl 也會標準化結果 URL。

破解 URL

InternetCrackUrl函式會將 URL 分成其元件元件,並傳回傳遞至函式之URL_COMPONENTS結構所指示的元件。

構成 URL_COMPONENTS 結構的元件是配置編號、主機名稱、埠號碼、使用者名稱、密碼、URL 路徑,以及其他資訊 (,例如搜尋參數) 。 除了配置和埠號碼之外,每個元件都有一個保留資訊的字串成員,以及保留字元串成員長度的成員。 配置和埠號碼只有儲存對應值的成員;這兩者都會在所有成功呼叫 InternetCrackUrl時傳回。

若要取得 URL_COMPONENTS 結構中特定元件的值,儲存該元件字串長度的成員必須設定為非零值。 字串成員可以是緩衝區的位址或 Null

如果指標成員包含緩衝區的位址,字串長度成員必須包含該緩衝區的大小。 InternetCrackUrl 會將元件資訊當做緩衝區中的字串傳回,並將字串長度儲存在字串長度成員中。

如果指標成員為 Null,字串長度成員可以設定為任何非零值。 InternetCrackUrl 會儲存包含元件資訊之 URL 字串的第一個字元位址,並將字串長度設定為與元件相關的 URL 字串其餘部分的字元數。

所有指標成員都設定為 Null ,且非零長度成員指向 URL 字串中適當的起點。 儲存在長度成員中的長度必須用來判斷個別元件資訊的結尾。

若要正確初始化 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 函式,直接存取網際網路上的 FTP 和 HTTP 資源。 InternetOpenUrl 會在傳遞至函式的 URL 上開啟與資源的連線。 建立此連線時,有兩個可能的步驟。 首先,如果資源是檔案, InternetReadFile 可以下載它;第二,如果資源是目錄, InternetFindNextFile 可以列舉目錄內的檔案 (,但使用 CERN Proxy) 時除外。 如需 InternetReadFile的詳細資訊,請參閱 讀取檔案。 如需 InternetFindNextFile的詳細資訊,請參閱 尋找下一個檔案

對於需要透過 CERN Proxy 運作的應用程式, InternetOpenUrl 可用來存取 FTP 目錄和檔案。 FTP 要求會封裝為類似 HTTP 要求,CERN Proxy 會接受此要求。

InternetOpenUrl會使用InternetOpen函式所建立的HINTERNET控制碼,以及資源的 URL。 URL 必須包含配置 (HTTP:、ftp:、file: [適用于本機檔案],或 HTTPs:[適用于超文字通訊協定安全]) 和網路位置 (,例如 www.microsoft.com) 。 URL 也可以包含路徑 (,例如 /isapi/gomscom.asp?TARGET=/windows/feature/) 和資源名稱 (例如,default.htm) 。 針對 HTTP 或 HTTPS 要求,可以包含其他標頭。

InternetQueryDataAvailableInternetFindNextFileInternetReadFile 和 InternetSetFilePointer (HTTP 或 HTTPS URL) 只能使用InternetOpenUrl建立的控制碼來下載資源。

下圖說明要搭配每個函式使用的控制碼。

要搭配函式使用的控制碼

InternetOpen所建立的根HINTERNET控制碼是由InternetOpenUrl使用。 InternetOpenUrl所建立的HINTERNET控制碼可以供InternetQueryDataAvailableInternetReadFileInternetFindNextFile (此處未顯示) ,而InternetSetFilePointer (HTTP 或 HTTPS URL) 。

如需詳細資訊,請參閱 HINTERNET 控制碼

注意

WinINet 不支援伺服器實作。 此外,不應該從服務使用它。 對於伺服器實作或服務,請使用 Microsoft Windows HTTP Services (WinHTTP)