Combo Box selection, WM_SETREDRAW and CB_SETCURSEL

Given the combo box initialization code:

m_ComboBox.SetRedraw(FALSE);
m_ComboBox.ResetContent();
for(INT nIndex = 0; nIndex < 3; nIndex++)
    m_ComboBox.AddString(AtlFormatString(_T("Item %d"), nIndex + 1));
m_ComboBox.SetCurSel(1);
m_ComboBox.SetRedraw(TRUE)

How the combo box is going to look like?

Continue reading →

IAMGraphBuilderCallback and Intelligent Connect tuning

There was a question asked about how IAMGraphBuilderCallback interface is used to prevent from particular filter insertion during Intelligent Connect.

First of all, there is sample code at The March Hare‘s website:

The IAMGraphBuilderCallback class can be used to remove problematic dshow filters when building a graph with intelligent connect.  This sample does not provide the UI to blacklist filters but will give you a better head start than the information in the documentation.  See the header file for an example of how to use it in your code.  Add your own code to the SelectedFilter or CreatedFilter methods to remove any unwanted filters.

The idea behind the method can be easily illustrated with a code snippet. Each time Filter Graph Manager considers trying a filter, before its instantiation it calls (if provided) IAMGraphBuilderCallback::SelectedFilter method, where there is an option to reject it (before it even was instantiated using CoCreateInstance).

The code snippet below checks selected filter and instructs to reject it if it is an FFDshow Audio Decoder filter.

// IAMGraphBuilderCallback
STDMETHOD(SelectedFilter)(IMoniker* pMoniker) throw()
{
    ATLTRACE(atlTraceCOM, 4, _T("...\n"));
    _ATLTRY
    {
        ATLASSERT(pMoniker);
        const CStringW sMonikerDisplayName = _FilterGraphHelper::GetMonikerDisplayName(pMoniker, NULL);
        ATLTRACE(atlTraceGeneral, 4, _T("sMonikerDisplayName %ls\n"), sMonikerDisplayName);
        static const LPCTSTR g_pszFfdshowAudioDecoderClassIdentifier = _T("{0F40E1E5-4F79-4988-B1A9-CC98794E6B55}");
        if(sMonikerDisplayName.Find(CStringW(g_pszFfdshowAudioDecoderClassIdentifier)) >= 0)
            return E_FAIL; // Die!
    }
    _ATLCATCH(Exception)
    {
        return Exception;
    }
    return S_OK;
}

Sharing Memory Allocators while at the same time Handling Dynamic Media Type Changes

Sharing memory allocators between input and output pins is an important concept to keep performance of filter graph. Unlike more frequent scenario with different allocators, a filter (referred to as “middle filter” below) which has equal media types on input and output pins has an advantage to avoid memory-to-memory copy operation for every frame processed, by delivering downstream the buffer obtained from an upstream filter. With a high resolution video, at high rate, multiple streams running simultaneously this is the expense one would try to avoid for performance reasons.

Memory allocators are (or can be) shared by well known filters, such as Sample Grabber Filter, Infinite Tee Pin Filter and in-place transformation base filters (CTransInPlaceFilter Class).

Still handling Dynamic Format Changes (not only from video renderer filter) filters that share memory allocators may run into the problem of being notified of media type change. Because allocator are typically owned by another filter (e.g. Video Mixing Renderer Filter) and originally its buffer is queried by an upstream filter, the upstream filter obtains allocated buffer independently from the middle filter that shares memory allocators. If the upstream filter decides to never deliver this buffer, however the buffer has a media type attached (see AM_SAMPLE2_PROPERTIES::pMediaType), there is no way for the middle filter to learn about dynamic format change completed.

As a workaround for handling Format Changes from the Video Renderer, when resolution is not changed and it is only stride which might be extended, middle filter might be checking data size in lActual field and learn about the change from an increase in this value.

To be reliably notified on media type change the middle filter is to take extra measures while sharing the allocator. Instead using raw allocator obtained from one pin on another pin (typically output pin’s allocator to be used on an input pin), middle filter may be using an internal proxy object, which implements IMemAllocator interface and forward calls to internal IMemAllocator, obtained originally. Additionally to that, the proxy can check for attached media types on every buffer taken from the allocator, and once the change is noticed – at the moment upstream filter is requesting the buffer – the proxy has a timely chance to remember the new media type so that in the following IMemInputPin::Receive call this media type can be checked for the case upstream buffer decided to not deliver the buffer with attached media type.

if(IsSharingMemAllocators())
{
    // ...
    ATLASSERT((InputMediaSampleProperties.pMediaType != NULL) ^ !(InputMediaSampleProperties.dwSampleFlags & AM_SAMPLE_TYPECHANGED));
    {
        CRoCriticalSectionLock DataLock(GetDataCriticalSection());
        const CObjectPtr<CProxyMemAllocator>& pInputProxyMemAllocator = m_pInputPin->GetProxyMemAllocatorReference();
        CMediaType pMediaType;
        if(pInputProxyMemAllocator && pInputProxyMemAllocator->GetDynamicallyChangedMediaType(pMediaType, TRUE))
        {
            m_pInputPin->SetMediaType(pMediaType);
            m_pOutputPin->SetMediaType(pMediaType);
            // ...
        }
    }
    if(InputMediaSampleProperties.pMediaType)
    {
        m_pInputPin->SetMediaType(InputMediaSampleProperties.pMediaType);
        m_pOutputPin->SetMediaType(InputMediaSampleProperties.pMediaType);
        // ...
    }
    DeliverMediaSample(pMemInputPin, pInputMediaSample);
}

Microsoft MVP Award in DirectShow/Media Foundation

As an appreciation of outstanding contributions in DShow/Media Foundation technical communities during the past year I was presented with the 2009 Microsoft® MVP Award, which I am very much pleased to receive.

Microsoft Most Valuable Professional Award
// Microsoft Most Valuable Professional Award

In an intention to accompany the post with useful DirectShow related information, here is the link list of most important DirectShow resources on the Internet:

Continue reading →

Ahead Nero’s NeResize DirectShow Filter

Another example of a negligence with a cost of incompatibility and enormous amount of support time. Ahead Nero installs a number of DirectShow filters into $(Program Files)\Common Files\Ahead\DSFilter directory. One of the files is NeResize.ax and it hosts a Nero Resize filter. Let us take a closer look:

CLSID: {30002E0C-C574-481E-A5DE-90AE54A79E10} (note that Nero 8 ships the same buggy stuff with new CLSID of {3D0A27C9-B4D6-487B-AFE4-E3CABD4B81F9} – 11.05.2010)
Merit: 0x00400000 (MERIT_UNLIKELY)
Input Pin’s Media Type: major type GUID_NULL, subtype GUID_NULL
Output Pin’s Media Type: major type GUID_NULL, subtype GUID_NULL

The filter is clearly a video filter:

Ahead Nero Resize Filter's Property Page

So the filter register itself under a merit that allows taking it during Intelligent Connect, it registers using media type wildcard which is clearly widely than the filter can affectively operate with and the most interesting part is: with certain video media types the filter crashes (memory access violation) during pin connection negotiation process. That is, inaccurate filter may be crashing third party software it has nothing to deal with at all.

*** Unhandled Exception
Process: 0x000001d4, Thread: 0x00000ce4, Date: 6/29/2009, Time: 11:20:56 AM, Application: C:\Program Files\...
Module: C:\..., Product Version: 1.7.1.1, File Version: 1.7.1.20014, File Time: 23.06.2009, 19:02
Code: 0xc0000005, Flags: 0x00000000, Address: 0x05fc6c65
Parameters: 0x00000001, 0x15be9030

** Call Stack
NeResize!05fc6c65 DllUnregisterServer +21909 @05fc0000
NeResize!05fc7888 DllUnregisterServer +25016 @05fc0000
NeResize!05fc7204 DllUnregisterServer +23348 @05fc0000

Additionally to that the filter does not allow its insertion in debugging environment, and it seems even with Visual Studio running without a debugging session active. Which means that developer may be unaware of issues until incompatibility comes up at a later stage such as testing, or at production site.

It is not the first Nero filter which is bringing real problems. Basically any user who want to keep his system far from issues while still having Nero installed, needs to do find $(Program Files)\Common Files\Ahead\DSFilter directory and immediately rename it to some ~DSFilter in order to invalidate all Nero filters registration.

A few quotes from Guidelines for Registering Filters:

Avoid specifying MEDIATYPE_None, MEDIASUBTYPE_None, or GUID_NULL in the AMOVIESETUP_MEDIATYPE information for a pin. IFilterMapper2 treats these as wildcards, which can slow the graph-building process.

Nero Resize does specify and obviously slows the system down.

Choose the lowest merit value possible. Here are some guidelines:

Special purpose filter; any filter that is created directly by the application: MERIT_DO_NOT_USE

Nero Resize uses higher value and thus affects proper applications.

Software developers will be safer to prevent from DirectShow Filter Graph Manager considering the buggy filter to be used during Intelligent Connect by implementing IAMGraphBuilderCallback interface.

Windows Shell integration and Windows Live Messenger: things that should have never been done

If you drag a file over Windows Live Messenger‘s My Sharing Folders shell name space item, it would immediately interrupt dragging with an error message box, even if you never planned to drop onto this folder:

Windows Live Messenger

This should definitely be rather implemented a different way. If you ever tried to drag something using slow PC touchpad, you probably have an idea of how annoying such an interruption could be.

Windows Live Messenger was pre-installed. To disable shared folders feature, I wanted to find a proper setting in options, but it did not appear to be easy enough: Help button, Show the menu bar, Tools, Options, Sharing Folders on the left, oops! it was not helpful.

Going another way of finding DLL that hosts the shell extension revealed file named: C:\Program Files\Windows Live\Messenger\fsshext.8.5.1302.1018.dll. Once unregistered, the folder went away from the shell (process restart needed, typically logoff/logon):

C:\>regsvr32 /u "C:\Program Files\Windows Live\Messenger\fsshext.8.5.1302.1018.dll"

RegSetKeySecurity, CRegKey::SetKeySecurity and CSecurityDesc

One thing is worth special mentioning in connection with previous post on DirectShow Filter Graph Spy on Microsoft Vista system: ATL’s CSecurityDesc class caused to waste some time.

CRegKey Key;
ATLENSURE_SUCCEEDED(HRESULT_FROM_WIN32(Key.Open(HKEY_CLASSES_ROOT, pszKeyName, READ_CONTROL | WRITE_OWNER)));
CSecurityDesc AdministratorsOwnerSecurityDescriptor;
AdministratorsOwnerSecurityDescriptor.SetOwner(Sids::Admins());
ATLENSURE_SUCCEEDED(HRESULT_FROM_WIN32(Key.SetKeySecurity(OWNER_SECURITY_INFORMATION, &AdministratorsOwnerSecurityDescriptor)));

The code compiles fine, but on runtime it gives error 87 (ERROR_INVALID_PARAMETER, E_INVALIDARG) in the last line, returned from RegSetKeySecurity API call. My first guess was that ATL’s CSecurityDesc class for some reason prepared wrong descriptor which resulted in rejecting it as an argument. From the first glance it looks (not sure) that this class deals, to some extent, with structures itself rather than using API functions, so it could be that it results in something looking differently from expected by API calls.

Still the problem is in class itself and its cast from CSecurityDesc& to required SECURITY_DESCRIPTOR* type. The class only implements operator to automatically cast to const SECURITY_DESCRIPTOR* type, so the following line would not be passed by compiler:

Key.SetKeySecurity(OWNER_SECURITY_INFORMATION, AdministratorsOwnerSecurityDescriptor)

However &AdministratorsOwnerSecurityDescriptor is another level of indirection and hence SECURITY_DESCRIPTOR** type, which is passed by compiler, but results in indeed invalid argument.

So in order to correctly convert CSecurityDesc& to SECURITY_DESCRIPTOR* it can be done this way:

CRegKey Key;
ATLENSURE_SUCCEEDED(HRESULT_FROM_WIN32(Key.Open(HKEY_CLASSES_ROOT, pszKeyName, READ_CONTROL | WRITE_OWNER)));
CSecurityDesc AdministratorsOwnerSecurityDescriptor;
AdministratorsOwnerSecurityDescriptor.SetOwner(Sids::Admins());
ATLENSURE_SUCCEEDED(HRESULT_FROM_WIN32(Key.SetKeySecurity(OWNER_SECURITY_INFORMATION, const_cast<SECURITY_DESCRIPTOR*>((const SECURITY_DESCRIPTOR*) AdministratorsOwnerSecurityDescriptor))));