步驟 6. 新增 COM 的支援

[與此頁面相關的功能 DirectShow是舊版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayerIMFMediaEngine音訊/視訊擷取 ,而不是 DirectShow。 Microsoft 建議使用舊版 API 的現有程式碼盡可能重寫為使用新的 API。

這是 撰寫轉換篩選教學課程的步驟 6。

最後一個步驟是新增 COM 的支援。

參考計數

您不需要實作 IUnknown::AddRefIUnknown::Release。 所有篩選和釘選類別都衍生自 CUnknown,其會處理參考計數。

QueryInterface

所有篩選和釘選類別都會針對繼承的任何 COM 介面實作 IUnknown::QueryInterface 。 例如,CTransformFilter會透過CBaseFilter) 繼承 IBaseFilter (。 如果您的篩選未公開任何其他介面,您就不需要執行任何其他動作。

若要公開其他介面,請覆寫 CUnknown::NonDelegatingQueryInterface 方法。 例如,假設您的篩選實作名為 IMyCustomInterface 的自訂介面。 若要將此介面公開給用戶端,請執行下列動作:

下列程式碼顯示這些步驟:

CMyFilter : public CBaseFilter, public IMyCustomInterface
{
public:
    DECLARE_IUNKNOWN
    STDMETHODIMP NonDelegatingQueryInterface(REFIID iid, void **ppv);
};
STDMETHODIMP CMyFilter::NonDelegatingQueryInterface(REFIID iid, void **ppv)
{
    if (riid == IID_IMyCustomInterface) {
        return GetInterface(static_cast<IMyCustomInterface*>(this), ppv);
    }
    return CBaseFilter::NonDelegatingQueryInterface(riid,ppv);
}

如需詳細資訊,請參閱 如何實作 IUnknown

建立物件

如果您打算在 DLL 中封裝篩選,並使它可供其他用戶端使用,則必須支援 CoCreateInstance 和其他相關的 COM 函式。 基類庫會實作大部分的專案;您只需要提供有關篩選準則的一些資訊。 本節提供要執行的簡短概觀。 如需詳細資訊,請參閱 如何建立 DirectShow 篩選 DLL

首先,撰寫靜態類別方法,以傳回您篩選的新實例。 您可以將此方法命名為您想要的任何專案,但簽章必須符合下列範例所示的簽章:

CUnknown * WINAPI CRleFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr)
{
    CRleFilter *pFilter = new CRleFilter();
    if (pFilter== NULL) 
    {
        *pHr = E_OUTOFMEMORY;
    }
    return pFilter;
}

接下來,宣告名為 g_Templates 的 CFactoryTemplate類別實例全域陣列。 每個 CFactoryTemplate 類別都包含一個篩選的登錄資訊。 數個篩選準則可以位於單一 DLL 中;只要包含其他 CFactoryTemplate 專案即可。 您也可以宣告其他 COM 物件,例如屬性頁。

static WCHAR g_wszName[] = L"My RLE Encoder";
CFactoryTemplate g_Templates[] = 
{
  { 
    g_wszName,
    &CLSID_RLEFilter,
    CRleFilter::CreateInstance,
    NULL,
    NULL
  }
};

定義名為 g_cTemplates 的全域整數,其值等於 g_Templates 陣列的長度:

int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);  

最後,實作 DLL 註冊函式。 下列範例顯示這些函式的最小實作:

STDAPI DllRegisterServer()
{
    return AMovieDllRegisterServer2( TRUE );
}
STDAPI DllUnregisterServer()
{
    return AMovieDllRegisterServer2( FALSE );
}

篩選登錄專案

上述範例示範如何註冊篩選的 COM CLSID。 對於許多篩選準則而言,這已足夠。 然後,用戶端應該使用 CoCreateInstance 建立篩選,並藉由呼叫 IFilterGraph::AddFilter將它新增至篩選圖形。 不過,在某些情況下,您可能會想要提供登錄中篩選的其他資訊。 這項資訊會執行下列動作:

下列範例會在影片壓縮類別中註冊 RLE 編碼器篩選準則。 如需詳細資訊,請參閱 如何註冊 DirectShow 篩選。 請務必閱讀 註冊篩選的指導方針一節,其中說明篩選註冊的建議做法。

// Declare media type information.
FOURCCMap fccMap = FCC('MRLE'); 
REGPINTYPES sudInputTypes = { &MEDIATYPE_Video, &GUID_NULL };
REGPINTYPES sudOutputTypes = { &MEDIATYPE_Video, (GUID*)&fccMap };

// Declare pin information.
REGFILTERPINS sudPinReg[] = {
    // Input pin.
    { 0, FALSE, // Rendered?
         FALSE, // Output?
         FALSE, // Zero?
         FALSE, // Many?
         0, 0, 
         1, &sudInputTypes  // Media types.
    },
    // Output pin.
    { 0, FALSE, // Rendered?
         TRUE, // Output?
         FALSE, // Zero?
         FALSE, // Many?
         0, 0, 
         1, &sudOutputTypes      // Media types.
    }
};
 
// Declare filter information.
REGFILTER2 rf2FilterReg = {
    1,                // Version number.
    MERIT_DO_NOT_USE, // Merit.
    2,                // Number of pins.
    sudPinReg         // Pointer to pin information.
};

STDAPI DllRegisterServer(void)
{
    HRESULT hr = AMovieDllRegisterServer2(TRUE);
    if (FAILED(hr))
    {
        return hr;
    }
    IFilterMapper2 *pFM2 = NULL;
    hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
            IID_IFilterMapper2, (void **)&pFM2);
    if (SUCCEEDED(hr))
    {
        hr = pFM2->RegisterFilter(
            CLSID_RLEFilter,                // Filter CLSID. 
            g_wszName,                       // Filter name.
            NULL,                            // Device moniker. 
            &CLSID_VideoCompressorCategory,  // Video compressor category.
            g_wszName,                       // Instance data.
            &rf2FilterReg                    // Filter information.
            );
        pFM2->Release();
    }
    return hr;
}

STDAPI DllUnregisterServer()
{
    HRESULT hr = AMovieDllRegisterServer2(FALSE);
    if (FAILED(hr))
    {
        return hr;
    }
    IFilterMapper2 *pFM2 = NULL;
    hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
            IID_IFilterMapper2, (void **)&pFM2);
    if (SUCCEEDED(hr))
    {
        hr = pFM2->UnregisterFilter(&CLSID_VideoCompressorCategory, 
            g_wszName, CLSID_RLEFilter);
        pFM2->Release();
    }
    return hr;
}

此外,篩選不需要封裝在 DLL 內。 在某些情況下,您可能會撰寫專為特定應用程式設計的特製化篩選。 在此情況下,您可以直接在應用程式中編譯篩選類別,並使用 運算子加以建立 new ,如下列範例所示:

#include "MyFilter.h"  // Header file that declares the filter class.
// Compile and link MyFilter.cpp.
int main()
{
    IBaseFilter *pFilter = 0;
    {
        // Scope to hide pF.
        CMyFilter* pF = new MyFilter();
        if (!pF)
        {
            printf("Could not create MyFilter.\n");
            return 1;
        }
        pF->QueryInterface(IID_IBaseFilter, 
            reinterpret_cast<void**>(&pFilter));
    }
    
    /* Now use pFilter as normal. */
    
    pFilter->Release();  // Deletes the filter.
    return 0;
}

智慧型連線

撰寫 DirectShow 篩選

撰寫轉換篩選