AMCap issue

While playing around with a camera DirectShow video source filter, AMCap, which is widely used sample and which I believed to be very stable, started crashing in an inner call CDeviceMoniker::IsEqual:

 	devenum.dll!CDeviceMoniker::IsEqual()  + 0x13 bytes
>	AmCap.exe!ChooseDevices(IMoniker * pmVideo=0x003e9a38, IMoniker * pmAudio=0x00000000)  Line 2672 + 0x2a bytes	C++
 	AmCap.exe!ChooseDevices(wchar_t * szVideo=0x0013f5b8, wchar_t * szAudio=0x0013edb0)  Line 2753 + 0x13 bytes	C++
 	AmCap.exe!AppInit(HINSTANCE__ * hInst=0x00400000, HINSTANCE__ * hPrev=0x00000000, int sw=1)  Line 379 + 0x13 bytes	C++
 	AmCap.exe!WinMain(HINSTANCE__ * hInst=0x00400000, HINSTANCE__ * hPrev=0x00000000, char * szCmdLine=0x00161f32, int sw=1)  Line 453 + 0x11 bytes	C++
 	AmCap.exe!__tmainCRTStartup()  Line 578 + 0x35 bytes	C
 	AmCap.exe!WinMainCRTStartup()  Line 403	C
 	kernel32.dll!_BaseProcessStart@4()  + 0x23 bytes

It appeared that while setting a checkmark on proper menu item the code does not check for the moniker tobe not NULL and a NULL IMoniker pointer passed as an argument into IMoniker::IsEqual is not checked inside devenum.dll (which is obviously a bug for an API entry).

To hotfix the problem, it is necessary to add an extra check near line 2650 of amcap.cpp:

    int i;
    for(i = 0; i < NUMELMS(gcap.rgpmVideoMenu); i++)
    {
        if(gcap.rgpmVideoMenu[i] == NULL)
            break;
        // HOTFIX: Avoid calling IMoniker::IsEqual(NULL) due to possible memory access violation
        if(!gcap.pmVideo)
            continue;
        CheckMenuItem(GetMenu(ghwndApp),
            MENU_VDEVICE0 + i,
            (S_OK == gcap.rgpmVideoMenu[i]->IsEqual(gcap.pmVideo)) ? MF_CHECKED : MF_UNCHECKED);
    }

    for(i = 0; i < NUMELMS(gcap.rgpmAudioMenu); i++)
    {
        if(gcap.rgpmAudioMenu[i] == NULL)
            break;
        // HOTFIX: Avoid calling IMoniker::IsEqual(NULL) due to possible memory access violation
        if(!gcap.pmAudio)
            continue;
        CheckMenuItem(GetMenu(ghwndApp), MENU_ADEVICE0 + i,
            (S_OK == gcap.rgpmAudioMenu[i]->IsEqual(gcap.pmAudio)) ? MF_CHECKED : MF_UNCHECKED);
    }

Leave a Reply