忍者ブログ
プログラミングとか日常とかの覚書っぽいなにか
[13] [12] [11] [10] [9] [8] [7] [6] [5] [4] [3]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

今日は再びWinsockの話。
前回は簡略化のために、エラー処理を省いたり、1文字ずつしかやり取りしなかったりと思いっきり手を抜いてきたわけですが、少しずつ実用に耐えうる形に直していけたらいいなと思っています。
時間があればですが。

今日はエラー処理の1つ、Winsock初期化時のエラー判定のお話でも。


Winsockの初期化にはWSAStartup関数を使いますが、あちこちのサイトを見ていると、この関数に要求バージョン(とWSADATA構造体)を渡して、戻り値がゼロ以外ならエラー、戻り値ゼロなら正常に初期化完了!としているところがあまりにも多いんですが、ホントにそれでいいのかな?と思うんですよ。
少なくとも、本来の意図した目的を達成するコードではないことは確かだと思います。

例えば、WSAStartup関数にバージョン2.0を要求して(C/C++での書き方では最初の引数に MAKEWORD(2,0)、すなわち 0x0002 を渡し)、戻り値がゼロであれば、バージョン2.0のWinsockが利用可能であると考えているのかも知れませんが、実は必ずしもそうとは限りません。
WSAStartupは、Winsock実装とアプリとの間で、使用するバージョンのネゴシエーション(情報のやり取り、すりあわせ)を行うように作られていて、「このシステムのWinsockはバージョン△と□をサポートしてますよ、あなたのアプリはどれとどれのバージョンを使えますか?」というようなやり取りの元で、「では両方が共通して使えるこのバージョン△でいきましょう」という決定を下さなくてはならないのです。

では、本来の初期化はどのようにすべきなのか?
それは以下のようになります。

WSAStartup関数に、アプリで利用可能なもっとも大きいバージョンを要求します。アプリが 1.1 でも 2.0 でも大丈夫なら 2.0 の方を指定します。

WSAStartupの戻り値がゼロ以外である場合。これは、Winsockが、アプリで利用可能なもっとも大きいバージョンよりもさらにバージョン番号の大きいものしかサポートしていないということなので、もはやWinsock実装とアプリで共通して使えるバージョンはないことを表しています(下位互換性が保証されていて、大きいバージョンの実装ならば、より小さいバージョンを必ずサポートしているはず!ということならばこのようなことはないんでしょうけどね)。
または、バージョン関連以外でエラーが発生した場合もゼロ以外の戻り値になります。
この場合は、エラーメッセージでも出してアプリを終了するしかありません。

WSAStartupの戻り値がゼロであれば、初期化は完了かというと、そうではありません。まだ、「指定したバージョン、またはそれより小さいバージョン」のものをシステムのWinsock実装がサポートしていることが分かっただけです。

今度は、WSADATA構造体のwVersionメンバとwHighVersionメンバに注目します。
wVersionメンバは「このバージョンなら使えるんじゃない?」とWinsockがアプリに聞いてきているバージョン、wHighVersionメンバは「このシステムで使えるなかで最も高いバージョンはコレ」と示したバージョンになっています。
一般的なプログラムではwHighVersionメンバは大して重要ではなく、wVersionメンバのバージョンがアプリで利用可能かどうかを判定します。
先ほどの例のように、アプリ側は 1.1 と 2.0 が利用可能で、システムが 1.1 しか使えない場合には、WSAStartupの引数に MAKEWORD(2,0) を渡して関数が成功したにも関わらず、wVersionメンバが MAKEWORD(1,1) の値になっていて、実際にはバージョン1.1で初期化されているということなのです。

つまり、アプリでは、WSAStartup関数がゼロを返せばオッケーというわけではなく、wVersionメンバのバージョンを判定して、このバージョンがアプリで利用可能でない場合には、バージョン不一致のエラーとして扱わなければならないということです。この場合、WSAStartup関数呼び出し自体は成功しているので、WSACleanup関数を呼び出しておかなければいけないことに注意してください。

これらのチェックをクリアできて初めて、Winsockの初期化が完了したといえるわけです。
初期化だけでも一苦労という感じです。

実際には、「1.1と2.0の両方が使える」というような難しいことはせず、「アプリが扱えるのはWinsock 2.0 だけだ!」と1つだけに決めてしまってもいいでしょう(もちろん「1.1だけだ」としてもOK)。
この場合は、WSAStartup関数に要求バージョン(今回の例では2.0)を指定し、戻り値がゼロで、かつWSADATA構造体のwVersionメンバが要求バージョンと同じであれば初期化成功とできます。
Windows 98以降、現在までのWindowsにはWinsockバージョン2.2がインストールされており、バージョン1.1や2.0もサポートに含まれているので、2.0(または1.1)の1つだけに決め打ちする方法で問題ありません(そのおかげで、よく見られるようなWSAStartupの戻り値だけで判定するプログラムでも運よく動いているわけですが)。

さて、実際にHSPでは実際の初期化処理をどう行えばいいのかを、以下に書いてみましたので、参考にしてみてください。
; 外部関数(WinAPI)定義
#uselib "ws2_32.dll"
#func WSAStartup "WSAStartup" int,int
#func WSACleanup "WSACleanup"

; Winsockの使用バージョン
; (2バイト値のうち、下位バイトがメジャーバージョン番号、上位バイトが
; マイナーバージョン番号であるため、2.0 は 0x0002 となることに注意)
winsockver = 0x0002

; WSAStartup関数呼び出し(失敗した場合はエラーで終了)
dim wsadata, 100
WSAStartup winsockver, varptr(wsadata)
if stat != 0 {
dialog "初期化エラー"
end
}

; WSADATA構造体のwVersionメンバが要求バージョンと一致しなければエラー
; (この場合はWSACleanup関数が呼ばれるようにする)
if wpeek(wsadata, 0) != winsockver {
dialog "初期化エラー"
goto *cleanup
}

; アプリ終了時にWSACleanup関数が呼ばれるようにする
onexit goto *cleanup

;===================================================================
; ここでアプリの本来の処理を行う
;===================================================================
stop

*cleanup
; Winsockのクリーンアップ
WSACleanup
end

拍手

PR

コメント


コメントフォーム
お名前
タイトル
文字色
メールアドレス
URL
コメント
パスワード
  Vodafone絵文字 i-mode絵文字 Ezweb絵文字


トラックバック
この記事にトラックバックする:


忍者ブログ [PR]
プロフィール
HN:
はむぱい
職業:
ソフト作ったりしてる人
Twitter
最新CM
[06/09 replica rolex oyster perpetual datejust]
[06/09 bracelets imitation cartier love]
[06/09 replica the oyster perpetual datejust]
[06/09 datejust rolex oyster perpetual]
[06/09 replica gold love bangle]
カレンダー
06 2017/07 08
S M T W T F S
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
ブログ内検索
あ~いい漢字