MSDN молчит, вскрытие покажет

Модератор: Хакер

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

MSDN молчит, вскрытие покажет

Сообщение Хакер » 11.07.2009 (Сб) 19:49

Я считаю, что MSDN довольно качественная документация. Но, черт возьми, иногда и в ней бывают пробелы. Я всегда, когда сталкиваюсь с подобным, думаю, что стоило бы записывать это куда-то, чтобы другие не натыкались внезапано, а знали заранее. Но не получается запоминать о неточностях, а потом вспоминать и писать.

Поэтому я буду писать о них тогда, когда натыкаюсь. Я знаю достаточное количество, но я не могу сейчас тратить время на то, чтобы описать все. И я не буду описывать только некоторые, потому что я не знаю? где граница между «немного» и «это уж слишком». Вместо этого я буду постить сюда только тогда, когда вновь натыкаюсь на «проблемную» функцию.

Я настоятельно прошу не постить в этот топик без крайней необходимости, чтобы мои новые заметки не смешались с обсуждением и комментариями предыдущих, флудом и вспомнившимися кстати историями.

Обратите внимание на то, что названия функций, к котором относятся заметки, содержатся в заголовках сообщений.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

HttpSendRequest

Сообщение Хакер » 11.07.2009 (Сб) 20:07

Что пишет MSDN:
Returns TRUE if successful, or FALSE otherwise. To get extended error information, call GetLastError.


Как оно на самом деле
    На самом деле TRUE возвращается в случая успеха только при синхронных запросах. При асинхронных функция всегда возвращает FALSE; и это вовсе не означает, что произошла какая-то ошибка.

Мораль
    Проверять и обрабатывать ошибки — хорошо. Но в данном случае надо помнить, что проверять возвращаемое значение этой функции нужно только при синхронных запросах (иначе вы всегда будете обрабатывать ошибку, которой нет).
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

HttpQueryInfo

Сообщение Хакер » 14.07.2009 (Вт) 1:32

Что пишет MSDN:
Описание третьего параметра этой функции:
lpvBuffer
    [in, out] Pointer to a buffer to receive the requested information. This parameter must not be NULL.


Как оно на самом деле
На самом деле в большинстве случаев вы не знаете (и не можете знать), каким должен быть размер буфера, чтобы его хватило этой функции. И единственное, что вам остаётся сделать: вызвать эту функцию, передав в качестве третьего параметра NULL, чтобы узнать, какого размера нужен буфер (функция запишет нужный размер в переменную, указатель на который передан четвёртым параметром).

Мораль
«This parameter must not be NULL» — ложь. Вовсе не «must not», а вполне «can». И не просто «can», а «should be NULL, if you want to obtain enough size of buffer».
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

InternetStatusCallback

Сообщение Хакер » 14.07.2009 (Вт) 16:30

Что пишет MSDN:
Описание нотификации INTERNET_STATUS_RESPONSE_RECEIVED гласит:
Successfully received a response from the server. The lpvStatusInformation parameter points to a DWORD value that contains the number of bytes received.


Как оно на самом деле
Во-первых, когда это сообщение приходит в первый раз, в «number of bytes received» содержится полное число принятых байтов. В случае HTTP-запроса в них входят и заголовки. Но прочитать эти все принятые данные с помощью InternetReadFile у вас не получится, потому что о вас позаботились, и сделали так, что InternetReadFile читает только тело HTTP-ответа. Это значит, что чтобы хоть как-то прочитать тело ответа, вам нужно самим (к черту такая забота?) отследить момент, когда придут все заголовки, определить их длину и отнять от числа, которое вам сообщили с INTERNET_STATUS_RESPONSE_RECEIVED, чтобы узнать, сколько действительно байтов вы можете прочитать с помощью InternetReadFile.

Но это ещё не всё.
Во-вторых, INTERNET_STATUS_RESPONSE_RECEIVED сообщает не о том (кроме первого раза), сколько байтов действительно принято, а о том, сколько байтов из тех, что вы просили, приятно. Под «сколько вы просили» понимается попытка прочитать данные из буфера, котороых в буфере нет!

Допустим, вам сначала пришло 200 байт. Из них 150 — заголовки, 50 — тело сообщения. Вы вызываете InternetReadFile, чтобы прочитать эти 50 байт себе, прочитываете. Дальше вы ждёте «события» INTERNET_STATUS_RESPONSE_RECEIVED, чтобы узнать, когда придёт следующая порция, и прочитать её.

Так? Ждёте? Ждите-ждите. Новое событие INTERNET_STATUS_RESPONSE_RECEIVED больше не произойдёт. Почему? Потому что WinInet заботится о вас и полагает, что раз вы, после того как прочитали свои 50 байтов, больше ничего не попытались прочитать, то значит вам больше ничего и не нужно. И WinInet не станет отвлекать вас событием INTERNET_STATUS_RESPONSE_RECEIVED, вам ведь больше не интересно, что новые данные пришли, раз вы не попытались их прочитать, пока они ещё не пришли? Забавно да?

Понимаете, кака логика? Чтобы WinINet не прекращал сообщать вам о приходящих данных, вам нужно, в те моменты, когда данные в буфере кончились, а новые ещё не пришли, преднамеренно делать ошибочные попытки что-то читать из буфера.

Это не так просто, как кажется. Первый вопрос: сколько байтов просить, делая такие i-want-more-попытки? Тут на самом деле есть подводный камень. К тому моменту, когда вы будете делать fake-вызов (попытка прочитать, когда вы знаете,что читать нечего), данные в от сервера могут появится, и fake-вызов действительно что-то прочитает. Проблема в том, что вы уже прочитали эти данные, но о том, что они пришли, узнаете чуть позже :) И, скорее всего, когда узнаете, вы захотите их опять прочитать.

Мораль
Если вы ждёте ответ от сервера, этот ответ не имеет конкретной длины, а сервер отвечает маленькими блоками с большим временным промежутком, вы испытаете много проблем.
Так что юзайте сокеты (боже, я это говорю?) или новую библиотеку WinHTTP, которая доступна только начиная с WinXP SP1 или Win2k SP3.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

WinHttpOpenRequest

Сообщение Хакер » 19.07.2009 (Вс) 1:19

Что пишет MSDN:
Returns a valid HTTP request handle if successful, or NULL if not. For extended error information, call GetLastError. Among the error codes returned are:

ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
    The type of handle supplied is incorrect for this operation.
...


Как оно на самом деле
На самом деле, если функции передать в качестве первого параметра некорректный хендл (например 0), она возвращает ERROR_INVALID_HANDLE, а не ERROR_WINHTTP_INCORRECT_HANDLE_TYPE.

Мораль
Если WinHttpOpenRequest не сработала, надо проверять возврат GetLastError на предмет равенства обоим константам, а не только той, о которой говорится в MSDN.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.


Вернуться в Мой блог

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 12

    TopList