{"id":777,"date":"2009-01-28T22:46:01","date_gmt":"2009-01-28T20:46:01","guid":{"rendered":"https:\/\/alax.info\/blog\/?p=777"},"modified":"2013-07-08T22:44:00","modified_gmt":"2013-07-08T20:44:00","slug":"directshow-filtergraphspy","status":"publish","type":"post","link":"https:\/\/alax.info\/blog\/777","title":{"rendered":"DirectShow Filter Graph Spy"},"content":{"rendered":"<p>Inspired by <a href=\"http:\/\/social.msdn.microsoft.com\/Forums\/en-US\/windowsdirectshowdevelopment\/thread\/4a448884-9062-4e45-b73e-2c9ab681ade0\">a post on DirectShow Development Forum<\/a>, it is sometimes useful to find out how certain application is using DirectShow to capture live video and\/or audio, or play certain file etc. Filter graph topology, details about media types on the pins, used interfaces and call order. There is quite an easy step for the developer to spy over running filter graphs system wide by connecting <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms680573.aspx\">Component Object Model<\/a> (COM) &#8220;Treat As&#8221; feature with filter graph remoting through <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms684004(VS.85).aspx\">Running Object Table<\/a> and connecting to remote process graphs <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd390650(VS.85).aspx\">using GraphEdit<\/a> utility. Who could imagine Windows Media Player to build such a filter graph for a video and audio Windows Media file? SRS Wow DMO, equalizer, analyzer and stuff? <a href=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2009\/01\/Image006.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2009\/01\/Image006-800x284.png\" alt=\"\" title=\"Windows Media Player DirectShow Filter Graph\" width=\"620\" height=\"220\" class=\"aligncenter size-large wp-image-1266\" srcset=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2009\/01\/Image006-800x284.png 800w, https:\/\/alax.info\/blog\/wp-content\/uploads\/2009\/01\/Image006-320x113.png 320w, https:\/\/alax.info\/blog\/wp-content\/uploads\/2009\/01\/Image006.png 1067w\" sizes=\"auto, (max-width: 620px) 100vw, 620px\" \/><\/a> <!--more--> There are many tricks to catch the created filter graph but the most legit is the mentioned above &#8220;Treat As&#8221; feature to emulate certain COM class identifier (CLSID) through another coclass. COM API offers<\/p>\n<p><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms693452(VS.85).aspx\">CoTreatAsClass<\/a> and <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms694494(VS.85).aspx\">CoGetTreatAsClass<\/a> functions to manage the emulation. To emulate DirectShow&#8217;s <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms783670(VS.85).aspx\">CLSID_FilterGraph<\/a> through my CSpy class, I am adding the following code to the default COM registration process for the utility DLL:<\/p>\n<pre><code>static HRESULT WINAPI UpdateRegistry(BOOL bRegister) throw()\n{\n    _Z2(atlTraceRegistrar, 2, _T(\"bRegister %d\\n\"), bRegister);\n    _ATLTRY\n    {\n        CLSID TreatAsClassIdentifier;\n        HRESULT nCoGetTreatAsClassResult = CoGetTreatAsClass(CLSID_FilterGraph, &amp;TreatAsClassIdentifier);\n        __C(nCoGetTreatAsClassResult);\n        __D(!bRegister || nCoGetTreatAsClassResult != S_OK || TreatAsClassIdentifier == GetObjectCLSID(), E_UNNAMED);\n        if(!bRegister &amp;&amp; nCoGetTreatAsClassResult == S_OK)\n            __C(CoTreatAsClass(CLSID_FilterGraph, CLSID_NULL));\n        _A(_pAtlModule);\n        UpdateRegistryFromResource&lt;CSpy&gt;(bRegister);\n        if(bRegister)\n            __C(CoTreatAsClass(CLSID_FilterGraph, GetObjectCLSID()));\n    }\n    _ATLCATCH(Exception)\n    {\n        _C(Exception);\n    }\n    return S_OK;\n}\n<\/code><\/pre>\n<p>The idea is that the emulation is enabled in case it was not enabled before or it was already enabled through our coclass. Every time an application cocreates an instance of Filter Graph Manager (FGM), CSpy class will be created instead. Still as we have no plans to reinvent one of the most important DirectShow objects, we have to embed the original FGM insde to forward the calls to. The proper place in the ATL C++ code is FinalConstruct and what we actually do there is instantiating an aggregated FGM from the original in-process server. The safest would be to query registry for the original host, but we know it&#8217;s hosted by quartz.dll and it seems to be safe to hardcode:<\/p>\n<pre><code>HINSTANCE hModule = CoLoadLibrary(_T(\"quartz.dll\"), TRUE);\n_ATLTRY\n{\n    typedef HRESULT (WINAPI *DLLGETCLASSOBJECT)(REFCLSID, REFIID, VOID**);\n    DLLGETCLASSOBJECT DllGetClassObject = (DLLGETCLASSOBJECT) GetProcAddress(hModule, \"DllGetClassObject\");\n    __E(DllGetClassObject);\n    CComPtr&lt;IClassFactory&gt; pClassFactory;\n    __C(DllGetClassObject(CLSID_FilterGraph, __uuidof(IClassFactory), (VOID**) &amp;pClassFactory));\n    _A(pClassFactory);\n    CComPtr&lt;IUnknown&gt; pUnknown;\n    __C(pClassFactory-&gt;CreateInstance(GetControllingUnknown(), __uuidof(IUnknown), (VOID**) &amp;pUnknown));\n    CComQIPtr&lt;IFilterGraph2&gt; pFilterGraph2 = pUnknown;\n    __D(pFilterGraph2, E_NOINTERFACE);\n<\/code><\/pre>\n<p>What is remained at the point is to implement IFilterGraph2 (along with inherited IGraphBuilder and IFilterGraph) and forward all incoming calls to the inner aggregated object. It would also be helpful to trace calls to a log file C:\\FilterGraphSpy.log:<\/p>\n<pre>2009-01-28 22:07:51    4456    5656    dllmain.h(29): CFilterGraphSpyModule::CFilterGraphSpyModule: this 0x01cd327c\n2009-01-28 22:07:51 4456    5656    Spy.h(75): CSpy::CSpy: this 0x01ce297c\n2009-01-28 22:07:51 4456    5656    Spy.h(132): CSpy::AddFilter: pszName \"C:\\Documents and Settings\\John Doe\\Desktop\\Melissa Cherry Interview.wmv\"\n2009-01-28 22:07:51 4456    5656    Spy.h(142): CSpy::EnumFilters: ...\n2009-01-28 22:07:51 4456    5656    Spy.h(132): CSpy::AddFilter: pszName \"ffdshow Audio Decoder\"\n2009-01-28 22:07:51 4456    5656    Spy.h(152): CSpy::ConnectDirect: ...\n2009-01-28 22:07:51 4456    5656    Spy.h(137): CSpy::RemoveFilter: ...\n2009-01-28 22:07:51 4456    5656    Spy.h(132): CSpy::AddFilter: pszName \"WMAudio Decoder DMO\"\n2009-01-28 22:07:51 4456    5656    Spy.h(152): CSpy::ConnectDirect: ...\n2009-01-28 22:07:51 4456    5656    Spy.h(142): CSpy::EnumFilters: ...<\/pre>\n<p>And additionally, the filter graph is immediately published on the ROT and is accessible with<\/p>\n<p><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd390650(VS.85).aspx\">GraphEdit<\/a>. Feel free to spy over graphs system wide. A partial Visual C++ .NET 2008 source code is <a href=\"http:\/\/code.assembla.com\/roatl-utilities\/subversion\/nodes\/trunk\/FilterGraphSpy\">available from SVN<\/a> (outdated!), release binary <a href=\"https:\/\/www.alax.info\/svn\/public\/trunk\/DirectShowSpy\/_Bin\/DirectShowSpy-Win32.dll\">included<\/a>.<\/p>\n<div style=\"background-color: #efa; border: 1px solid #cc6; padding: 3px;\">\n  To install Filter Graph Spy: <\/p>\n<ul>\n<li>\n      Download the binary DLL from version control repository (<em>DirectShowSpy.dll<\/em> &#8211; <a href=\"https:\/\/www.alax.info\/svn\/public\/trunk\/DirectShowSpy\/_Bin\/DirectShowSpy-Win32.dll\">Win32<\/a>, <a href=\"https:\/\/www.alax.info\/svn\/public\/trunk\/DirectShowSpy\/_Bin\/DirectShowSpy-x64.dll\">x64<\/a>), get also helper .BAT files <a href=\"https:\/\/www.alax.info\/svn\/public\/trunk\/DirectShowSpy\/_Bin\/\">here<\/a>.\n    <\/li>\n<li>\n      COM register the DLL on target system, for example from command line using <em>regsvr32<\/em> utility &#8220;<em>regsvr32 <\/em><em>DirectShowSpy<\/em><em>.dll<\/em>&#8221; (administrative privilege elevation required on Vista)\n    <\/li>\n<li>\n      On Vista\/7 operating systems it is also required to make SDK <em>proppage.dll<\/em> library available and registered in the system, see <a href=\"https:\/\/alax.info\/blog\/944\">Vista related post<\/a> for more details\n    <\/li>\n<\/ul>\n<p> To uninstall: <\/p>\n<ul>\n<li>\n      use &#8220;<em>regsvr32 \/u&#8221;<\/em> utility to unregister the DLL, close all DirectShow based applications or reboot OS, and delete the file; see also a <a href=\"http:\/\/social.msdn.microsoft.com\/Forums\/en-US\/windowsdirectshowdevelopment\/thread\/20c7f4a1-b920-4420-9342-9534e5144769\">related thread on MSDN Forums<\/a>\n    <\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Inspired by a post on DirectShow Development Forum, it is sometimes useful to find out how certain application is using DirectShow to capture live video and\/or audio, or play certain file etc. Filter graph topology, details about media types on the pins, used interfaces and call order. There is quite an easy step for the&hellip; <\/p>\n<p><a class=\"moretag\" href=\"https:\/\/alax.info\/blog\/777\">Read the full article<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,13,2],"tags":[96,487,38,78,119,163,488,164],"class_list":["post-777","post","type-post","status-publish","format-standard","hentry","category-atl","category-source","category-utilities","tag-aggregation","tag-atl","tag-c","tag-directshow","tag-filter","tag-graph","tag-source","tag-spy"],"_links":{"self":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/777","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/comments?post=777"}],"version-history":[{"count":0,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/777\/revisions"}],"wp:attachment":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/media?parent=777"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/categories?post=777"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/tags?post=777"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}