При вызове функций wininet возникает случайное исключение NullReferenceException

Не уверен, почему, но случайным образом при вызове функции InternetQueryDataAvailable возникает исключение null ref без видимой причины, поскольку ни один из аргументов, которые он принимает, не может быть нулевым:

[DllImport(Dlls.Wininet, SetLastError = true)]
public static extern bool InternetQueryDataAvailable([In] IntPtr hFile, [Out] out int numberOfBytesAvailable, [Optional, In] int reserved0, [Optional, In] IntPtr reserved1);

Вот исключение:

введите здесь описание изображения

И нет, CheckHandle() не является виновником, поскольку все, что он делает, это проверяет, является ли _handle нулем или нет, то есть недействительным.

Кроме того, если это не так, то после загрузки всех данных и попытки закрыть приложение, грехи, которые я настроил, чтобы все дескрипторы закрывались до закрытия приложения, вызов InternetCloseHandle даже вызывает исключение null ref, как и в случае с InternetQueryDataAvailable, ни один из аргументов не может принимать значения NULL, поскольку все, что он принимает, это один IntPtr:

[DllImport(Dlls.Wininet, SetLastError = true)]
public static extern bool InternetCloseHandle([In] IntPtr hInternet);

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

Для тех, кому интересно, как выглядит функция с InternetCloseHandle, это просто:

public void Dispose()
{
    if (_handle != IntPtr.Zero)
    {
        if (!WinINet.InternetCloseHandle(_handle))
        {
            throw new Win32Exception();
        }
        _handle = IntPtr.Zero;
    }

}

Обратите внимание, что исключение, которое выдается после вызова InternetQueryDataAvailable, возникает только после первого вызова, поэтому первый вызов в порядке, но все последующие имеют шанс вызвать исключение.


person Vincent Bree    schedule 30.10.2019    source источник
comment
Я предполагаю, что это означает, что _handle не установлен. Но было бы очень полезно, если бы вы показали внутреннее исключение и трассировку стека - в основном результат Exception.ToString(). Есть две статьи об обработке исключений, на которые я часто ссылаюсь, возможно, вам стоит их прочитать: blogs.msdn.microsoft.com/ericlippert/2008/09/10/ | codeproject.com/Articles/9538/   -  person Christopher    schedule 31.10.2019
comment
@Christopher _handle равен IntPtr, даже если бы он был равен нулю, 1) пользовательское исключение будет вызвано из-за CheckHandle, 2) исключение нулевой ссылки было бы невозможно, поскольку IntPtr не может быть обнулено, и даже если проблема была с _handle что это не так, поскольку он указывает на действительный файл данных HTTP-запроса, InternetQueryDataAvailable возвращает логическое значение, которое указывает, успешно ли он завершен или нет, и не должен генерировать исключения, особенно неуправляемые С#.   -  person Vincent Bree    schedule 31.10.2019
comment
Но что, если значение InPtr равно null? Я немного заржавел, но указатель IIRC, установленный на все 0, является нереференсным способом аннотирования нулевых значений. Будет ли попытка разрешить экземпляр за _handle, а затем не вызывать исключение с нулевой ссылкой?   -  person Christopher    schedule 31.10.2019
comment
Исключение NullReferenceException может исходить от самого InternetQueryDataAvailable, возможно, из-за того, что дескриптор не нулевой, но недопустимый.   -  person Raymond Chen    schedule 31.10.2019
comment
@RaymondChen, как я писал: Note that the exception which is thrown after calling InternetQueryDataAvailable only occurs after the first call, первый вызов успешен, и нет, я не вызываю InternetCloseHandle сразу после этого, после него я вызываю InternetReadFile, который затем возвращается к InternetQueryDataAvailable, и это продолжается до тех пор, пока либо возврат из запроса не станет ложным, либо число байт равно нулю, только после этого дескриптор закрывается, поэтому нет, дескриптор действителен на протяжении всей процедуры.   -  person Vincent Bree    schedule 31.10.2019
comment
Удалите необязательные атрибуты и передайте что-то реальное   -  person Simon Mourier    schedule 31.10.2019
comment
@SimonMourier попробовал это, удалил необязательный и сделал InternetQueryDataAvailable(_handle, out numberOfBytes, 0, IntPtr.Zero);, но это ничего не изменило, исключение null ref все еще возникает.   -  person Vincent Bree    schedule 31.10.2019
comment
Хорошо, но я хочу сказать, что NullReferenceException может исходить от самого InternetQueryDataAvailable. Не исключайте этого. Запуск с отладчиком для исключений первого шанса должен идентифицировать источник.   -  person Raymond Chen    schedule 31.10.2019
comment
@RaymondChen, похоже, это нравится, ну, я пришел к выводу, что я тратил слишком много времени на загрузку данных, потому что срок их действия истек, и поэтому было выбрано исключение, не уверен, но похоже, что это решило проблему для меня для время.   -  person Vincent Bree    schedule 31.10.2019


Ответы (2)


То, что я не указал, что было причиной этого исключения нулевой ссылки, заключалось в том, что когда выполняется InternetOpen, я мгновенно назначаю дескриптор обратного вызова состояния через InternetSetStatusCallback, просто выполняя InternetSetStatusCallback(handle, CallbackMethod);, по-видимому, происходило то, что сборщик мусора собрал делегат, потому что он предположительно не использовался, обратите внимание, что когда InternetQueryDataAvailable вызывается, статусы INTERNET_STATUS_RECEIVING_RESPONSE и INTERNET_STATUS_RESPONSE_RECEIVED действительно передаются обратному вызову, поэтому я не уверен, почему GC собрал делегат, но это было причиной нулевой ссылки . Дело было не в том, что InternetQueryDataAvailable выдавал исключение, как некоторые подозревали. Решение для этого состояло в том, чтобы создать поле, которому назначается делегат, например, чтобы сборщик мусора не собирал его из-за того, что класс, содержащий его, всегда жив и никогда не выходит за рамки.

person Vincent Bree    schedule 30.10.2019
comment
Если вы хотите проголосовать против этого ответа, пожалуйста, либо предоставьте лучший ответ, либо прокомментируйте, почему вы решили проголосовать против. Обратите внимание, что комментировать что-то вроде: This is not a good answer, but I don't have a answer of my own не является хорошим оправданием. - person Vincent Bree; 31.10.2019

Обратите внимание, что исключение, которое выдается после вызова InternetQueryDataAvailable, возникает только после первого вызова, поэтому первый вызов в порядке, но все последующие имеют шанс вызвать исключение.

Этого следовало ожидать. Это поиск данных. Они могут всегда сталкиваться с экзогенными исключениями. Не имеет значения, охватывает ли эта функция доступ к файлу или сетевую операцию, охватывающую половину планеты — оба этих сценария имеют потенциал экзогенного исключения в пиках.

Конечно, конкретное исключение по-прежнему странно.

person Christopher    schedule 30.10.2019
comment
Верно, но, как написано в документе для InternetQueryDataAvailable: If there is currently no data available and the end of the file has not been reached, the request waits until data becomes available.. Кроме того, неуправляемый код не может генерировать управляемые исключения, единственное, что может это импортировать COM, и все же, только типа ComException. - person Vincent Bree; 31.10.2019
comment
@VincentBree: даже когда он ждет, он должен ждать только до тех пор, пока не произойдет тайм-аут или не будет подтверждено, что соединение разорвано. В этих случаях все еще ждать, а не заканчивать исключением, было бы плохой идеей. Плохое обращение со взрывчаткой плохо, на самом деле :) - person Christopher; 31.10.2019