在 DirectShow Filter 的 EnumPins 方法(Delphi/DSPACK)中处理 NULL ppPins 参数的正确方法?

发布于 2024-12-13 01:34:28 字数 4597 浏览 1 评论 0原文

我有一个在 Windows XP/32 机器上使用 DSPACK DirectShow 组件库用 Delphi 6 编写的自定义推送源过滤器。在编码过程中,在我打开范围检查后,我遇到了属于 DSPACK (BaseClass.pas) 的 BaseClass 单元中发生范围检查错误的问题。 em> 在我的应用程序的编译器选项中。

该错误发生在我的 EnumPins() 方法期间。请注意,此方法驻留在 DSPACK 的 BaseClass 单元中,而不是我的应用程序中。我跟踪了这​​个问题,发现当我构建的使用我的过滤器的过滤器图表播放过滤器时,就会出现问题。请注意,我的过滤器作为私有未注册过滤器而不是外部 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 代码似乎是合理的,但从编码习惯的角度来看有点奇怪并且其结构方式与范围检查不兼容。通过 ppPins“out”参数传入的 NIL 映射到调用者传递给 TBCEnumPins.Next() 作为 ppPins 参数的目标缓冲区。例如,下面的代码来自此页面:

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

该页面上的以下代码与 DirectShow Filter 交互以枚举过滤器的引脚:

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”参数中Next() 函数“cPins”参数中请求的引脚。只要调用者传递一个可以保存“cPins”中请求的引脚数量的目标缓冲区,一切就可以正常工作(只要范围检查关闭)。请注意,在这种情况下,名为“outPin”的 IPin 变量是目标缓冲区。如果打开范围检查,则会发生范围检查错误,因为 Delphi 将 NIL 视为零长度数组。

I have a custom push source filter written in Delphi 6 using the DSPACK DirectShow component library on a Windows XP/32 machine. During coding I ran into a problem with range check errors occurring in the BaseClass unit belonging to DSPACK (BaseClass.pas), after I turned on Range Checking in my application's Compiler options.

The error is occurring during my the EnumPins() method. Note, this method resides in DSPACK's BaseClass unit, not my application. I traced the problem and discovered it occurs when the Filter graph I build that uses my filter, plays the filter. Note, my filter is being directly incorporated into my application as a private unregistered filter not as an external AX. When DirectShow calls into the base class method TBCEnumPins.Next(), if the ppPins parameter is NIL then the range check error occurs. Since I am not a DirectShow expert, I am not sure what is the correct way to fix this error without disturbing the proper flow of the DirectShow pin enumeration process. If instead it is a true error condition that is not to be ignored, then I need to know what is the correct Exception to throw or HRESULT code to return in this event. Can anyone tell me the proper way to adjust this code for a NIL ppPins parameter? The full method code is below with the line at which the range check error occurs, highlighted:

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;

UPDATE: It appears that the DSPACK code is sound from a technical standpoint, but a little odd from a coding idiom viewpoint and is structured in a way that is not compatible with range checking. The NIL coming in via the ppPins "out" parameter maps to the destination buffer the caller passes to TBCEnumPins.Next() as the ppPins parameter. For example, the code below comes from this page:

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

On that page is the following code that interacts with a DirectShow Filter to enumerate the Filter's pins:

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();

By telling the Next() method how many pins to retrieve, the TBCEnumPins.Next() method code with its unusual Dynamic array cast works out to be safe since it will only copy into the ppPins "out" parameter as many as pins as were requested in the Next() functions "cPins" parameter. As long as the caller passes a destination buffer that can hold the number of pins requested in "cPins" everything works fine (as long as range checking is turned off). Note, in this case, the IPin variable named "outPin" is the destination buffer. The range check error occurs if range checking is turned on because Delphi treats the NIL as a zero length array.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

虚拟世界 2024-12-20 01:34:28

我对您对 ppPins 参数的声明以及对 TPointerDynArrayTIPinDynArray 的强制转换表示怀疑。

如果您假装传入的数组是 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;

I'm suspicious of your declaration of the ppPins parameter and your casts to TPointerDynArray and TIPinDynArray.

You will get range errors if you pretend that the array passed in is a Delphi dynamic array when in fact it is not. Delphi dynamic arrays have a block of memory that precedes the first item of the array and identifies the reference count to the array, and it's length. Since this block is missing in your array, tricking the compiler into believing it is there will result in the error you see.

I'd do it as shown below. This matches the declarations used in MSDN. You have to use a bit of pointer arithmetic but I believe it is easier to do this since your code now will be easy to relate to any C++ samples you can find online.

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;
維他命╮ 2024-12-20 01:34:28

IEnumPins::Next 方法不应使用 ppPins == NULL 进行调用。因此,最好的处理方法是立即返回 E_POINTER 错误代码。

您可能想检查调用者的代码以找出为什么您首先有 NULL

IEnumPins::Next method is not supposed to be called with ppPins == NULL. The best way to handle is thus return immediately with E_POINTER error code.

You might want to check caller's code to find out why you have NULL there in first place.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文