如何创建Filter的属性页

来源:互联网 发布:破解手游用什么软件 编辑:程序博客网 时间:2024/05/16 18:59

http://blog.csdn.net/believefym/article/details/1796611

Creating a Filter Property Page
如何建立filter属性页。
This section describes how to create a property page for a custom DirectShow filter, using theCBasePropertyPage class. The example code in this section shows all the steps needed to create a property page. The example shows a property page for a hypothetical video effect filter that supports a saturation property. The property page has a slider, which the user can move to adjust the filter's saturation level.
下面通过一个例子说明属性页的建立方法
This section contains the following topics:
  • Step 1. Define a Mechanism for Setting the Property
  • Step 2. Implement ISpecifyPropertyPages
  • Step 3. Support QueryInterface
  • Step 4. Create the Property Page
  • Step 5. Store a Pointer to the Filter
  • Step 6. Initialize the Dialog
  • Step 7. Handle Window Messages
  • Step 8. Apply Property Changes
  • Step 9. Disconnect the Property Page
  • Step 10. Support COM Registration
Step 1. Define a Mechanism for Setting the Property
1.定义设置属性的机制
The filter must support a way for the property page to communicate with it, so that the property page can set and retrieve properties on the filter. Possible mechanisms include the following:
  • Expose a custom COM interface.
  • Support Automation properties, through IDispatch.
  • Expose the IPropertyBag interface and define a set of named properties.
This example uses a custom COM interface, named ISaturation. This is not an actual DirectShow interface; it is defined only for this example. Start by declaring the interface in a header file, along with the interface identifier (IID):
这里以实现COM接口为例将此过程:
// 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;
};
You can also define the interface with IDL and use the MIDL compiler to create the header file. Next, implement the custom interface in the filter. This example uses "Get" and "Set" methods for the filter's saturation value. Notice that both methods protect the m_lSaturation member with a critical section.
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)
    {
        if (lSat < SATURATION_MIN || lSat > SATURATION_MAX)
        {
            return E_INVALIDARG;
        }
        CAutoLock lock(&m_csShared);
        m_lSaturation = lSat;
        return S_OK;
    }
};
Of course, the details of your own implementation will differ from the example shown here.
Step 2. Implement IspecifyPropertyPages
2.
实现IspecifyPropertyPages
Next, implement the ISpecifyPropertyPages interface in your filter. This interface has a single method, GetPages, which returns an array of CLSIDs for the property pages that the filter supports. In this example, the filter has a single property page. Start by generating the CLSID and declaring it in your header file:
在自己的filter类中实现IspecifyPropertyPages接口,该接口只有一个方法GetPages,返回filter支持的所有属性页的CLSID数组。首先应该在头文件中声明属性页的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);
Now implement the GetPages method: 
接着实现GetPages方法:
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;
    }
};
 
/* ... */
 
}
Allocate memory for the array using CoTaskMemAlloc. The caller will release the memory.
Step 3. Support QueryInterface
3.
支持QueryInterface
To expose the filter's new interfaces to clients, do the following:  
可以通过下面的步骤暴露新的接口:
  • Include the DECLARE_IUNKNOWN macro in the public declaration section of your filter:
         public:
                         DECLARE_IUNKNOWN;
·                Override CUnknown::NonDelegatingQueryInterface to check for the IIDs of the two interfaces: 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<IYuvGray*>(this), ppv);
    }
    return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
}
Step 4. Create the Property Page
4.
创建属性页
At this point the filter supports everything that it needs for a property page. The next step is implementing the property page itself. Start by deriving a new class from CBasePropertyPage. The following example shows part of the declaration, including some private member variables that will be used later in the example: 
到此已经完成了filter属性页所需的所有支持,接下来要正式实现属性页了。首先从CbasePropertyPage继承一个新类。
class CGrayProp : public CBasePropertyPage
{
private:
    ISaturation *m_pGray;    // Pointer to the filter's custom interface.
    long        m_lVal       // Store the old value, so we can revert.
    long        m_lNewVal;   // New value.
public:
    /* ... */
};
Next, create a dialog resource in the resource editor, along with a string resource for the dialog title. The string will appear in the tab for the property page. The two resource IDs are arguments to theCBasePropertyPage constructor:
接着,创建相应的对话框资源和字符串资源,其中字符串将出现在属性页的tab中。这两个资源的ID就是CbasePropertyPage构造函数的参数:
CGrayProp::CGrayProp(IUnknown *pUnk) : 
  CBasePropertyPage(NAME("GrayProp"), pUnk, IDD_PROPPAGE, IDS_PROPPAGE_TITLE),
 m_pGray(0)
{ }
The following illustration shows the dialog resource for the example property page.
Now you are ready to implement the property page. Here are the methods in CBasePropertyPage to override:
  • OnConnect is called when the client creates the property page. It sets the IUnknown pointer to the filter.
  • OnActivate is called when the dialog is created.
  • OnReceiveMessage is called when the dialog receives a window message.
  • OnApplyChanges is called when the user commits the property changes by clicking the OK or Apply button.
  • OnDisconnect is called when the user dismisses the property sheet.
The remainder of this tutorial describes each of these methods.
Step 5. Store a Pointer to the Filter
5.
保存指向Filter的指针。
Override the CBasePropertyPage::OnConnect method to store a pointer to the filter. The following example queries the pUnk parameter for the filter's custom ISaturation interface:
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));
}
Step 6. Initialize the Dialog
6.
初始化对话框
Override the CBasePropertyPage::OnActivate通过CBasePropertyPage::OnActivate方法初始化对话框。method to initialize the dialog. In this example, the property page uses a slider control, so the first step in OnActivate is to initialize the common control library. The method then initializes the slider control using the current value of the filter's saturation property:
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;
}
注:这里的SendDlgItemMessage是一个消息传递函数,与之类似的还有SendMessage,用于控制相应的控件,传递消息。
Step 7. Handle Window Messages
7.
处理window消息
Override the CBasePropertyPage::OnReceiveMessage method to update the dialog controls in response to user input. If you don't handle a particular message, call the OnReceiveMessage method on the parent class. Whenever the user changes a property, do the following:
  • Set the m_bDirty variable of the property page to TRUE.
  • Call the IPropertyPageSite::OnStatusChange method of the property frame with the PROPPAGESTATUS_DIRTY flag. This flag informs the property frame that it should enable the Apply button. The CBasePropertyPage::m_pPageSite member holds a pointer to the IPropertyPageSite interface.
To simplify this step, you can add the following helper function to your property page:
private:
    void SetDirty()
    {
        m_bDirty = TRUE;
        if (m_pPageSite)
        {
            m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);
        }
    }
Call this private method inside OnReceiveMessage whenever a user action changes a property, as shown in the following example:
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);
} 
The property page in this example has two controls, a slider bar and a Revert to Default button. If the user scrolls the slider bar, the property page sets the saturation value on the filter. If the user clicks the button, the property page restores the filter's default saturation value. In each case, m_lNewVal holds the current value and m_lVal holds the original value. The value of m_lVal is not updated until the user commits the change, which happens when the user clicks the OK or Apply button on the property frame.
Step 8. Apply Property Changes
8.
生效属性的改变
Override the CBasePropertyPage::OnApplyChanges method to commit any property changes. In this example, the m_lNewVal variable is updated whenever the user scrolls the slider bar. The OnApplyChanges method copies this value into the m_lVal variable, overwriting the original value:
HRESULT CGrayProp::OnApplyChanges(void)
{
    m_lVal = m_lNewVal;
    return S_OK;
} 
Step 9. Disconnect the Property Page
9.
断开属性页连接
Override the CBasePropertyPage::OnDisconnect在用户cancels时,由于没有"OnCancel"方法,所以必须想办法知道用户是否调用了OnApplyChanges方法,这里采用的方法是使用m_lVal变量。 method to release any interfaces that you obtained in the OnConnect method. Also, if the user dismisses the property sheet without committing the changes, you should restore the original values if they have changed. There is no "OnCancel" method that gets called when the user cancels, so you need to keep track of whether the user has called OnApplyChanges. This example uses the m_lVal variable, described earlier:
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;
}
Step 10. Support COM Registration
10.
支持COM组件注册
The last remaining task is to support COM registration, so that the property frame can create new instances of your property page. Add another CFactoryTemplate entry to the global g_Templates array, which is used to register all of the COM objects in your DLL. Do not include any filter set-up information for the property page.
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
    }
};
If you declare g_cTemplates as shown in the following code, then it automatically has the correct value based on the array size:
int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]);
Also, add a static CreateInstance method to the property page class. You can name the method anything that you prefer, but the signature must match the one shown the following example:
static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr) 
{
    CGrayProp *pNewObject = new CGrayProp(pUnk);
    if (pNewObject == NULL) 
    {
        *pHr = E_OUTOFMEMORY;
    }
    return pNewObject;
} 
To test the property page, register the DLL and then load the filter in GraphEdit. Left-click the filter to view the property page.
原创粉丝点击