aodama.gif(0.97KB) WinInet API で HTTP 上のデータを読む(β版)

● ご注意

 WinInet はまだまだ研究中のため、今のところ大したことは書けません。 よってこのコーナーはベータ版とさせていただきます。 テクニックの紹介というより、研究成果の中間発表といった感じのコーナーです(笑)

VBコーナーへもどる
トップページへもどる

● WinInet の簡単な紹介

 Visual Basic でインターネット上のデータを操作するには色々な方法がありますが、 ActiveX コントロールを使わない方法で代表的なものとしては、 WinSock API を使用する方法と WinInet API を使用する方法があります。 このコーナーでは WinInet について取り扱います。

 WinInet とは Win32 Internet Functions の略で、"Wininet.dll" を呼び出して使用します。 ただしこの DLL は、 Microsoft Internet Explorer 3.0 以上がインストールされている環境にしかありませんので、 WinSock と比較すると汎用性では劣ります。 しかし、Microsoft の Netscape 潰しの策が功を奏し Windows 98 発売以来の IE の普及はすさまじいので、 大半の Win32 ユーザーは IE 入ってると思います(^^;

 WinInet を使用すれば、WinSock よりずっと簡単に、データの送受信を行うことができます。 ポートを開いて・・・ホスト名をアドレスに変換して・・・などといった一連の処理は、 すべて WinInet が自動的にやってくれますので、 比較的 TCP/IP 初心者にもとっつきやすい API です。 そのかわり WinInet で扱えるプロトコルは HTTP, HTTPS, FTP, GOPHER だけ(たぶん)で、 POP3 や SMTP などとは通信できません。


● 前準備

 WinInet で HTTP クライアントみたいなやつを作ってみたくなったので、 とりあえず VB のサンプルを探してみたのですが、FTP のサンプルは結構あるのに、 HTTP クライアントのサンプルは全然ありませんね〜(泣)

 でも、さいわい C/C++ で書かれたすばらしい解説ページをいくつか見つけました。 それと Microsoft で公開されている Visual Basic 用 WinInet 宣言モジュールを参考にして、 なんとか作ることができました。

参考にさせていただいたページ & Microsoft の資料
Wininet Programing すばらしいです。WinInet の使い方を初心者向けに解説されています(C/C++)
MASAPICO's PAGE WinInet API 各関数の説明が詳しく、非常に役立ちました(C/C++)
WinINet Functions MSDN ライブラリの WinInet リファレンス。英語(C/C++)
vbinet.exe Visual Basic 用 WinInet 宣言モジュール。とりあえず、これは入手しましょう(笑)

 まず手っ取り早く、 vbinet.exe アーカイブ(自己解凍式アーカイブ : 27.7KB)をダウンロードし、その中の Wininet.bas(modWinInet) をプロジェクトに組み込みましょう。 これで WinInet の主要な関数はすぐに使えるようになります。

 ところが、InternetReadFile() の宣言文がよくありませんね(^^;

Public Declare Function InternetReadFile Lib "wininet.dll" _
    (ByVal hFile As Long, _
     ByVal sBuffer As String, _
     ByVal lNumBytesToRead As Long, _
     lNumberOfBytesRead As Long) As Integer

 第2引数 sBuffer が、String 型として宣言されてしまっています。 これではデータを文字列型変数に読み込むということになるため、 ANSI/Shift-JIS で記述されたページくらいしか読み込めません。 もし文字コードが EUC や UTF-8 で記述されたページや、その他画像ファイル等バイナリデータを読み込んだ場合には、 データの内容が破壊されてしまいます。

 これでは悲惨なので、バイナリデータも受け取れるように宣言文を書き換えましょう。 戻り値が Integer 型というのもおかしいので、Long 型に変更しておきます。 ついでに、lNumberOfBytesRead も ByRef キーワードを明示しておきます。

Public Declare Function InternetReadFile Lib "wininet.dll" _
    (ByVal hFile As Long, _
     ByRef lpBuffer As Any, _
     ByVal lNumBytesToRead As Long, _
     ByRef lNumberOfBytesRead As Long) As Long

 これで、引数 lpBuffer にバイト型配列の要素を指定※1することにより、 バイナリデータの読み込みも可能になります。

※1 実際に渡されるのは、実は「ポインタ」値です。 ポインタについては、一冊の本が出てるほど解説がめんどくさいものなので、またの機会に(^^;

 この modWinInet ですが、他にも各所不適当な部分が散見されますので、 実際に使用される際は上記の参考ページの情報などを元に改良などを行った方が良いと思われます。 まあ間違いだらけの Win32api.txt よりはましかもしれません(^^;


● 実際にデータを取得してみる

SAMPLE

 InternetReadFile() の宣言文を書き換えたら、 次に以下のようなオブジェクト名のコントロールをフォーム上に配置して下さい。

txtUrl         - テキストボックス
txtProxy       - テキストボックス
txtRecvData    - テキストボックス(MultiLine = True)
cmdGetInetData - コマンドボタン


 あとは以下のようなコードをフォーム内に記述して下さい。 あくまでもサンプルですので、「こうした方がいい」なんてメールはやめて下さい(笑)

Option Explicit

Private Const USER_AGENT = "WinInet Test" 'User_agentヘッダ(任意)

'InternetReadFile で一度に読み込むサイズ
Private Const WI_READ_READSIZE = 1024

'データを受け取るバッファサイズの初期値
'必ず WI_READ_READSIZE 以上のサイズを指定して下さい
Private Const WI_INITBUFSIZE = 32767

'----- GetHttpData -----
'HTTP上のデータを取得します。
'
'引数 strUrl
'   データを取得したいHTTPのアドレス(URL)を指定します。
'
'引数 bytRecvData()
'   受信したデータを格納するバッファを、Byte型配列で指定します。
'   ここに実際に受信したデータが格納されます。
'
'引数 strProxy
'   省略可能です。Proxyサーバーを経由してデータを送受信したい場合、
'   ここに "proxy.hoge.co.jp:8080" のような型式で指定します。
'   省略または "" を渡した場合、レジストリに登録されている
'   アクセス方法で送受信します。
'
'戻り値
'   成功した場合、読み込んだデータの総サイズが返ります。
'   失敗した場合、0 が返ります。
'
Private Function GetHttpData _
    (ByVal strUrl As String, _
     ByRef bytRecvData() As Byte, _
     Optional ByVal strProxy As String) As Long

 Dim lngInetHandle As Long 'インターネットハンドルを格納
 Dim lngUrlHandle As Long  'URLハンドルを格納
 Dim lngArraySize As Long  'データを受け取るバッファ bytRecvData のサイズ
 Dim lngRet As Long     'InternetReadFile の戻り値を格納
 Dim lngTotalRead As Long  '受信したデータの総サイズ 兼 バッファへのポインタ
 Dim lngReadSize As Long   '受信したデータのサイズ
 
    If Len(strUrl) = 0 Then Exit Function
 
    '経由するProxyサーバーが指定されている場合
    If Len(strProxy) Then
        'インターネットハンドルを作成
        lngInetHandle = InternetOpen(USER_AGENT, _
                                     INTERNET_OPEN_TYPE_PROXY, _
                                     strProxy, _
                                     vbNullString, _
                                     0)
    '経由するProxyサーバーが指定されていない場合
    Else
        'インターネットハンドルを作成
        lngInetHandle = InternetOpen(USER_AGENT, _
                                     INTERNET_OPEN_TYPE_PRECONFIG, _
                                     vbNullString, _
                                     vbNullString, _
                                     0)
    End If
    If lngInetHandle = 0 Then Exit Function '失敗したら関数を抜ける

    'URLハンドルを作成
    lngUrlHandle = InternetOpenUrl(lngInetHandle, _
                                   strUrl, _
                                   vbNullString, _
                                   0, _
                                   INTERNET_FLAG_RELOAD, _
                                   0)
    If lngUrlHandle = 0 Then GoTo CloseInetHandle '失敗したら CloseInetHandle に飛ぶ
    
    lngArraySize = WI_INITBUFSIZE - 1
    ReDim bytRecvData(lngArraySize) 'バッファの初期サイズを確保する
    
    Do
        '次回受信時、バッファのサイズをオーバーしてしまう危険性がある場合
        If (lngTotalRead + WI_READ_READSIZE) > lngArraySize Then
            'バッファサイズを WI_INITBUFSIZE バイト増やす
            lngArraySize = lngArraySize + WI_INITBUFSIZE
            ReDim Preserve bytRecvData(lngArraySize - 1)
        End If
        'インターネット上のデータを読み込む
        lngRet = InternetReadFile(lngUrlHandle, _
                                  bytRecvData(lngTotalRead), _
                                  WI_READ_READSIZE, _
                                  lngReadSize)
        lngTotalRead = lngTotalRead + lngReadSize '読み込んだ総サイズを計算
        '受信サイズが0、または読み込み失敗の場合、ループを抜ける
        If (lngReadSize = 0) Or (lngRet = 0) Then Exit Do
    Loop
    
    If lngTotalRead Then '実際にデータが読み込まれた場合
        '配列サイズを実サイズに削る
        ReDim Preserve bytRecvData(lngTotalRead - 1)
        GetHttpData = lngTotalRead '読み込んだバイト数を返す
    Else
        Erase bytRecvData '総受信サイズが 0 の場合、配列を消去
    End If
    
    Call InternetCloseHandle(lngUrlHandle) 'URLハンドルを閉じる

CloseInetHandle:

    Call InternetCloseHandle(lngInetHandle) 'インターネットハンドルを閉じる

End Function

Private Sub cmdGetInetData_Click()

 Dim lngSize As Long    'GetHttpData()の戻り値(受信サイズ)を格納する
 Dim bytArray() As Byte 'データを受け取るバッファ(Byte型配列)

    'txtUrl で指定したURLのデータを取得
    lngSize = GetHttpData(txtUrl.Text, bytArray(), txtProxy.Text)
    If lngSize Then
        '成功したら、バッファのデータをUnicodeに変換し txtRecvData に表示
        txtRecvData.Text = StrConv(bytArray, vbUnicode)
    End If

    Erase bytArray

End Sub

べたテキスト版 : ダウンロードgethttp.txt (4.50KB) ※ 右クリックで「対象をファイルに保存(A)」して下さい

 まずダイヤルアップ接続してインターネットができる環境にしてから、 テキストボックス txtUrl に "http://www.vector.co.jp/" などのURLを入力し、 コマンドボタン cmdGetInetData をクリックしてみて下さい。 すると、テキストボックス txtRecvData にHTMLファイルのソースが読み込まれます※1, 2

※1 日本語文字コードが Shift-JIS 以外のページは正常に表示されません。
※2 改行コードが CR または LF の場合、正常に改行されません。

 このサンプルでは受信したデータを Unicode に変換してテキストボックスに出力していますが、 バッファのデータを Put ステートメントで書き出してやれば、バイナリファイルとしても保存できますね(^^)

 また文字コードが EUC, JIS で記述されたページも、NKF32.DLL などで変換してやれば読めます。

参考 : NKF32.DLL で文字コード変換
参考 : 文字列を UTF-8 に変換

InternetOpen - インターネットのハンドルを作成する
宣言文
Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" _
    (ByVal sAgent As String, _
     ByVal lAccessType As Long, _
     ByVal sProxyName As String, _
     ByVal sProxyBypass As String, _
     ByVal lFlags As Long) As Long
戻り値
この関数が成功した場合、インターネットのハンドルが返ります。 失敗した場合は 0 が返ります。
引数説明
sAgent 任意。ここに指定した名前で User_agent ヘッダが送られる。
lAccessType インターネットへのアクセス方法を指定。INTERNET_OPEN_TYPE_DIRECT など。(下記定数表参照)
sProxyName プロキシサーバーを使用する場合、"proxy.hoge.co.jp:8080" のように指定。
sProxyBypass イントラネット内など、プロキシを経由させたくないコンテンツがある場合はここにそのホストを指定。
lFlags 下記定数表参照。
lAccessType の定数
Const INTERNET_OPEN_TYPE_PRECONFIG = 0
レジストリに記録されている方法(IE と共通?)でアクセスする。
Const INTERNET_OPEN_TYPE_DIRECT = 1
プロキシを経由せずにアクセスする。
Const INTERNET_OPEN_TYPE_PROXY = 3
sProxyName で指定したプロキシ経由でアクセスする。
lFlags の定数
Const INTERNET_FLAG_ASYNC = &H10000000
InternetSetStatusCallback() を使用した非同期処理を行う場合に指定する。
Const INTERNET_FLAG_FROM_CACHE = &H1000000
インターネットからデータを取得せず、キャッシュからのみデータを取得する。
Const INTERNET_FLAG_OFFLINE = &H1000000
同上(^^;


InternetOpenUrl - URL のハンドルを作成する
宣言文
Declare Function InternetOpenUrl Lib "wininet.dll" Alias "InternetOpenUrlA" _
    (ByVal hInternetSession As Long, _
     ByVal sUrl As String, _
     ByVal sHeaders As String, _
     ByVal lHeadersLength As Long, _
     ByVal lFlags As Long, _
     ByVal lContext As Long) As Long
戻り値
この関数が成功した場合、URLのハンドルが返ります。 失敗した場合は 0 が返ります。
引数説明
hInternetSession InternetOpen() で返されたハンドルを指定
sUrl HTTP の場合、http:// から始まる、HTTP プロトコルの URL を指定。
sHeaders HTTP サーバーに送信するヘッダを指定。
lHeadersLength sHeaders の文字数。もし sHeaders が NULL でなかった場合に -1 を指定すると、 勝手に文字数を計算してくれる。
lFlags どのようにオープンするかを示す。(定数表参照)
lContext InternetSetStatusCallback() 使用時に、コールバック関数の第二引数に渡す任意の値を指定する。
lFlags の定数
Const INTERNET_FLAG_RELOAD = &H80000000
キャッシュを使わずに、サーバーからデータを取得する。
Const INTERNET_FLAG_NO_CACHE_WRITE = &H4000000
キャッシュにデータを書き込まない。
Const INTERNET_FLAG_NO_AUTO_REDIRECT = &H200000
自動的にリダイレクト(Location ヘッダなど)しない。
Const INTERNET_FLAG_PRAGMA_NOCACHE = &H100
プロキシサーバーのキャッシュを使用しない。


InternetReadFile - サーバーからデータを読み込む
宣言文
Declare Function InternetReadFile Lib "wininet.dll" _
    (ByVal hFile As Long, _
     ByRef lpBuffer As Any, _
     ByVal lNumBytesToRead As Long, _
     ByRef lNumberOfBytesRead As Long) As Long
戻り値
この関数が成功した場合、1 が返ります。 失敗した場合は 0 が返ります。 ただし、IE4 の時はファイルの読込が終了しても 1 のままでしたので注意が必要です。
引数説明
hFile InternetOpenUrl() などで返されたハンドルを指定。
lpBuffer 読み込み結果を受け取るバッファを指定。
lNumBytesToRead 一度に読み込むバイト数を指定。
lNumberOfBytesRead 実際に読み込まれたバイト数がここに格納される。読み込みが終了したら 0 が格納される。


InternetCloseHandle - ハンドルを閉じる
宣言文
Declare Function InternetCloseHandle Lib "wininet.dll" (ByVal hInet As Long) As Integer
戻り値
この関数が成功した場合、1 が返ります。 失敗した場合は 0 が返ります。
引数説明
hInet InternetOpen(), InternetOpenUrl() などで開いたハンドルを指定。そのハンドルを閉じる。


● タイムアウト

 基本的に WinInet は同期的な処理を行います。つまり接続→データの取得が終了するまで、 アプリケーションがフリーズしてしまうことになります。

 また、サーバーからの応答がなかった場合の、WinInet のデフォルトタイムアウト時間は 300 秒だそうです。 いくら何でも 5 分間フリーズしてしまうのは悲しすぎるので、 自分でタイムアウト時間を設定しましょう。

InternetSetOption - WinInet の数々のオプションを設定
宣言文
Declare Function InternetSetOption Lib "wininet.dll" Alias "InternetSetOptionA" _
    (ByVal hInternet As Long, _
     ByVal lOption As Long, _
     ByRef sBuffer As Any, _
     ByVal lBufferLength As Long) As Integer
戻り値
この関数が成功した場合、1 が返ります。 失敗した場合は 0 が返ります。
引数説明
hInternet HTTP プロトコルの場合、InternetOpen() で取得したインターネットのハンドルを指定。
lOption 設定するオプションをあらわすフラグを設定。(定数表参照)
sBuffer lOption で指定したオプションの値が入っているバッファを指定。
lBufferLength sBuffer のサイズ。
lOption の定数
Const INTERNET_OPTION_CONNECT_TIMEOUT = 2
接続タイムアウトを指定するためのフラグ。値は sBuffer にミリ秒単位で指定。
Const INTERNET_OPTION_RECEIVE_TIMEOUT = 6
受信タイムアウトを指定するためのフラグ。値は sBuffer にミリ秒単位で指定。
Const INTERNET_OPTION_SEND_TIMEOUT = 5
タイムアウトを指定するためのフラグ。値は sBuffer にミリ秒単位で指定。
Const INTERNET_OPTION_CONNECT_RETRIES = 3
接続リトライ回数を指定するためのフラグ。値は sBuffer に指定。(デフォルトでは 5 回)

 InternetOpen() でインターネットのハンドルを取得した後、 そのハンドルを使用して InternetSetOption() を呼び出します。

lngConnectTimeOut = 15 * 1000 '接続タイムアウト15秒
lngReceiveTimeOut = 30 * 1000 '受信タイムアウト30秒
Call InternetSetOption(lngInetHandle, INTERNET_OPTION_CONNECT_TIMEOUT, lngConnectTimeOut, LenB(lngConnectTimeOut))
Call InternetSetOption(lngInetHandle, INTERNET_OPTION_RECEIVE_TIMEOUT, lngReceiveTimeOut, LenB(lngReceiveTimeOut))

 ちなみに非同期で処理を行う方法はあることはあるのですが、けっこう難しいです。 Microsoft に参考資料があります。関係ありそうな資料のリンクを集めてみました。一番下の機械翻訳タイトルは笑えますが(^^;

[INFO] Visual Basic 内で WinInet API を非同期的に使用する

サンプル非同期に WinInet API と呼び出す 使用 AsyncHTTP の方法

BUG: バイト読み取るは、非同期の InternetReadFile に設定されていません。

 一番上のリンク先に、

ここで注意する必要があるのは、Visual Basic 内で非同期の WinInet API を実現するのに必要な一連の技術は、WinInet を非同期モードで使用して C/C++ アプリケーションを記述するのに必要な技術に匹敵するということです。このことからも、Visual Basic で WinInet API を非同期的に使用することは望ましい方法ではありません。

なんて少し挑発的な(笑)記述があったので無理やりコーディングしてみたのですが、一応動作させることは出来ました。 ちなみに VC++ でも試しに記述してみたのですが、なぜかそちらは動きませんでした(爆)。 しかし正直、VB の非同期操作についてよく解っていないため、 処理待ちに空ループを使用していたりして動作が著しく不安定で、 まだ実用化レベルには至っていないので非公開とさせていただきます。

 ソースを公開することは無理だと思いますが、万年初心者の私でも何とか作れたので(笑)、 コールバック関数などの処理に詳しい人にとってはそれほど難しくはないと思います。がんばってください(^^;


● URL 文字列を分割する

 このページではまだ触れていませんが、InternetConnect() で HTTP 接続する場合は URL を分割して引数に渡す必要があります。 具体的には、"http://www.geocities.co.jp/SilkRoad/4511/" に接続したい場合は、

スキーム名 : "http"
ホスト名 : "www.geocities.co.jp"
パス : "/SilkRoad/4511/"

のように分割する必要があります。その他にもポート番号や、ユーザー名・パスワードなどの引数もあります。 まあ InStr なんかで自力で分割してもいいんですが、 WinInet には InternetCrackUrl() という関数が用意されており、 これを使用すると一発で URL_COMPONENTS 構造体に分割結果を格納してくれます。

 URL_COMPONENTS 構造体のメンバが異常に多い上に、文字列メンバのサイズもメンバに設定してやらないといけないため関数化してみました。 メンバ名を VB のプリフィックスに書き直すのがめんどくさかったので、wininet.h の丸写しです。 ご了承下さい(^^;

Option Explicit

Public Declare Function InternetCrackUrl Lib "wininet.dll" Alias "InternetCrackUrlA" _
    (ByVal lpszUrl As String, _
     ByVal dwUrlLength As Long, _
     ByVal dwFlags As Long, _
     ByRef lpUrlComponents As URL_COMPONENTS) As Long

Public Const ICU_ESCAPE = &H80000000                        '(un)escape URL characters
Public Const ICU_DECODE = &H10000000                        'use internal username & password

'----- URL_COMPONENTS 構造体 -----
Public Type URL_COMPONENTS
    dwStructSize As Long                                    'URL_COMPONENTS 構造体のサイズ(60)
    lpszScheme As String                                    'スキーム("http", "ftp" など)
    dwSchemeLength As Long                                  'lpszScheme のサイズ
    nScheme As Long                                         'スキームの種類。INTERNET_SCHEME 定数参照
    lpszHostName As String                                  'ホスト名("www.yahoo.co.jp" など)
    dwHostNameLength As Long                                'lpszHostName のサイズ
    nPort As Long                                           'ポート番号
    lpszUserName As String                                  'ユーザー名
    dwUserNameLength As Long                                'lpszUserName のサイズ
    lpszPassword As String                                  'パスワード
    dwPasswordLength As Long                                'lpszPassword のサイズ
    lpszUrlPath As String                                   'パス("/index.html" など)
    dwUrlPathLength As Long                                 'lpszUrlPath のサイズ
    lpszExtraInfo As String                                 'pointer to extra information (e.g. ?foo or #foo)
    dwExtraInfoLength As Long                               'lpszExtraInfo のサイズ
End Type

Public Const INTERNET_SCHEME_PARTIAL = -2
Public Const INTERNET_SCHEME_UNKNOWN = -1
Public Const INTERNET_SCHEME_DEFAULT = 0
Public Const INTERNET_SCHEME_FTP = 1
Public Const INTERNET_SCHEME_GOPHER = 2
Public Const INTERNET_SCHEME_HTTP = 3
Public Const INTERNET_SCHEME_HTTPS = 4
Public Const INTERNET_SCHEME_FILE = 5
Public Const INTERNET_SCHEME_NEWS = 6
Public Const INTERNET_SCHEME_MAILTO = 7
Public Const INTERNET_SCHEME_SOCKS = 8
Public Const INTERNET_SCHEME_FIRST = INTERNET_SCHEME_FTP
Public Const INTERNET_SCHEME_LAST = INTERNET_SCHEME_SOCKS

Private Const INTERNET_MAX_HOST_NAME_LENGTH = 256           'ホスト名の最大サイズ。以下同上(^^;
Private Const INTERNET_MAX_USER_NAME_LENGTH = 128
Private Const INTERNET_MAX_PASSWORD_LENGTH = 128
Private Const INTERNET_MAX_PORT_NUMBER_LENGTH = 5
Private Const INTERNET_MAX_PORT_NUMBER_VALUE = 65535
Private Const INTERNET_MAX_PATH_LENGTH = 2048
Private Const INTERNET_MAX_SCHEME_LENGTH = 32
Private Const INTERNET_MAX_URL_LENGTH = INTERNET_MAX_SCHEME_LENGTH + 3 + INTERNET_MAX_PATH_LENGTH
Private Const INTERNET_MAX_EXTRAINFO_LENGTH = 512           'wininet.h にはありませんが自分で追加しました

'----- GetUrlComponents 関数 Version 1.00 -----
'InternetCrackUrl() を呼び出し URL を分解します。
'
'引数 strUrl
'   分解の対象となる URL 文字列式を指定します。
'
'引数 ucUrlComponents
'   分解された URL の各要素を格納する URL_COMPONENTS 構造体を指定します。
'   関数が成功した場合、ucUrlComponents の各メンバに情報が格納されます。
'
'引数 lngFlags
'   省略可能です。URL の分解方法を指定します。この引数を省略すると、
'   ICU_ESCAPE の値が使用されます。
'
'戻り値
'   関数が成功した場合、True を返します。
'
'※ 注意事項 ※
'
'この関数では、URL 文字列に全角文字(Shift-JIS で 2 バイトになる文字)が
'使用された場合のことは考慮しておりません。もし使用された場合、
'ucUrlComponents の文字列型メンバに格納される文字列が不正なものに
'なってしまいます。もし全角文字が使用される可能性がある場合は、
'後半の Left$ 関数を、拙作の TrimNull 関数などに置き換えるなどして
'変更した上でお使い下さい。
'
'TrimNull - http://www.geocities.co.jp/SilkRoad/4511/vb/kowaza.htm#trimnull
'
Public Function GetUrlComponents _
    (ByRef strUrl As String, _
     ByRef ucUrlComponents As URL_COMPONENTS, _
     Optional ByVal lngFlags As Long = ICU_ESCAPE) As Boolean

    If Len(strUrl) = 0 Then Exit Function                                       'URL 文字列が 0 文字の場合、関数を抜ける
    
    With ucUrlComponents                                                        'URL_COMPONENTS 構造体の各メンバの情報を設定
        .dwStructSize = LenB(ucUrlComponents)                                   'URL_COMPONENTS のサイズを設定(60 バイト)
        .dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH                            'dwSchemeLength を INTERNET_MAX_SCHEME_LENGTH に設定
        .lpszScheme = String$(.dwSchemeLength, vbNullChar)                      'lpszScheme を dwSchemeLength 個の NULL で埋める
        .dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH                       '以下同上(^^;
        .lpszHostName = String$(.dwHostNameLength, vbNullChar)
        .dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH
        .lpszUserName = String$(.dwUserNameLength, vbNullChar)
        .dwPasswordLength = INTERNET_MAX_PASSWORD_LENGTH
        .lpszPassword = String$(.dwPasswordLength, vbNullChar)
        .dwUrlPathLength = INTERNET_MAX_PATH_LENGTH
        .lpszUrlPath = String$(.dwUrlPathLength, vbNullChar)
        .dwExtraInfoLength = INTERNET_MAX_EXTRAINFO_LENGTH
        .lpszExtraInfo = String$(.dwExtraInfoLength, vbNullChar)
    End With

    If InternetCrackUrl(strUrl, Len(strUrl), lngFlags, ucUrlComponents) Then    'InternetCrackUrl() が成功した場合
        With ucUrlComponents
            .lpszScheme = Left$(.lpszScheme, .dwSchemeLength)                   'lpszScheme の余分な部分を削り再格納
            .lpszHostName = Left$(.lpszHostName, .dwHostNameLength)             '以下同上(^^;;
            .lpszUserName = Left$(.lpszUserName, .dwUserNameLength)
            .lpszPassword = Left$(.lpszPassword, .dwPasswordLength)
            .lpszUrlPath = Left$(.lpszUrlPath, .dwUrlPathLength)
            .lpszExtraInfo = Left$(.lpszExtraInfo, .dwExtraInfoLength)
        End With
        GetUrlComponents = True                                                 'True を返す
    End If

End Function

 なんとなく禍々しくも思えるソースコードです(爆)。なんで URL を分割するだけでここまでやらなきゃいけないんでしょうか?(^^;


VBコーナーにもどる   トップページにもどる