文档说明可以多次调用InternetOpen而不会出现任何问题。我的问题是,我应该多次在它返回的句柄上调用InternetCloseHandle吗?
例如,我有一个我调用CAPIRequestContext
的类,它有一个由InternetOpen返回的句柄。我的每个请求都有它自己的副本。现在,我在析构函数中调用了InternetCloseHandle,因此它被多次调用。
我想知道以下是否会导致问题:
线程A创建一个CAPIRequestObject
,它调用InternetOpen并存储句柄。线程B执行相同操作,但在线程A退出之前超出范围,因此线程B调用它自己的CAPIRequestObject
中的析构函数,该函数在InternetOpen返回的句柄上调用InternetCloseHandle
。
我应该在班级的析构函数中删除对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;
}
答案 0 :(得分:1)
文档说明可以多次调用InternetOpen而不会出现任何问题。我的问题是,我应该多次在它返回的句柄上调用InternetCloseHandle吗?
是的,如InternetOpen()
文档中所述:
在调用应用程序使用
InternetOpen
返回的HINTERNET
句柄完成后,必须使用InternetCloseHandle
函数关闭它。
例如,我有一个我调用
CAPIRequestContext
的类,它有一个由InternetOpen
返回的句柄。我的每个请求都有它自己的副本。现在,我在析构函数中调用InternetCloseHandle
,因此多次调用它。
如果该类的每个实例都调用InternetOpen()
,或者获得唯一HINTERNET
的所有权,那么这将是一个正确的实现。
HOWEVER ,请注意此类需要实现Rule of Three。基本上,如果您必须提供析构函数来释放资源,您还需要提供一个复制构造函数和复制赋值运算符。
但是,您无法在同一InternetCloseHandle()
上多次致电HINTERNET
,因此您无法使用同一CAPIRequestContext
多个HINTERNET
他们都叫InternetCloseHandle()
1 。因此,您的复制构造函数和复制赋值运算符必须:
取得正在复制的来源HINTERNET
的{{1}}的所有权。
完全禁用,以防止将CAPIRequestContext
复制到另一个。{/ p>
在你的情况下,我会选择#2。
1 :您需要一个每实例标志,指示哪个实例可以调用它,哪些实例不能调用它。但这不是好的课堂设计。如果您需要共享CAPIRequestContext
,则应该实现引用计数语义,例如由HINTERNET
提供。
我想知道以下是否会导致问题:线程A创建一个CAPIRequestObject,它调用InternetOpen并存储句柄。线程B执行相同操作,但在线程A退出之前超出范围,因此线程B调用其自身的CAPIRequestObject中的析构函数,该函数调用InternetOpen返回的句柄上的InternetCloseHandle。
这是完全安全的,前提是每个std::shared_ptr
都有自己的CAPIRequestObject
。
我应该在班级的析构函数中删除对InternetCloseHandle的调用吗?
不,如果每个类实例都包含唯一的HINTERNET
。
我假设我应该为HttpOpenRequest返回的句柄调用InternetCloseHandle。
是的,正如HttpOpenRequest()
文档中所述:
在调用应用程序使用
HINTERNET
返回的HINTERNET
句柄完成后,必须使用InternetCloseHandle
函数关闭它。
我对InternetConnect返回的句柄有类似的疑问。这些句柄是否共享?
每个HttpOpenRequest
必须单独关闭。