• ベストアンサー

「HttpSendRequest」の異常終了の原因について

プログラム環境は「WinXP SP3 + VB2008 Express Edition」です。 Webサーバ上のファイルを取得するプログラムを作成しています。 「VB6」で「Inet/OpenURL」を使用して実現していた機能を「WinInet.dll」を使用して実現したいと思っています。 その処理の途中の「HttpSendRequest」で異常終了してしまいます。 「エラーコード:2(ERROR_FILE_NOT_FOUND:The system cannot find the file specified.)」です。 「Wireshark」でPCとサーバの交信をチェックしたところ、「Inet/OpenURL」でうまくいった時と、「HttpSendRequest」でうまくいかない時に違いが見当たりません。「HTTP:GET」に対して「HTTP:200 OK」が返ってきます。TCPでも「SYN-->ACK」で始まって、「FIN-->ACK」で終っています。交信は正常に行われているのに、「HttpSendRequest」の内部の処理でエラーにしているように見えます。(例えば、「HTTP:GET」~「HTTP:200 OK」でPCとサーバの間でパケットのやり取りがありますが、その回数が限界値を超えたため、エラーにしてしまうような処理があれば、エラーになりそうです。このあたりがわかりませんでした。) また、この「HttpSendRequest」が異常終了する事態は「GET」の対象が「*.php」の場合に発生します。「GET」の対象が「*.html」「*.jpg」「*.zip」の場合には、正常終了して、ファイルが正常に取得できます。「GET」の対象が「*.php」の場合に「HttpSendRequest」を実行する前に何か設定が必要なのでしょうか? よろしく、お願いいたします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

大雑把な手順(すでにご存知とは思いますが) A.InternetOpenUrl の場合: 1.WinInet を初期化 hInternet = InternetOpen ( lpszAgent, dwAccessType, lpszProxyName, lpszProxyBypass, dwFlags ); 2.接続を確立をしてファイル読み込みの準備 hFile = InternetOpenUrl ( hInternet, lpszUrl, lpszHeaders, dwHeadersLength, dwFlags, dwContext ); 3.ファイル読み込みループで使用 InternetReadFile ( hFile, lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead ); 4.InternetOpenUrl で取得したハンドルをクローズ InternetCloseHandle ( hFile ); 5.InternetOpen で取得したハンドルをクローズ InternetCloseHandle ( hInternet ); B.InternetConnect の場合: 1.WinInet を初期化 hInternet = InternetOpen ( lpszAgent, dwAccessType, lpszProxyName, lpszProxyBypass, dwFlags ); 2.接続を確立をしてファイル読み込みの準備 hConnect = InternetConnect ( hInternet, lpszServerName, nServerPort, lpszUsername, lpszPassword, wService, dwFlags, dwContext ); 3.HTTP リクエストハンドルを作成 hRequest = HttpOpenRequest ( hConnect, lpszVerb, lpszObjectName, lpszVersion, lpszReferer, *lplpszAcceptTypes, dwFlags, dwContext ); 4.HTTP リクエストを送信 HttpSendRequest ( hRequest, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength ); 5.ファイル読み込みループで使用 InternetReadFile ( hRequest, lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead ); 6.HttpOpenRequest で取得したハンドルをクローズ InternetCloseHandle ( hRequest ); 7.InternetConnect で取得したハンドルをクローズ InternetCloseHandle ( hConnect ); 8.InternetOpen で取得したハンドルをクローズ InternetCloseHandle ( hInternet ); 個々の関数や、その中のパラメータについては、私は MSDN LIBRARY を参考にしていました: http://msdn.microsoft.com/en-us/library/aa385473(VS.85).aspx 手順や引数の設定を間違わなければ、~.php?id=xxxx のようなパラメータを含むケースでも問題はないはずなのですが。 異常終了がソースのどの箇所で発生するかを検出できれば解決が早いですが、この場合パケットの解析よりもプログラム自体のデバッグに重点を置いたほうがいいと思います。 私が経験したエラーとしては、保存するファイル名に「 \ / : * ? " <> | 」のような Windows のファイル名に使用できない文字が含まれていたケースです。 例えば、~.php?id=xxxx をそのままローカルファイル名として保存しようとすれば当然エラーになりますよね。 この場合は ? を別の文字に代えることで対処できました。

xi_fjw_ix
質問者

お礼

結果から申し上げますと、「HttpSendRequest」のエラーを回避して、「*.php」を正常に取得することができました。 「rumi_さん」、「talooさん」貴重なアドバイスをありがとうございました。 経緯は以下の通りです。 まず、プログラムのどこでエラーと認識するのか単純に考えました。それは「HttpSendRequest」実行直後の「GetLastError」のリターン値が「0」でなかったからです。 次に、「MSDN」の「HttpSendRequest」の説明をチェックしました。 そして「Return Value」の項の「Returns TRUE if successful, or FALSE otherwise. To get a specific error message, call GetLastError.」に着目しました。 プログラムでは「HttpSendRequest」のリターン値はチェックせず、いきなり「GetLastError」を呼んでエラーの有無をチェックしています。そこで「MSDN」の記述に沿って、「HttpSendRequest」のリターン値をチェックして、「FALSE」の場合にのみ「GetLastError」を呼ぶようにしました。 その結果、「HttpSendRequest」は正常に処理されました。 同様のコーディングが「HttpQueryInfo」の箇所にもありましたので、同時に修正したところ、正常に「*.php」が取得できました。 プログラム作成に当たっての反省 (1)「MSDN」をもう少しだけ大事にする (2)プログラムは手を抜かず、きっちり作る ただ今回、良い点もありました。 (1)ここでお世話になって問題が解決できたこと (2)正常か異常かの判断を「GetLastError」では行ってはいけない事がわかった。(リターン値が異常の場合にエラー情報取得のために使用する) (3)「Wireshark」に出会えたこと (4)TCPパケットや、HTTPの理解が少し深まったこと 「HttpSendRequest」のリターン値が正常終了を示しているのに、「GetLastError」は異常終了を示している、それも「ファイルが見つけられない」みたいな変なエラーで、な~んて?バグ?みたいな事もありますが、とりあえず動作に支障がなければOKにしておこうと思います。 クラス化したり、ExcelのVBAに移植したりと今後の展望もでてきました。 私の面倒臭がりと、プログラミングの未熟さが原因という落ちが付きましたが、正常に動作するようになってうれしいです。 「rumi_さん」、「talooさん」本当にありがとうございました。

xi_fjw_ix
質問者

補足

「rumi_さん」ご回答ありがとうございます。そして、丁寧にまとめて頂いて感謝いたします。 パケットの解析をしていても異常な点は見当たらず、なぜ「HttpSendRequest」がエラーになるのか、ますます疑問が深まるばかりです。ご指摘の通りに、もう一度プログラムを点検してみます。「*.html」「*.jpg」「*.zip」でうまくいっているので、見過ごしている箇所がないか再点検です。 「MSDN」も毛嫌いせずに、もう少し突っ込んで見る事にします。 ありがとうございます。

その他の回答 (2)

回答No.2

HttpSendRequest は第1パラメータに HttpOpenRequest の戻り値が入り、HTTP リクエストをサーバに送信します。その辺の動きは、パケットをチェックしている xi_fjw_ix さんの方が良くご存でしょう。 想像ですが HttpOpenRequest で指定したファイル名に問題があると、その戻り値(ハンドル)が正しく取得されず、それでエラーとなるのではないでしょうか。 「*.html」「*.jpg」「*.zip」のような静的(static)なファイル名指定と違い、「*.php」は一種の CGI でリクエストに応じて動的(dynamic)にデータを生成する仕組みなので、静的ファイルのつもりでリクエストを送った結果エラーになるのではないかと・・・ GET メソッドの場合は .php の後に何かパラメータが付いていませんか。URL にパラメータがなければ多分 POST でしょう。 それと「Inet/OpenURL」ですが、これは WinInet の InternetOpenUrl とは別物でしょうか。 私は VB 使いではありませんが、以前 Delphi で WinInet を使ったアプリを作ったとき、特に認証などを必要としないケースでは InternetOpenUrl を使った方が安定してるし、コーディングも簡単なので多用した記憶があります(笑)

xi_fjw_ix
質問者

補足

「rumi_さん」ご回答ありがとうございます。返信が遅くなり申し訳ありません。 「InternetOpenUrl」でもやってみましたが、結果は「HttpSendRequest」の時と同じ「エラーコード:2」で異常終了でした。 「HttpOpenRequest」で指定しているファイル名は、正常に取得できる場合も異常終了する場合も、「Wireshark」で見える範囲では同じに見えますので、問題はないのでは?と思っています。 また、このプログラムで取得しようとしている「*.php」のURLは、「~.php?id=xxxx (xxxx:4桁の数字)」の形式になっています。 「Inet/OpenURL」は「WinInet/InternetOpenUrl」の機能を包含している関数です。「OpenURL」の第1引数はデータを取得したいURL、第2引数は取得形式で、テキストかバイナリかの指定のみです。「InternetOpen」から「InternetReadFile」、そして「InternetCloseHandle」までの一連の動作を行ってくれる関数でした。 それから、「Wireshark」の結果を私なりにチェックしてみました。「GET」の部分の正常時と異常時の比較、「GET」直後に受信したパケットの比較では、特に異常と思えるような箇所は見つけられませんでした。 「*.php」は動的に変化するものなので、静的なものと同じ様に扱えないのでは?という「rumi_さん」のご指摘は的を射ていると感じています。「Inet/OpenURL」は「WinInet」を使っていると、どこかで見た記憶があります。「OpenURL」やブラウザで実現できている事なので、何かポイントがあるのだろうと思って、いろいろネット上で検索しているのですが、なかなか見つからないので、こちらでお世話になっております。 よろしく、お願いいたします。

回答No.1

HTTP 200 OK のHTTPヘッダを出した後のPHPの挙動が悪いんじゃないでしょうか。 Wiresharkのログはどうなっていますか?

xi_fjw_ix
質問者

補足

「taloo」さん、ご回答ありがとうございます。 「Wiresharkのログ」とは、どの情報なのか具体的に教えてください。 ネットでいろいろ検索して「Wireshark」の存在を知りましたが、全く使いこなせてはいません。「SYN」とか「ACK」、「GET」とか「OK」が交信されているのが、何となくわかるレベルで、情報の見方がよくわかっていません。 よろしく、お願いいたします。

関連するQ&A