2020年5月3日日曜日

WSACleanup でやらかした話


システムで思わぬところでエラーになって、俺関係ないやん?
そういう事ありません?

今回は、Windows の 通信Socket の初期化と終了処理でやらかした話を書きます。
ソース中の一箇所、WSACleanup の呼び出し場所が間違っていました。

正しくないコード
#include <winsock2.h>

void simpleSend( const char* str ) {
  WSADATA wsaData;

  if( !WSAStartup(MAKEWORD(2,0), &wsaData) ) {
    // 送信処理
  }
  WSACleanup();  // 初期化に失敗したのにクリーンナップが呼び出される。
  return 0;
}

これだと、WSAStartup に失敗した時も WSACleanup が呼び出されてしまいます。
WSAStartup と WSACleanup の処理は、コールカウント方式で処理されます。従って、サードパーティ製のDLLで WSAStartup していたコードが巻き添えを食らって WSACleanup されてしまうため、Socket の close 処理時にエラーになってしまうという現象となりました。
WSAStartup が失敗するタイミングとしては、アプリケーション終了間際があります。アプリケーションの終了間際にサードパーティ製のDLLが例外を送出してしまうのですが、この原因が WSAStartup が失敗したにも関わらず、WSACleanup を呼び出していた事によります。

正しいコード
#include <winsock2.h>

void simpleSend( const char* str ) {
  WSADATA wsaData;

  if( !WSAStartup(MAKEWORD(2,0), &wsaData) ) {
    // 送信処理
    WSACleanup();
  }
  return 0;
}
このように、WSAStartup に失敗したら、WSACleanup を呼び出さないようにする事が重要です。