创建DirectShow Filter 属性页

本文详细介绍了如何为一个视频filter创建一个支持饱和度调整的属性页。包括设置属性机理、实现接口、创建对话框资源及处理用户输入等多个步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

创建filter 属性页


本篇文档我们将要讲述如何给一个filter 创建一个属性页,通过CBasePropertyPage 基类。

这篇文档的实例代码演示了创建属性页的步骤,这里我们假设我们要创建属性页的视频filter
支持饱和度属性页,这个属性页有一个滑动条,用户可以通过这个滑动条来控制饱和度。

 

第一步,设置属性的机理
Filter 必须支持一种和属性页沟通的方式,通过属性页可以设置或者获取filter 的属性,
下面是可能的三种方式
1 暴露一个接口
2 通过IDispatch 支持自动化属性
3 暴露IPropertyBag 接口,并定义一系列的属性
下面的例子利用了一个普通的COM 接口,叫做ISaturaton,这并不是一个真正的com 接口,
只是我们用来在这里举例的,你也可以自己定义任何的com 对象。

首先我们在一个头文件中声明接口的ID 和定义。

 

// Always create new GUIDs! Never copy a GUID from an example.
DEFINE_GUID(IID_ISaturation, 0x19412d6e, 0x6401,
0x475c, 0xb0, 0x48, 0x7a, 0xd2, 0x96, 0xe1, 0x6a, 0x19);

interface ISaturation : public IUnknown
{
     STDMETHOD(GetSaturation)(long *plSat) = 0;
     STDMETHOD(SetSaturation)(long lSat) = 0;
}


你也可以用IDL 定义接口,并用MIDL 编译器创建头文件,然后在Filter 上实现这个接口,
这个例子采用“Get”,“Set”方法来设置饱和度的值,注意,修改这个m_lSaturation 的值的

时候一定要进行保护


class CGrayFilter : public ISaturation, /* Other inherited classes. */
{
private:
    CCritSec m_csShared; // Protects shared data.
    long m_lSaturation; // Saturation level.
public:
    STDMETHODIMP GetSaturation(long *plSat)
    {
        if (!plSat) return E_POINTER;  
       CAutoLock lock(&m_csShared);
       *plSat = m_lSaturation;
       return S_OK;
    }


    STDMETHODIMP SetSaturation(long lSat)
   {
        CAutoLock lock(&m_csShared);
        if (lSat < SATURATION_MIN || lSat > SATURATION_MAX)
       {
            return E_INVALIDARG;
       } 
       m_lSaturation = lSat;
       return S_OK;
   }
};

当然你实现接口的一些细节可能和上面的代码不一致。反正你自己实现就是了


第二步,实现ISpecifyPropertyPages 接口
做完了上一步,下面就要在你个filter 中实现ISpecifyPropertyPages 接口,这个接口只有一
个方法,GetPages,这个方法返回filter 所支持的所有的属性页的CLSID。在这个例子里,Filter
只支持一个属性页,
首先产生一个CLSID,并在头文件声明

 

// Always create new GUIDs! Never copy a GUID from an example. DEFINE_GUID(CLSID_SaturationProp, 0xa9bd4eb, 0xded5, 0x4df0, 0xba, 0xf6, 0x2c, 0xea, 0x23, 0xf5, 0x72, 0x61);

 

然后要实现ISpecifyPropertyPages 接口的GetPages 方法:


Microsoft DirectX DirectShow Step 2. Implement ISpecifyPropertyPages #PopUp{cursor: hand; color: green; text-decoration: underline;} #idGlossary{cursor: hand; color: green; text-decoration: underline;}
class CGrayFilter : public ISaturation,
                    public ISpecifyPropertyPages, 
                    /* Other inherited classes. */
{
public:
    STDMETHODIMP GetPages(CAUUID *pPages)
    {
        if (pPages == NULL) return E_POINTER;
        pPages->cElems = 1;
        pPages->pElems = (GUID*)CoTaskMemAlloc(sizeof(GUID));
        if (pPages->pElems == NULL) 
        {
            return E_OUTOFMEMORY;
        }
        pPages->pElems[0] = CLSID_SaturationProp;
        return S_OK;
    }
};

/* ... */

}






第三步,支持QueryInterface
为了暴露Filter 的接口,照着下面的步骤作哦

1 在你的filter 中包含DECLARE_IUNKNOWN 宏的声明:


Public:
    DECLARE_IUNKNOWN;

 

2 重载CUnknown::NonDelegatingQueryInterface 方法来检查两个接口的IIDs。


STDMETHODIMP CGrayFilter::NonDelegatingQueryInterface(REFIID riid,  void **ppv)
{
if (riid == IID_ISpecifyPropertyPages)
{
return GetInterface(static_cast<ISpecifyPropertyPages*>(this),ppv);
}

if (riid == IID_ISaturation) {
return GetInterface(static_cast<ISaturation*>(this), ppv);
}

return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
}

第四步,创建属性页

到这一步,filter 已经支持一个属性页的所需要的东西了,下一步就是要实现属性页本身了。
首先创建一个对话框的资源,然后以这个对话的资源声明一个类,要从CBasePropertyPage.
派生,

下面的代码显示了部分的声明,包含了我们在后面将要用到的部分变量。


class CGrayProp : public CBasePropertyPage {

private:

      // Pointer to the filter's custom interface.

    ISaturation *m_pGray;

    // Store the old value, so we can revert.  

    long m_lVal;

      // New value.

    long m_lNewVal; 

public:

/*... */

};

 

看看构造函数吧


Microsoft DirectX DirectShow Step 4. Create the Property Page #PopUp{cursor: hand; color: green; text-decoration: underline;} #idGlossary{cursor: hand; color: green; text-decoration: underline;}
CGrayProp::CGrayProp(IUnknown *pUnk) : 
  CBasePropertyPage(NAME("GrayProp"), pUnk, IDD_PROPPAGE, IDS_PROPPAGE_TITLE),
  m_pGray(0)
{ }



下面,你还要记得重载CBasePropertyPage 的几个方法哦
OnConnect,当属性页创建的时候,会调用这个方法,通过这个方法将IUnknown 指针付给
Filter。
OnActivate 当对话框创建的时候被调用
OnReceiveMessage 当对话框接收到窗口消息时被调用
OnApplyChanges 当用户单击OK 或者Apply 按钮来确认对属性进行更新时,调用

OnDisconnect 当用户取消Property sheet 时调用


第五步,保存filter 的一个指针
通过重载CBasePropertyPage::OnConnect 方法将一个指针保存到filter,下面的例子演示

了如何通过方法传递过来的参数查询filter 支持的接口


HRESULT CGrayProp::OnConnect(IUnknown *pUnk)

{

    if (pUnk == NULL)

    {

        return E_POINTER;

    }

    ASSERT(m_pGray == NULL);

    return pUnk->QueryInterface(IID_ISaturation, reinterpret_cast<void**>(&m_pGray));

}


第六步,初始化对话框
通过重载CBasePropertyPage::OnActivate 方法来初始化一个对话框,在这个例子里,属性

页使用了滑动条,所以,在初始化的第一步就是要初始化控件动态库,然后再初始化slider。


Microsoft DirectX DirectShow Step 6. Initialize the Dialog #PopUp{cursor: hand; color: green; text-decoration: underline;} #idGlossary{cursor: hand; color: green; text-decoration: underline;}
HRESULT CGrayProp::OnActivate(void)
{
    INITCOMMONCONTROLSEX icc;
    icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icc.dwICC = ICC_BAR_CLASSES;
    if (InitCommonControlsEx(&icc) == FALSE)
    {
        return E_FAIL;
    }

    ASSERT(m_pGray != NULL);
    HRESULT hr = m_pGray->GetSaturation(&m_lVal);
    if (SUCCEEDED(hr))
    {
        SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETRANGE, 0,
            MAKELONG(SATURATION_MIN, SATURATION_MAX));

        SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETTICFREQ, 
            (SATURATION_MAX - SATURATION_MIN) / 10, 0);

        SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETPOS, 1, m_lVal);
    }
    return hr;
}



第七步,处理窗口消息
重载CBasePropertyPage::OnReceiveMessage 方法来处理用户的输入等消息。如果你不想
处理消息,你只需简单调用父类的OnReceiveMessage 即可。
无论何时用户改变了属性,都会做下面的事情
1 将属性页的m_bDirty 设置为TRUE;
2 调用属性框的IPropertyPageSite::OnStatusChange 方法, 并传递一个
PROPPAGESTATUS_DIRTY,这个标志用来通知property frame 应该将Apply 按钮可用,
CBasePropertyPage::m_pPageSite 变量保存着一个IPropertyPageSite 接口

为了简化步骤,你可以在你的属性页中添加下面的代码


Microsoft DirectX DirectShow Step 7. Handle Window Messages #PopUp{cursor: hand; color: green; text-decoration: underline;} #idGlossary{cursor: hand; color: green; text-decoration: underline;}
private:
    void SetDirty()
    {
        m_bDirty = TRUE;
        if (m_pPageSite)
        {
            m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);
        }
    }

 

当用户改变了属性的时候,在OnReceiveMessage 方法中调用上面的函数。


Microsoft DirectX DirectShow Step 7. Handle Window Messages #PopUp{cursor: hand; color: green; text-decoration: underline;} #idGlossary{cursor: hand; color: green; text-decoration: underline;}
BOOL CGrayProp::OnReceiveMessage(HWND hwnd,
    UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_COMMAND:
        if (LOWORD(wParam) == IDC_DEFAULT)
        {
            // User clicked the 'Revert to Default' button.
            m_lNewVal = SATURATION_DEFAULT;
            m_pGray->SetSaturation(m_lNewVal);

            // Update the slider control.
            SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETPOS, 1,
                m_lNewVal);
            SetDirty();
            return (LRESULT) 1;
        }
        break;

    case WM_HSCROLL:
        {
            // User moved the slider.
            switch(LOWORD(wParam))
            {
            case TB_PAGEDOWN:
            case SB_THUMBTRACK:
            case TB_PAGEUP:
                m_lNewVal = SendDlgItemMessage(m_Dlg, IDC_SLIDER1,
                    TBM_GETPOS, 0, 0);
                m_pGray->SetSaturation(m_lNewVal);
                SetDirty();
            }
            return (LRESULT) 1;
        }
    } // Switch.
    
    // Let the parent class handle the message.
    return CBasePropertyPage::OnReceiveMessage(hwnd,uMsg,wParam,lParam);
} 



第八步,处理属性的改变
重载CBasePropertyPage::OnApplyChanges 方法来提交属性页的改变,如果用户单击了确

定,或者应用按钮,OnApplyChanges 方法都会调用到


Microsoft DirectX DirectShow Step 8. Apply Property Changes #PopUp{cursor: hand; color: green; text-decoration: underline;} #idGlossary{cursor: hand; color: green; text-decoration: underline;}
HRESULT CGrayProp::OnApplyChanges(void)
{
    m_lVal = m_lNewVal;
    return S_OK;
} 



第九步,断开属性页连接
重载CBasePropertyPage::OnDisconnect 方法来释放你在OnConnect 方法中请求的所有的
接口,如果用户没有更新属性,而是单击了取消按钮,你还要将属性的原始值保存下来。当
用户单击取消按钮,但是没有相应的响应这个消息的方法,所以,你要检查用户是否调用了

OnApplyChanges 方法,看看例子也好:


Microsoft DirectX DirectShow Step 9. Disconnect the Property Page #PopUp{cursor: hand; color: green; text-decoration: underline;} #idGlossary{cursor: hand; color: green; text-decoration: underline;}
HRESULT CGrayProp::OnDisconnect(void)
{
    if (m_pGray)
    {
        // If the user clicked OK, m_lVal holds the new value.
        // Otherwise, if the user clicked Cancel, m_lVal is the old value.
        m_pGray->SetSaturation(m_lVal);  
        m_pGray->Release();
        m_pGray = NULL;
    }
    return S_OK;
}



第十步,支持com 的注册
最后一步就是要支持com 的注册,因此 属性框才能够创建你属性页的实例,首先在全局数
组g_Templates 添加一个类厂模板的说明。这个全局的数组是你的DLL 中创建的所有的com

对象都要用到的。


Microsoft DirectX DirectShow Step 10. Support COM Registration #PopUp{cursor: hand; color: green; text-decoration: underline;} #idGlossary{cursor: hand; color: green; text-decoration: underline;}
const AMOVIESETUP_FILTER FilterSetupData = 
{ 
    /* Not shown ... */
};

CFactoryTemplate g_Templates[] =
{   
    // This entry is for the filter.
    {
        wszName,
        &CLSID_GrayFilter,
        CGrayFilter::CreateInstance,
        NULL,
        &FilterSetupData 
    },
    // This entry is for the property page.
    { 
        L"Saturation Props",
        &CLSID_SaturationProp,
        CGrayProp::CreateInstance, 
        NULL, NULL
    }
};


 

如果你用下面的方式声明全局数组,数组的大小就会自动地得到修改


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

同时,还要在属性页类中添加一个CreateInstance 方法


Microsoft DirectX DirectShow Step 10. Support COM Registration #PopUp{cursor: hand; color: green; text-decoration: underline;} #idGlossary{cursor: hand; color: green; text-decoration: underline;}
static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr) 
{
    CGrayProp *pNewObject = new CGrayProp(pUnk);
    if (pNewObject == NULL) 
    {
        *pHr = E_OUTOFMEMORY;
    }
    return pNewObject;
} 



如果想测试属性页,可以注册DLL,然后将filter 加载到GraphEdit,鼠标右击来查看filter 的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值