[解決済み] WinInetとInternetOpen
質問
ドキュメントには、InternetOpenは問題なく複数回呼び出すことができると記載されています。しかし、私の質問は、私はそれが返されたハンドルにInternetCloseHandleを複数回呼び出す必要がありますか?
例えば、私のクラスは
CAPIRequestContext
このハンドルは InternetOpen によって返されます。私のリクエストはそれぞれそれ自身のコピーを持っています。現在、デストラクタで InternetCloseHandle を呼び出しているので、複数回呼び出されています。
次のようなことが問題になるのではないかと思っています。
スレッド A が
CAPIRequestObject
InternetOpenを呼び出し、ハンドルを保存します。スレッド B も同じことをしますが、スレッド A が終了する前にスコープ外に出てしまうので、スレッド B は自身の
CAPIRequestObject
を呼び出し、その結果
InternetCloseHandle
を、InternetOpen が返すハンドル上で実行します。
私のクラスのデストラクタで InternetCloseHandle の呼び出しを削除すべきでしょうか?少なくともInternetHandleについては?私は、HttpOpenRequestによって返されたハンドルに対してInternetCloseHandleを呼び出すべきだと推測しています。
InternetConnect が返すハンドルに関しても同様の質問があります。これらのハンドルは共有されているのでしょうか?
以下はサンプルコードで、問題とは関係ないと思われる外部コードを除いたものです。
class CAPIClient;
class CAPIRequest
{
public:
CAPIRequestContext()
{
m_hConn = NULL;
m_hInternet = NULL;
m_hRequest = NULL;
}
~CAPIRequestContext()
{
if (m_hRequest) InternetCloseHandle(m_hRequest);
if (m_hConn) InternetCloseHandle(m_hConn);
if (m_hInternet) InternetCloseHandle(m_hInternet);
}
bool Init(const CAPIClient &client, const std::string &uri, const std::string &method)
{
ATLASSERT(!(m_hInternet || m_hConn || m_hRequest));
if (m_hInternet || m_hConn || m_hRequest) throw std::exception("Cannot init request more than once.");
bool success = false;
m_AuthToken = *client.m_pAuthToken;
URI = uri;
m_hInternet = InternetOpen("MyApp", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
DWORD requestTimeout = 60 * 1000; // Set timeout to 60 seconds instead of 30
if (m_hInternet)
{
InternetSetOption(m_hInternet, INTERNET_OPTION_RECEIVE_TIMEOUT, &requestTimeout, sizeof(requestTimeout));
m_hConn = InternetConnect(m_hInternet, (LPSTR)client.m_host.c_str(), client.m_port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)this);
if (m_hConn) {
m_hRequest = HttpOpenRequest(m_hConn, method.c_str(), uri.c_str(), "HTTP/1.1", NULL, NULL, client.GetOpenRequestFlags(), 0);
}
if (m_hRequest)
{
success = true;
}
}
return success;
}
}
// There are additional calls like
// SendRequest
// GetData
// GetFullResponse
private:
// Added these methods to make sure I'm not copying the handles
enter code here
CAPIRequestContext(const CAPIRequestContext &other) = delete;
CAPIRequestContext& operator=(const CAPIRequestContext& other) = delete;
private:
HINTERNET m_hInternet;
HINTERNET m_hConn;
HINTERNET m_hRequest;
}
解決方法は?
<ブロッククオートドキュメントには、InternetOpenは問題なく複数回呼び出すことができると記載されています。しかし、私の質問は、私はそれが返されたハンドルにInternetCloseHandleを複数回呼び出す必要がありますか?
はい。
InternetOpen()
のドキュメントを参照してください。
呼び出し側のアプリケーションが
HINTERNET
が返すハンドルInternetOpen
, を使用して閉じなければなりません。InternetCloseHandle
機能 .
例えば、私は以下のようなクラスを持っています。
CAPIRequestContext
で返されるハンドルを持っています。InternetOpen
. 私のリクエストはそれぞれそれ自身のコピーを持っています。今、私はInternetCloseHandle
をデストラクタで呼び出すと、複数回呼び出されることになります。
これは正しい実装で、もしクラスの各インスタンスが
InternetOpen()
の所有権を得るか、あるいは一意の
HINTERNET
.
しかし を実装する必要があることに注意してください。 ルールオブスリー . 基本的に、リソースを解放するためのデストラクタを提供しなければならない場合、同様にコピーコンストラクタとコピーアサインメント演算子を提供する必要があります。
しかし
InternetCloseHandle()
を複数回実行すると、同じ
HINTERNET
ということで、複数の
CAPIRequestContext
を使用して、同じ
HINTERNET
を呼び出し、それらすべてが
InternetCloseHandle()
1
. ですから、コピーコンストラクタとコピー代入演算子はどちらかでなければなりません。
-
の所有権を取得します。
HINTERNET
ソースからCAPIRequestContext
をコピーしています。 -
コピーできないように完全に無効にする
CAPIRequestContext
を別のものに置き換えます。
あなたの場合、私なら2を選びますね。
1
: どのインスタンスが呼び出せて、どのインスタンスが呼び出せないかを示す、インスタンスごとのフラグが必要でしょう。しかし、これは良いクラス設計とは言えません。 もし
HINTERNET
が提供するような参照カウントのセマンティクスを実装する必要があります。
std::shared_ptr
.
以下のようなことが問題になるのではないかと思っています。スレッドAは、CAPIRequestObjectを作成し、InternetOpenを呼び出してハンドルを保存します。スレッド B も同じことをしますが、スレッド A が終了する前にスコープ外に出るので、スレッド B は、それ自身の CAPIRequestObject でデストラクタを呼び出し、 InternetOpen が返したハンドルに対して InternetCloseHandle を呼びます。
これは完全に安全であり、各
CAPIRequestObject
は独自の
HINTERNET
.
クラスのデストラクタでInternetCloseHandleの呼び出しを削除した方が良いですか?
いいえ、各クラスのインスタンスが一意の
HINTERNET
.
HttpOpenRequestから返されたハンドルに対してInternetCloseHandleを呼び出す必要があると推測されます。
はい、に記載されているように
HttpOpenRequest()
のドキュメントを参照してください。
呼び出し側のアプリケーションが
HINTERNET
が返すハンドルHttpOpenRequest
, を使用して閉じなければなりません。InternetCloseHandle
機能 .
InternetConnectから返されるハンドルに関しても同様の質問があります。これらのハンドルは共有されているのでしょうか?
それぞれの
HINTERNET
は個別に閉じる必要があります。
関連
-
[解決済み】Cygwin Make bash コマンドが見つかりません。
-
[解決済み】C++の余分な資格エラー
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み】指定範囲内の乱数で配列を埋める(C++)
-
[解決済み】なぜ、サイズ8の初期化されていない値を使用するのでしょうか?
-
[解決済み] 変数サイズのオブジェクトが初期化されないことがある c++
-
[解決済み] 1ビットのセット、クリア、トグルはどのように行うのですか?
-
[解決済み] static_cast, dynamic_cast, const_cast, reinterpret_cast はいつ使うべきですか?
-
[解決済み] コピーアンドスワップ慣用句とは?
-
[解決済み] スマートポインターとは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】 unsigned int vs. size_t
-
[解決済み】コンストラクターでのエラー:識別子を期待されますか?
-
[解決済み】致命的なエラー LNK1169: ゲームプログラミングで1つ以上の多重定義されたシンボルが発見された
-
[解決済み】C++エラー:の初期化に一致するコンストラクタがありません。
-
[解決済み】リンカーエラーです。"リンカ入力ファイルはリンクが行われていないため未使用"、そのファイル内の関数への未定義参照
-
[解決済み】C++の余分な資格エラー
-
[解決済み】なぜ、サイズ8の初期化されていない値を使用するのでしょうか?
-
[解決済み】VC++の致命的なエラーLNK1168:書き込みのためにfilename.exeを開くことができません。
-
[解決済み】デバッグアサーションに失敗しました
-
[解決済み] 警告:暗黙の定数変換でのオーバーフロー