source: trunk/DirectShowSpy/FilterMapperSpy.h @ 937

Last change on this file since 937 was 545, checked in by roman, 6 years ago

Added previously not included hooks; added IFilterMapper3 hook suppors; template library related updates; added FGT to replace ROT (to access services in particular) - work in progress; fixed class enumeration hook

  • Property svn:keywords set to Id
File size: 16.9 KB
Line 
1////////////////////////////////////////////////////////////
2// Copyright (C) Roman Ryltsov, 2008-2015
3// Created by Roman Ryltsov roman@alax.info, http://alax.info
4//
5// This source code is published to complement DirectShowSpy developer powertoy
6// and demonstrate the internal use of APIs and tricks powering the tool. It is
7// allowed to freely re-use the portions of the code in other projects, commercial
8// or otherwise (provided that you don’t pretend that you wrote the original tool).
9//
10// Please keep in mind that DirectShowSpy is a developer tool, it is strongly recommended
11// that it is not shipped with release grade software. It is allowed to distribute
12// DirectShowSpy if only it is not registered with Windows by default and either
13// used privately, or registered on specific throubleshooting request. The advice applies
14// to hooking methods used by DirectShowSpy in general as well.
15
16#pragma once
17
18#include "rodshow.h"
19#include "fil_data.h" //<..\Samples\multimedia\directshow\misc\mapper\fil_data.h>
20#include "Module_i.h"
21#include "Common.h"
22
23////////////////////////////////////////////////////////////
24// CFilterMapperSpyT
25
26template <typename T, const CLSID* t_pFilterMapperClassIdentifier>
27class ATL_NO_VTABLE CFilterMapperSpyT :
28        public CComObjectRootEx<CComMultiThreadModel>,
29        //public CComCoClass<CFilterMapperSpyT, &CLSID_FilterMapperSpy>,
30        public CTransparentCoClassT<T, t_pFilterMapperClassIdentifier>,
31        public IDispatchImpl<IFilterMapperSpy>,
32        public IAMFilterData,
33        public IFilterMapper,
34        public IFilterMapper3
35{
36        typedef CFilterMapperSpyT<T, t_pFilterMapperClassIdentifier> CFilterMapperSpy;
37
38public:
39        //enum { IDR = IDR_FILTERMAPPERSPY };
40
41//DECLARE_REGISTRY_RESOURCEID(IDR)
42
43DECLARE_PROTECT_FINAL_CONSTRUCT()
44
45DECLARE_GET_CONTROLLING_UNKNOWN()
46
47DECLARE_QI_TRACE(CFilterMapperSpyT)
48
49BEGIN_COM_MAP(CFilterMapperSpyT)
50        COM_INTERFACE_ENTRY(IFilterMapperSpy)
51        COM_INTERFACE_ENTRY(IAMFilterData)
52        COM_INTERFACE_ENTRY(IFilterMapper3)
53        COM_INTERFACE_ENTRY(IFilterMapper2)
54        COM_INTERFACE_ENTRY(IFilterMapper)
55        COM_INTERFACE_ENTRY_AGGREGATE_BLIND(m_pInnerUnknown)
56        //COM_INTERFACE_ENTRY(IDispatch)
57END_COM_MAP()
58
59private:
60        BOOL m_bIsAggregated;
61        HINSTANCE m_hQuartzModule;
62        CComPtr<IUnknown> m_pInnerUnknown;
63        CComPtr<IFilterMapper3> m_pFilterMapper3;
64        CComPtr<IFilterMapper> m_pFilterMapper;
65        CComPtr<IAMFilterData> m_pAmFilterData;
66
67        static VOID Trace(const REGFILTER2* pFilterInformation)
68        {
69                _A(pFilterInformation);
70                _Z4(atlTraceCOM, 4, _T("pFilterInformation { dwVersion %d, dwMerit 0x%08X, cPins2 %d }\n"), pFilterInformation->dwVersion, pFilterInformation->dwMerit, pFilterInformation->cPins2);
71                if(pFilterInformation->dwVersion == 2)
72                        for(ULONG nPinIndex = 0; nPinIndex < pFilterInformation->cPins2; nPinIndex++)
73                        {
74                                const REGFILTERPINS2& Pin = pFilterInformation->rgPins2[nPinIndex];
75                                _Z4(atlTraceCOM, 4, _T("pFilterInformation->rgPins2[%d] { dwFlags 0x%X, cInstances %d, nMediaTypes %d, nMediums %d }\n"), nPinIndex, Pin.dwFlags, Pin.cInstances, Pin.nMediaTypes, Pin.nMediums, Pin.clsPinCategory ? (LPCWSTR) _PersistHelper::StringFromIdentifier(*Pin.clsPinCategory) : L"NULL");
76                                for(UINT nIndex = 0; nIndex < Pin.nMediaTypes; nIndex++)
77                                {
78                                        const REGPINTYPES& Type = Pin.lpMediaType[nIndex];
79                                        _Z4(atlTraceCOM, 4, _T("pFilterInformation->rgPins2[...].lpMediaType[%d] { clsMajorType %ls, clsMinorType %ls }\n"), nIndex, Type.clsMajorType ? (LPCWSTR) _PersistHelper::StringFromIdentifier(*Type.clsMajorType) : L"NULL", Type.clsMinorType ? (LPCWSTR) _PersistHelper::StringFromIdentifier(*Type.clsMinorType) : L"NULL");
80                                }
81                                for(UINT nIndex = 0; nIndex < Pin.nMediums; nIndex++)
82                                {
83                                        const REGPINMEDIUM& Medium = Pin.lpMedium[nIndex];
84                                        _Z4(atlTraceCOM, 4, _T("pFilterInformation->rgPins2[...].lpMedium[%d] { clsMedium %ls, dw1 0x%X, dw2 0x%X }\n"), nIndex, _PersistHelper::StringFromIdentifier(Medium.clsMedium), Medium.dw1, Medium.dw2);
85                                }
86                        }
87        }
88        BOOL IsAggregated() const
89        {
90                return (ULONG) m_dwRef >= 0x00001000;
91        }
92
93public:
94// CFilterMapperSpyT
95        static LPCTSTR GetOriginalLibraryName()
96        {
97                return _T("quartz.dll");
98        }
99        static CString GetObjectFriendlyName()
100        {
101                return _StringHelper::GetLine(T::IDR, 2);
102        }
103        static HRESULT WINAPI UpdateRegistry(BOOL bRegister)
104        {
105                _Z2(atlTraceRegistrar, 2, _T("bRegister %d\n"), bRegister);
106                _ATLTRY
107                {
108                        TreatAsUpdateRegistryFromResource<T>(*t_pFilterMapperClassIdentifier, bRegister);
109                }
110                _ATLCATCH(Exception)
111                {
112                        _C(Exception);
113                }
114                return S_OK;
115        }
116        CFilterMapperSpyT() :
117                m_hQuartzModule(NULL)
118        {
119                _Z4_THIS();
120        }
121        ~CFilterMapperSpyT()
122        {
123                _Z4_THIS();
124        }
125        HRESULT FinalConstruct()
126        {
127                _ATLTRY
128                {
129                        m_bIsAggregated = IsAggregated();
130                        TCHAR pszPath[MAX_PATH] = { 0 };
131                        _W(GetModuleFileName(NULL, pszPath, DIM(pszPath)));
132                        _Z4(atlTraceRefcount, 4, _T("pszPath \"%s\", this 0x%p, m_dwRef %d, m_bIsAggregated %d\n"), pszPath, this, m_dwRef, m_bIsAggregated);
133                        const HINSTANCE hModule = CoLoadLibrary(const_cast<LPOLESTR>((LPCOLESTR) CT2COLE(GetOriginalLibraryName())), TRUE);
134                        _ATLTRY
135                        {
136                                typedef HRESULT (WINAPI *DLLGETCLASSOBJECT)(REFCLSID, REFIID, VOID**);
137                                DLLGETCLASSOBJECT DllGetClassObject = (DLLGETCLASSOBJECT) GetProcAddress(hModule, "DllGetClassObject");
138                                __E(DllGetClassObject);
139                                CComPtr<IClassFactory> pClassFactory;
140                                __C(DllGetClassObject(*t_pFilterMapperClassIdentifier, __uuidof(IClassFactory), (VOID**) &pClassFactory));
141                                _A(pClassFactory);
142                                const CComPtr<IUnknown> pControllingUnknown = GetControllingUnknown();
143                                {
144                                        CComPtr<IUnknown> pUnknown;
145                                        __C(pClassFactory->CreateInstance(pControllingUnknown, __uuidof(IUnknown), (VOID**) &pUnknown));
146                                        const CComQIPtr<IFilterMapper3> pFilterMapper3 = pUnknown;
147                                        __D(pFilterMapper3, E_NOINTERFACE);
148                                        pFilterMapper3.p->Release();
149                                        const CComQIPtr<IFilterMapper> pFilterMapper = pUnknown;
150                                        __D(pFilterMapper, E_NOINTERFACE);
151                                        pFilterMapper.p->Release();
152                                        const CComQIPtr<IAMFilterData> pAmFilterData = pUnknown;
153                                        __D(pAmFilterData, E_NOINTERFACE);
154                                        pAmFilterData.p->Release();
155                                        m_pInnerUnknown = pUnknown;
156                                        m_pFilterMapper3 = pFilterMapper3;
157                                        m_pFilterMapper = pFilterMapper;
158                                        m_pAmFilterData = pAmFilterData;
159                                }
160                        }
161                        _ATLCATCHALL()
162                        {
163                                CoFreeLibrary(hModule);
164                                _ATLRETHROW;
165                        }
166                        _A(!m_hQuartzModule);
167                        m_hQuartzModule = hModule;
168                }
169                _ATLCATCH(Exception)
170                {
171                        _C(Exception);
172                }
173                return S_OK;
174        }
175        VOID FinalRelease()
176        {
177                _Z5(atlTraceRefcount, 5, _T("m_dwRef 0x%X\n"), m_dwRef);
178                CComPtr<IUnknown> pControllingUnknown = GetControllingUnknown();
179                if(m_pFilterMapper3)
180                {
181                        pControllingUnknown.p->AddRef();
182                        m_pFilterMapper3.Release();
183                }
184                if(m_pFilterMapper)
185                {
186                        pControllingUnknown.p->AddRef();
187                        m_pFilterMapper.Release();
188                }
189                if(m_pAmFilterData)
190                {
191                        pControllingUnknown.p->AddRef();
192                        m_pAmFilterData.Release();
193                }
194                _ATLTRY
195                {
196                        m_pInnerUnknown.Release();
197                }
198                _ATLCATCHALL()
199                {
200                        _Z_EXCEPTION();
201                        // NOTE: For some unidentified reason Quartz's FilterGraph may crash during final release, to smooth the effect the exception is silently caught
202                        m_pInnerUnknown.p = NULL;
203                }
204                if(m_hQuartzModule)
205                {
206                        CoFreeLibrary(m_hQuartzModule);
207                        m_hQuartzModule = NULL;
208                }
209        }
210
211// IFilterMapperSpy
212
213// IAMFilterData
214        STDMETHOD(ParseFilterData)(BYTE* pnFilterData, ULONG nFilterDataSize, BYTE** ppFilterInformation) override
215        {
216                _Z4(atlTraceCOM, 4, _T("nFilterDataSize %d\n"), nFilterDataSize);
217                const HRESULT nResult = m_pAmFilterData->ParseFilterData(pnFilterData, nFilterDataSize, ppFilterInformation);
218                if(SUCCEEDED(nResult) && ppFilterInformation && *ppFilterInformation && *((BYTE**) *ppFilterInformation))
219                        _ATLTRY
220                        {
221                                Trace((REGFILTER2*) *((BYTE**) *ppFilterInformation));
222                        }
223                        _ATLCATCHALL()
224                        {
225                                _Z_EXCEPTION();
226                        }
227                return nResult;
228        }
229        STDMETHOD(CreateFilterData)(REGFILTER2* pFilterInformation, BYTE** ppnFilterData, ULONG* pnFilterDataSize) override
230        {
231                _Z4(atlTraceCOM, 4, _T("...\n"));
232                if(pFilterInformation)
233                        _ATLTRY
234                        {
235                                Trace(pFilterInformation);
236                        }
237                        _ATLCATCHALL()
238                        {
239                                _Z_EXCEPTION();
240                        }
241                return m_pAmFilterData->CreateFilterData(pFilterInformation, ppnFilterData, pnFilterDataSize);
242        }
243
244// IFilterMapper3
245        STDMETHOD(GetICreateDevEnum)(ICreateDevEnum** ppEnum) override
246        {
247                _Z4(atlTraceCOM, 4, _T("...\n"));
248                return m_pFilterMapper3->GetICreateDevEnum(ppEnum);
249        }
250
251// IFilterMapper2
252        STDMETHOD(CreateCategory)(REFCLSID CategoryIdentifier, DWORD nMerit, LPCWSTR pszDescription) override
253        {
254                _Z4(atlTraceCOM, 4, _T("CategoryIdentifier %ls, nMerit 0x%08X, pszDescription \"%s\"\n"), _PersistHelper::StringFromIdentifier(CategoryIdentifier), nMerit, CString(pszDescription));
255                return m_pFilterMapper3->CreateCategory(CategoryIdentifier, nMerit, pszDescription);
256        }
257        STDMETHOD(UnregisterFilter)(const CLSID* pCategoryIdentifier, LPCOLESTR pszInstance, REFCLSID FilterClassIdentifier) override
258        {
259                _Z4(atlTraceCOM, 4, _T("pCategoryIdentifier %ls, pszInstance %s, FilterClassIdentifier %ls\n"), pCategoryIdentifier ? (LPCWSTR) _PersistHelper::StringFromIdentifier(*pCategoryIdentifier) : L"NULL", pszInstance ? (LPCTSTR) AtlFormatString(_T("\"%s\""), CString(pszInstance)) : _T("NULL"), _PersistHelper::StringFromIdentifier(FilterClassIdentifier));
260                return m_pFilterMapper3->UnregisterFilter(pCategoryIdentifier, pszInstance, FilterClassIdentifier);
261        }
262        STDMETHOD(RegisterFilter)(REFCLSID FilterClassIdentifier, LPCWSTR pszName, IMoniker** ppMoniker, const CLSID* pCategoryIdentifier, LPCOLESTR pszInstance, const REGFILTER2* pFilterInformation) override
263        {
264                _Z4(atlTraceCOM, 4, _T("FilterClassIdentifier %ls, pszName \"%s\", pCategoryIdentifier %ls, pszInstance %s\n"), _PersistHelper::StringFromIdentifier(FilterClassIdentifier), CString(pszName), pCategoryIdentifier ? (LPCWSTR) _PersistHelper::StringFromIdentifier(*pCategoryIdentifier) : L"NULL", pszInstance ? (LPCTSTR) AtlFormatString(_T("\"%s\""), CString(pszInstance)) : _T("NULL"));
265                if(pFilterInformation)
266                        _ATLTRY
267                        {
268                                Trace(pFilterInformation);
269                        }
270                        _ATLCATCHALL()
271                        {
272                                _Z_EXCEPTION();
273                        }
274                return m_pFilterMapper3->RegisterFilter(FilterClassIdentifier, pszName, ppMoniker, pCategoryIdentifier, pszInstance, pFilterInformation);
275        }
276        STDMETHOD(EnumMatchingFilters)(IEnumMoniker** ppEnumMoniker, DWORD nFlags, BOOL bExactMatch, DWORD nMinimalMerit, BOOL bInputNeeded, DWORD nInputTypeCount, const GUID* pInputTypes, const REGPINMEDIUM* pInputMedium, const CLSID* pInputPinCategory, BOOL bRender, BOOL bOutputNeeded, DWORD nOutputTypeCount, const GUID* pOutputTypes, const REGPINMEDIUM* pOutputMedium, const CLSID* pOutputPinCategory) override
277        {
278                _Z4(atlTraceCOM, 4, _T("nFlags 0x%X, bExactMatch %d, nMinimalMerit 0x%08X, bInputNeeded %d, nInputTypeCount %d, pInputPinCategory %ls, bRender %d, bOutputNeeded %d, nOutputTypeCount %d, pOutputPinCategory %ls\n"), nFlags, bExactMatch, nMinimalMerit, bInputNeeded, nInputTypeCount, pInputPinCategory ? (LPCWSTR) _PersistHelper::StringFromIdentifier(*pInputPinCategory) : L"NULL", bRender, bOutputNeeded, nOutputTypeCount, pOutputPinCategory ? (LPCWSTR) _PersistHelper::StringFromIdentifier(*pOutputPinCategory) : L"NULL");
279                for(DWORD nInputTypeIndex = 0; nInputTypeIndex < nInputTypeCount; nInputTypeIndex++)
280                {
281                        const GUID& MajorType = pInputTypes[2 * nInputTypeIndex + 0];
282                        const GUID& Subtype = pInputTypes[2 * nInputTypeIndex + 1];
283                        _Z4(atlTraceCOM, 4, _T("nInputTypeIndex %d, MajorType %ls, Subtype %ls\n"), nInputTypeIndex, _PersistHelper::StringFromIdentifier(MajorType), _PersistHelper::StringFromIdentifier(Subtype));
284                }
285                if(pInputMedium)
286                        _Z4(atlTraceCOM, 4, _T("pInputMedium { clsMedium %ls, dw1 0x%X, dw2 0x%X }\n"), _PersistHelper::StringFromIdentifier(pInputMedium->clsMedium), pInputMedium->dw1, pInputMedium->dw2);
287                for(DWORD nOutputTypeIndex = 0; nOutputTypeIndex < nOutputTypeCount; nOutputTypeIndex++)
288                {
289                        const GUID& MajorType = pOutputTypes[2 * nOutputTypeIndex + 0];
290                        const GUID& Subtype = pOutputTypes[2 * nOutputTypeIndex + 1];
291                        _Z4(atlTraceCOM, 4, _T("nOutputTypeIndex %d, MajorType %ls, Subtype %ls\n"), nOutputTypeIndex, _PersistHelper::StringFromIdentifier(MajorType), _PersistHelper::StringFromIdentifier(Subtype));
292                }
293                if(pOutputMedium)
294                        _Z4(atlTraceCOM, 4, _T("pOutputMedium { clsMedium %ls, dw1 0x%X, dw2 0x%X }\n"), _PersistHelper::StringFromIdentifier(pOutputMedium->clsMedium), pOutputMedium->dw1, pOutputMedium->dw2);
295                const HRESULT nResult = m_pFilterMapper3->EnumMatchingFilters(ppEnumMoniker, nFlags, bExactMatch, nMinimalMerit, bInputNeeded, nInputTypeCount, pInputTypes, pInputMedium, pInputPinCategory, bRender, bOutputNeeded, nOutputTypeCount, pOutputTypes, pOutputMedium, pOutputPinCategory);
296                if(SUCCEEDED(nResult))
297                        _ATLTRY
298                        {
299                                const CComPtr<IEnumMoniker>& pEnumMoniker = reinterpret_cast<const CComPtr<IEnumMoniker>&>(*ppEnumMoniker);
300                                __C(pEnumMoniker->Reset());
301                                for(; ; )
302                                {
303                                        CComPtr<IMoniker> pMoniker;
304                                        ULONG nElementCount;
305                                        if(pEnumMoniker->Next(1, &pMoniker, &nElementCount) != S_OK)
306                                                break;
307                                        _Z4(atlTraceGeneral, 4, _T("pMoniker %ls\n"), _FilterGraphHelper::GetMonikerDisplayName(pMoniker));
308                                        CComPtr<IBindCtx> pBindCtx;
309                                        __C(CreateBindCtx(0, &pBindCtx));
310                                        CComPtr<IPropertyBag> pPropertyBag;
311                                        __C(pMoniker->BindToStorage(pBindCtx, NULL, __uuidof(IPropertyBag), (VOID**) &pPropertyBag));
312                                        const CStringW sFriendlyName = _FilterGraphHelper::ReadPropertyBagString(pPropertyBag, OLESTR("FriendlyName"));
313                                        const CStringW sDescription = _FilterGraphHelper::ReadPropertyBagString(pPropertyBag, OLESTR("Description"));
314                                        const CStringW sDevicePath = _FilterGraphHelper::ReadPropertyBagString(pPropertyBag, OLESTR("DevicePath"));
315                                        _Z4(atlTraceCOM, 4, _T("sFriendlyName \"%ls\", sDescription \"%ls\", sDevicePath \"%ls\"\n"), sFriendlyName, sDescription, sDevicePath);
316                                }
317                                __C(pEnumMoniker->Reset());
318                        }
319                        _ATLCATCHALL()
320                        {
321                                _Z_EXCEPTION();
322                        }
323                return nResult;
324        }
325
326// IFilterMapper
327        STDMETHOD(RegisterFilter)(CLSID FilterClassIdentifier, LPCWSTR pszName, DWORD nMerit) override
328        {
329                _Z4(atlTraceCOM, 4, _T("...\n"));
330                return m_pFilterMapper->RegisterFilter(FilterClassIdentifier, pszName, nMerit);
331        }
332        STDMETHOD(RegisterFilterInstance)(CLSID FilterClassIdentifier, LPCWSTR pszName, CLSID* pMediaResourceIdentifier) override
333        {
334                _Z4(atlTraceCOM, 4, _T("...\n"));
335                return m_pFilterMapper->RegisterFilterInstance(FilterClassIdentifier, pszName, pMediaResourceIdentifier);
336        }
337        STDMETHOD(RegisterPin)(CLSID Filter, LPCWSTR pszName, BOOL bRendered, BOOL bOutput, BOOL bZero, BOOL bMany, CLSID ConnectsToFilter, LPCWSTR pszConnectsToPin) override
338        {
339                _Z4(atlTraceCOM, 4, _T("...\n"));
340                return m_pFilterMapper->RegisterPin(Filter, pszName, bRendered, bOutput, bZero, bMany, ConnectsToFilter, pszConnectsToPin);
341        }
342        STDMETHOD(RegisterPinType)(CLSID FilterClassIdentifier, LPCWSTR pszName, CLSID MajorType, CLSID Subtype) override
343        {
344                _Z4(atlTraceCOM, 4, _T("...\n"));
345                return m_pFilterMapper->RegisterPinType(FilterClassIdentifier, pszName, MajorType, Subtype);
346        }
347        STDMETHOD(UnregisterFilter)(CLSID FilterClassIdentifier) override
348        {
349                _Z4(atlTraceCOM, 4, _T("...\n"));
350                return m_pFilterMapper->UnregisterFilter(FilterClassIdentifier);
351        }
352        STDMETHOD(UnregisterFilterInstance)(CLSID MediaResourceIdentifier) override
353        {
354                _Z4(atlTraceCOM, 4, _T("...\n"));
355                return m_pFilterMapper->UnregisterFilterInstance(MediaResourceIdentifier);
356        }
357        STDMETHOD(UnregisterPin)(CLSID FilterClassIdentifier, LPCWSTR pszName) override
358        {
359                _Z4(atlTraceCOM, 4, _T("...\n"));
360                return m_pFilterMapper->UnregisterPin(FilterClassIdentifier, pszName);
361        }
362        STDMETHOD(EnumMatchingFilters)(IEnumRegFilters** ppEnum, DWORD nMerit, BOOL bInputNeeded, CLSID clsInMaj, CLSID clsInSub, BOOL bRender, BOOL bOutputNeeded, CLSID clsOutMaj, CLSID clsOutSub) override
363        {
364                _Z4(atlTraceCOM, 4, _T("...\n"));
365                return m_pFilterMapper->EnumMatchingFilters(ppEnum, nMerit, bInputNeeded, clsInMaj, clsInSub, bRender, bOutputNeeded, clsOutMaj, clsOutSub);
366        }
367};
368
369////////////////////////////////////////////////////////////
370// CFilterMapperSpy
371
372class ATL_NO_VTABLE CFilterMapperSpy :
373        public CFilterMapperSpyT<CFilterMapperSpy, &CLSID_FilterMapper2>,
374        public CComCoClass<CFilterMapperSpy, &CLSID_FilterMapperSpy>
375{
376public:
377        enum { IDR = IDR_FILTERMAPPERSPY };
378
379private:
380        static LPCTSTR g_pszClassName;
381
382public:
383        //typedef CBlackListAwareComCreatorT<CComObjectCached<CFilterMapperSpy>, CFilterMapperSpy, &g_pszClassName> _ClassFactoryCreatorClass; // DECLARE_CLASSFACTORY override
384        typedef CComCreator2<CBlackListAwareComCreatorT<CComObject<CFilterMapperSpy>, CFilterMapperSpy, &g_pszClassName>, CBlackListAwareComCreatorT<CComAggObject<CFilterMapperSpy>, CFilterMapperSpy, &g_pszClassName> > _CreatorClass; // DECLARE_AGGREGATABLE override
385
386public:
387// CFilterMapperSpy
388};
389
390__declspec(selectany) LPCTSTR CFilterMapperSpy::g_pszClassName = _T("CFilterMapperSpy");
391
392OBJECT_ENTRY_AUTO(__uuidof(FilterMapperSpy), CFilterMapperSpy)
393
Note: See TracBrowser for help on using the repository browser.