Правильный способ обработки NULL-параметра ppPins в методе EnumPins фильтра DirectShow (Delphi / DSPACK)? - PullRequest
2 голосов
/ 29 октября 2011

У меня есть собственный фильтр push-кода, написанный на Delphi 6 с использованием библиотеки компонентов DSPACK DirectShow на компьютере с Windows XP / 32.Во время кодирования я столкнулся с проблемой с ошибками проверки диапазона , возникающими в модуле BaseClass , принадлежащем DSPACK (BaseClass.pas), после того как я включил Проверка диапазона вОпции компилятора моего приложения.

Ошибка происходит во время моего метода EnumPins ().Обратите внимание, что этот метод находится в модуле BaseClass DSPACK, а не в моем приложении.Я отследил проблему и обнаружил, что она возникает, когда построенный мной график фильтра, использующий мой фильтр, воспроизводит фильтр.Обратите внимание, что мой фильтр напрямую включен в мое приложение как частный незарегистрированный фильтр, а не как внешний AX.Когда DirectShow вызывает метод базового класса TBCEnumPins.Next () , если параметр ppPins равен NIL , возникает ошибка проверки диапазона.Поскольку я не являюсь экспертом по DirectShow, я не уверен, как правильно исправить эту ошибку, не нарушая при этом процесс перечисления выводов DirectShow.Если вместо этого это истинное условие ошибки, которое не следует игнорировать, то мне нужно знать, что является правильным кодом исключения или HRESULT для возврата в этом событии.Может кто-нибудь сказать мне, как правильно настроить этот код для параметра NIL ppPins?Полный код метода ниже со строкой, в которой происходит ошибка проверки диапазона, выделен:

function TBCEnumPins.Next(cPins: ULONG; out ppPins: IPin; pcFetched: PULONG): HRESULT;
type
  TPointerDynArray = array of Pointer;
  TIPinDynArray = array of IPin;
var
  Fetched: cardinal;
  RealPins: integer;
  Pin: TBCBasePin;
begin
    // ATI: Debugging range check error.
    try
        if pcFetched <> nil then
          pcFetched^ := 0
        else
          if (cPins>1) then
          begin
            result := E_INVALIDARG;
            exit;
          end;
        Fetched := 0; // increment as we get each one.

        // Check we are still in sync with the filter
        // If we are out of sync, we should refresh the enumerator.
        // This will reset the position and update the other members, but
        // will not clear cache of pins we have already returned.
        if AreWeOutOfSync then
          Refresh;

        // Calculate the number of available pins
        RealPins := min(FPinCount - FPosition, cPins);
        if RealPins = 0 then
        begin
          result := S_FALSE;
          exit;
        end;

        {  Return each pin interface NOTE GetPin returns CBasePin * not addrefed
           so we must QI for the IPin (which increments its reference count)
           If while we are retrieving a pin from the filter an error occurs we
           assume that our internal state is stale with respect to the filter
           (for example someone has deleted a pin) so we
           return VFW_E_ENUM_OUT_OF_SYNC }

        while RealPins > 0 do
        begin
          // Get the next pin object from the filter */
          inc(FPosition);
          Pin := FFilter.GetPin(FPosition-1);
          if Pin = nil then
          begin
            // If this happend, and it's not the first time through, then we've got a problem,
            // since we should really go back and release the iPins, which we have previously
            // AddRef'ed.
            ASSERT(Fetched = 0);
            result := VFW_E_ENUM_OUT_OF_SYNC;
            exit;
          end;

          // We only want to return this pin, if it is not in our cache
          if FPinCache.IndexOf(Pin) = -1 then
          begin
            // From the object get an IPin interface
            TPointerDynArray(@ppPins)[Fetched] := nil; // <<<<<< THIS IS WHERE THE RANGE CHECK ERROR OCCURS.
            TIPinDynArray(@ppPins)[Fetched] := Pin;
            inc(Fetched);
            FPinCache.Add(Pin);
            dec(RealPins);
          end;
        end; // while RealPins > 0 do

        if (pcFetched <> nil) then pcFetched^ := Fetched;

        if (cPins = Fetched) then result := NOERROR else result := S_FALSE;
    except
        On E: Exception do
        begin
            OutputDebugString(PChar(
                '(TBCEnumPins.Next) Exception class name(' + E.ClassName + ') message: ' + E.Message
            ));

            raise;
        end;
    end;
end;

ОБНОВЛЕНИЕ: Похоже, что код DSPACK является звуковым с технической точки зрения, нонемного странно с точки зрения идиомы кодирования и структурировано таким образом, что не совместимо с проверкой диапазона.NIL, поступающий через параметр «out» ppPins, отображается в буфер назначения, который вызывающая сторона передает TBCEnumPins.Next () в качестве параметра ppPins.Например, приведенный ниже код взят с этой страницы:

http://tanvon.wordpress.com/2008/09/07/enumerating-the-directshow-filter-pin/

На этой странице приведен следующий код, который взаимодействует с фильтром DirectShow для перечисления выводов фильтра:

IEnumPins * pEP;
pSF->EnumPins(&pEP);
IPin * pOutPin;
while(pEP->Next(1,&pOutPin,0) == S_OK)
{
    PIN_DIRECTION pDir;
    pOutPin->QueryDirection(&pDir);
    if(pDir == PINDIR_OUTPUT)
        break;// success
    pOutPin->Release();
}
pEP->Release();

Сообщая методу Next (), сколько выводов нужно извлечь, код метода TBCEnumPins.Next () с его необычным приведением динамического массива оказывается безопасным, поскольку он копирует в параметр ppPins "out" только столько, сколькокак булавки, как было запрошено в параметре "cPins" функций Next ().Пока вызывающая сторона передает целевой буфер, который может содержать количество пинов, запрошенных в «cPins», все работает нормально (до тех пор, пока проверка диапазона отключена).Обратите внимание, что в этом случае переменная IPin с именем «outPin» является целевым буфером.Ошибка проверки диапазона возникает, если проверка диапазона включена, поскольку Delphi рассматривает NIL как массив нулевой длины.

Ответы [ 2 ]

1 голос
/ 29 октября 2011

Я с подозрением отношусь к вашему объявлению параметра ppPins и приведению к TPointerDynArray и TIPinDynArray.

Вы получите ошибки диапазона, если сделаете вид, что переданный массив является Delphiдинамический массив, когда на самом деле это не так.Динамические массивы Delphi имеют блок памяти, который предшествует первому элементу массива и определяет количество ссылок на массив и его длину.Поскольку этот блок отсутствует в вашем массиве, вынуждает компилятор поверить, что он есть, что приведет к появившейся ошибке.

Я бы сделал это, как показано ниже.Это соответствует объявлениям, используемым в MSDN.Вы должны использовать немного арифметики с указателями, но я считаю, что это легче сделать, поскольку теперь ваш код будет легко соотноситься с любыми примерами C ++, которые вы можете найти в Интернете.

type
  PIPin = ^IPin;

function TBCEnumPins.Next(cPins: ULONG; ppPins: PIPin; pcFetched: PULONG): HRESULT;
begin
  ... 
  // this is the meat of the loop, assign to the output array, and increment
  Pointer(ppPins)^ := nil; // initialise the intf ref to avoid bogus _Release
  ppPins^ := Pin;
  inc(ppPins);
  ...
end;
1 голос
/ 29 октября 2011

IEnumPins::Next метод не должен вызываться с ppPins == NULL.Таким образом, наилучший способ обработки - немедленно вернуться с кодом ошибки E_POINTER.

Возможно, вы захотите проверить код вызывающего абонента, чтобы выяснить, почему у вас на первом месте стоит NULL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...