2016-06-06 6 views
0

В документации указано, что InternetOpen можно вызывать несколько раз без каких-либо проблем. Мой вопрос, хотя, должен я должен вызывать InternetCloseHandle на дескрипторе, возвращенном им несколько раз?WinInet и InternetOpen

Например, у меня есть класс, который я называю CAPIRequestContext, у которого есть дескриптор, который возвращается InternetOpen. У каждого из моих запросов есть своя собственная копия. Прямо сейчас я вызываю InternetCloseHandle в деструкторе, поэтому он вызывается несколько раз.

Мне интересно, могут ли возникнуть следующие проблемы: Тема А создает CAPIRequestObject, который вызывает InternetOpen и сохраняет дескриптор. Thread B делает то же самое, но затем выходит за рамки до того, как Thread A выйдет, поэтому Thread B вызывает деструктор в собственном CAPIRequestObject, который вызывает InternetCloseHandle на дескрипторе, возвращенном InternetOpen.

Должен ли я удалить вызов InternetCloseHandle в деструкторах моего класса? По крайней мере, для InternetHandle? Я предполагаю, что я должен вызвать InternetCloseHandle для дескриптора, возвращаемого HttpOpenRequest.

У меня есть аналогичные вопросы относительно ручки, возвращаемой 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; 

} 
+1

Каждый InternetOpen() должен быть точно сопряжен с InternetCloseHandle(), который передает дескриптор, полученный от открытого вызова.Не могу предположить, что ваш код еще этого не делает. Почувствуйте это, используя assert() для возвращаемого значения. –

ответ

1

В документации говорится, что InternetOpen можно назвать несколько раз без каких-либо проблем. Мой вопрос, хотя, должен я должен вызывать InternetCloseHandle на дескрипторе, возвращенном им несколько раз?

Да, как указано в документации InternetOpen():

После того, как вызывающее приложение завершило с помощью HINTERNET ручки, возвращенное InternetOpen, он должен быть закрыт с помощью функции InternetCloseHandle.

К примеру, у меня есть класс, я называю CAPIRequestContext, который имеет дескриптор, который возвращается InternetOpen. У каждого из моих запросов есть своя собственная копия. Прямо сейчас, я вызываю InternetCloseHandle в деструктор, поэтому он вызывается несколько раз.

Это было бы правильное применение, если каждый экземпляр класса называет InternetOpen() или иным образом получает право собственности уникальной HINTERNET.

ОДНАКО, имейте в виду, что такой класс должен реализовывать Rule of Three. В принципе, если вам нужно предоставить деструктор для выпуска ресурса, вам также необходимо предоставить оператор-копир и оператор копирования-назначения.

Но вы не можете назвать InternetCloseHandle() несколько раз на одной и той же HINTERNET, так что вы не можете иметь несколько CAPIRequestContext, используя тот же HINTERNET и все они называя InternetCloseHandle() . Таким образом, ваш конструктор копирования и копирование назначение оператор должен:

  1. принять владение HINTERNET от источника CAPIRequestContext, копируемые.

  2. полностью отключить, чтобы предотвратить копирование одного CAPIRequestContext в другое.

В вашем случае я бы выбрал № 2.

: Вам понадобится флаг для каждого экземпляра, указывающий, какой экземпляр может вызвать его, а какие нет. Но это не классный дизайн. Если вам нужно разделить HINTERNET, вам следует вместо этого использовать семантику подсчета ссылок, например, предоставленную std::shared_ptr.

Мне интересно, могут ли возникнуть следующие проблемы: Thread A создает объект CAPIRequestObject, который вызывает InternetOpen и сохраняет дескриптор. Thread B делает то же самое, но затем выходит за пределы области действия до того, как Thread A выйдет, поэтому Thread B вызывает деструктор в собственном CAPIRequestObject, который вызывает InternetCloseHandle на дескрипторе, возвращаемом InternetOpen.

Это абсолютно безопасно, если у каждого CAPIRequestObject есть свои HINTERNET.

Следует ли мне удалить вызов InternetCloseHandle в деструкторах моего класса?

Нет, если каждый экземпляр класса содержит уникальный номер HINTERNET.

Предполагаю, что я должен вызвать InternetCloseHandle для дескриптора, возвращаемого HttpOpenRequest.

Да, как указано в документации HttpOpenRequest():

После того, как вызывающее приложение завершило с помощью HINTERNET ручки, возвращенное HttpOpenRequest, он должен быть закрыт с помощью функции InternetCloseHandle.

У меня есть аналогичные вопросы относительно ручки, возвращенного InternetConnect. Разделяются ли эти ручки?

Каждый HINTERNET должен быть закрыт отдельно.

+0

Я почти уверен, что все это делаю, хотя мне нужно проверить конструктор копирования, а назначение копии переопределено, чтобы убедиться, что я не делаю копии. – bpeikes

+0

Я пытаюсь отследить большой, который, по-видимому, может быть вызван одним экземпляром моего CAPITequestContext, который уничтожается и вызывает проблемы с другими потоками, используя WinInet в том же приложении. В основном изменение порядка некоторых вызовов в вызове, похоже, устраняет проблему, и я думаю, что это может быть связано с вызываемыми деструкторами. – bpeikes

+0

Без [минимального, полного и проверяемого примера] (http://stackoverflow.com/help/mcve) никто здесь не может помочь вам в этом. –