Windows XP/32マシンでDSPACKDirectShowコンポーネントライブラリを使用してDelphi6で記述されたカスタムプッシュソースフィルターがあります。コーディング中に、アプリケーションのコンパイラオプションで範囲チェックをオンにした後、DSPACK(BaseClass.pas)に属するBaseClassユニットで範囲チェックエラーが発生するという問題が発生しました。
EnumPins()メソッド中にエラーが発生しています。このメソッドは、私のアプリケーションではなく、DSPACKのBaseClassユニットにあることに注意してください。問題を追跡し、フィルターを使用して作成したフィルターグラフがフィルターを再生するときに発生することを発見しました。私のフィルターは、外部AXとしてではなく、プライベートの未登録フィルターとしてアプリケーションに直接組み込まれていることに注意してください。DirectShowが基本クラスメソッドTBCEnumPins.Next()を呼び出すとき、ppPinsパラメーターがNILの場合その後、範囲チェックエラーが発生します。私はDirectShowの専門家ではないため、DirectShowピン列挙プロセスの適切なフローを妨げることなくこのエラーを修正する正しい方法がわかりません。代わりに、それが無視されるべきではない真のエラー状態である場合、スローする正しい例外またはこのイベントで返されるHRESULTコードが何であるかを知る必要があります。誰かがこのコードをNILppPinsパラメーターに合わせて調整する適切な方法を教えてもらえますか?完全なメソッドコードは、範囲チェックエラーが発生する行とともに以下に強調表示されています。
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は、呼び出し元がppPinsパラメーターとしてTBCEnumPins.Next()に渡す宛先バッファーにマップされます。たとえば、以下のコードはこのページからのものです。
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」パラメーターにコピーするピンの数だけであるため、安全であることがわかります。 Next()関数の「cPins」パラメーターで要求されます。呼び出し元が「cPins」で要求されたピンの数を保持できる宛先バッファを通過する限り、すべてが正常に機能します(範囲チェックがオフになっている限り)。この場合、「outPin」という名前のIPin変数が宛先バッファーであることに注意してください。DelphiはNILを長さゼロの配列として扱うため、範囲チェックがオンになっていると、範囲チェックエラーが発生します。