本篇文档我们将要讲述如何给一个filter创建一个属性页,通过CBasePropertyPage基类。这篇文档的实例代码演 示了创建属性页的步骤,这里我们假设我们要创建属性页的视频filter支持饱和度属性页,这个属性页有一个滑动条,用户可以通过这个滑动条来控制饱和 度。
第一步,设置属性的机理
Filter必须支持一种和属性页沟通的方式,通过属性页可以设置或者获取filter的属性,下面是可能的三种方式
1暴露一个接口
2通过IDispatch支持自动化属性
3暴露IPropertyBag 接口,并定义一系列的属性
下面的例子利用了一个普通的COM接口,叫做ISaturaton,这并不是一个真正的com接口,只是我们用来在这里举例的,你也可以自己定义任何的com对象。
首先我们在一个头文件中声明接口的ID和定义。

  1. // Always create new GUIDs! Never copy a GUID from an example.
  2. DEFINE_GUID(IID_ISaturation, 0x19412d6e, 0x6401, 
  3. 0x475c, 0xb0, 0x48, 0x7a, 0xd2, 0x96, 0xe1, 0x6a, 0x19); 
  4. interface ISaturation : public IUnknown
  5. {
  6. STDMETHOD(GetSaturation)(long *plSat) = 0;
  7. STDMETHOD(SetSaturation)(long lSat) = 0;
  8. };

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

  1. class CGrayFilter : public ISaturation, 
  2. {
  3. private:
  4. CCritSec m_csShared; // Protects shared data.
  5. long m_lSaturation; // Saturation level.
  6. public:
  7. STDMETHODIMP GetSaturation(long *plSat)
  8. {
  9. if (!plSat) return E_POINTER;
  10. CAutoLock lock(&m_csShared);
  11. *plSat = m_lSaturation;
  12. return S_OK;
  13. }
  14. STDMETHODIMP SetSaturation(long lSat)
  15. {
  16. CAutoLock lock(&m_csShared);
  17. if (lSat < SATURATION_MIN || lSat > SATURATION_MAX)
  18. {
  19. return E_INVALIDARG;
  20. }
  21. m_lSaturation = lSat;
  22. return S_OK;
  23. }
  24. };

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

第二步,实现ISpecifyPropertyPages接口

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

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

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

  1. class CGrayFilter : public ISaturation,
  2. public ISpecifyPropertyPages, 
  3.  
  4. {
  5. public:
  6. STDMETHODIMP GetPages(CAUUID *pPages)
  7. {
  8. if (pPages == NULL) return E_POINTER;
  9. pPages->cElems = 1;
  10. pPages->pElems = (GUID*)CoTaskMemAlloc(sizeof(GUID));
  11. if (pPages->pElems == NULL) 
  12. {
  13. return E_OUTOFMEMORY;
  14. }
  15. pPages->pElems[0] = CLSID_SaturationProp;
  16. return S_OK;
  17. }
  18. }; 
  19.  
  20. }

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

  1. 1 在你的filter中包含DECLARE_IUNKNOWN宏的声明:
  2. Public
  3. DECLARE_IUNKNOWN;
  4. 2 重载CUnknown::NonDelegatingQueryInterface 方法来检查两个接口的IIDs
  5. STDMETHODIMP CGrayFilter::NonDelegatingQueryInterface(REFIID riid, 
  6. void **ppv)
  7. {
  8. if (riid == IID_ISpecifyPropertyPages)
  9. {
  10. return GetInterface(static_cast<ISpecifyPropertyPages*>(this),
  11. ppv);
  12. }
  13. if (riid == IID_ISaturation)
  14. {
  15. return GetInterface(static_cast<IYuvGray*>(this), ppv);
  16. }
  17. return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
  18. }

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

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

  1. class CGrayProp : public CBasePropertyPage
  2. {
  3. private:
  4. ISaturation *m_pGray; // Pointer to the filter's custom interface.
  5. long m_lVal // Store the old value, so we can revert.
  6. long m_lNewVal; // New value.
  7. public:
  8.  
  9. };

看看构造函数吧

  1. CGrayProp::CGrayProp(IUnknown *pUnk) : 
  2. CBasePropertyPage(NAME("GrayProp"), pUnk, IDD_PROPPAGE, IDS_PROPPAGE_TITLE),
  3. m_pGray(0)
  4. { }

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

第五步,保存filter的一个指针
通过重载CBasePropertyPage::OnConnect方法将一个指针保存到filter,下面的例子演示了如何通过方法传递过来的参数查询filter支持的接口

  1. HRESULT CGrayProp::OnConnect(IUnknown *pUnk)
  2. {
  3. if (pUnk == NULL)
  4. {
  5. return E_POINTER;
  6. }
  7. ASSERT(m_pGray == NULL);
  8. return pUnk->QueryInterface(IID_ISaturation, 
  9. reinterpret_cast<void**>(&m_pGray));
  10. }

第六步,初始化对话框
通过重载CBasePropertyPage::OnActivate方法来初始化一个对话框,在这个例子里,属性页使用了滑动条,所以,在初始化的第一步就是要初始化控件动态库,然后再初始化slider。

  1. HRESULT CGrayProp::OnActivate(void)
  2. {
  3. INITCOMMONCONTROLSEX icc;
  4. icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
  5. icc.dwICC = ICC_BAR_CLASSES;
  6. if (InitCommonControlsEx(&icc) == FALSE)
  7. {
  8. return E_FAIL;
  9. } 
  10. ASSERT(m_pGray != NULL);
  11. HRESULT hr = m_pGray->GetSaturation(&m_lVal);
  12. if (SUCCEEDED(hr))
  13. {
  14. SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETRANGE, 0,
  15. MAKELONG(SATURATION_MIN, SATURATION_MAX)); 
  16. SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETTICFREQ, 
  17. (SATURATION_MAX - SATURATION_MIN) / 10, 0); 
  18. SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETPOS, 1, m_lVal);
  19. }
  20. return hr;
  21. }

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

  1. private:
  2. void SetDirty()
  3. {
  4. m_bDirty = TRUE;
  5. if (m_pPageSite)
  6. {
  7. m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);
  8. }
  9. }

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

  1. BOOL CGrayProp::OnReceiveMessage(HWND hwnd,
  2. UINT uMsg, WPARAM wParam, LPARAM lParam)
  3. {
  4. switch (uMsg)
  5. {
  6. case WM_COMMAND:
  7. if (LOWORD(wParam) == IDC_DEFAULT)
  8. {
  9. // User clicked the 'Revert to Default' button.
  10. m_lNewVal = SATURATION_DEFAULT;
  11. m_pGray->SetSaturation(m_lNewVal); 
  12. // Update the slider control.
  13. SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETPOS, 1,
  14. m_lNewVal);
  15. SetDirty();
  16. return (LRESULT) 1;
  17. }
  18. break; 
  19. case WM_HSCROLL:
  20. {
  21. // User moved the slider.
  22. switch(LOWORD(wParam))
  23. {
  24. case TB_PAGEDOWN:
  25. case SB_THUMBTRACK:
  26. case TB_PAGEUP:
  27. m_lNewVal = SendDlgItemMessage(m_Dlg, IDC_SLIDER1,
  28. TBM_GETPOS, 0, 0);
  29. m_pGray->SetSaturation(m_lNewVal);
  30. SetDirty();
  31. }
  32. return (LRESULT) 1;
  33. }
  34. } // Switch.
  35.  
  36. // Let the parent class handle the message.
  37. return CBasePropertyPage::OnReceiveMessage(hwnd,uMsg,wParam,lParam);
  38. }

第八步,处理属性的改变
重载CBasePropertyPage::OnApplyChanges方法来提交属性页的改变,如果用户单击了确定,或者应用按钮,OnApplyChanges方法都会调用到

  1. HRESULT CGrayProp::OnApplyChanges(void)
  2. {
  3. m_lVal = m_lNewVal;
  4. return S_OK;
  5. }

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

  1. HRESULT CGrayProp::OnDisconnect(void)
  2. {
  3. if (m_pGray)
  4. {
  5. // If the user clicked OK, m_lVal holds the new value.
  6. // Otherwise, if the user clicked Cancel, m_lVal is the old value.
  7. m_pGray->SetSaturation(m_lVal); 
  8. m_pGray->Release();
  9. m_pGray = NULL;
  10. }
  11. return S_OK;
  12. }

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

  1. const AMOVIESETUP_FILTER FilterSetupData = 
  2. { 
  3.  
  4. }; 
  5. CFactoryTemplate g_Templates[] =
  6. { 
  7. // This entry is for the filter.
  8. {
  9. wszName,
  10. &CLSID_GrayFilter,
  11. CGrayFilter::CreateInstance,
  12. NULL,
  13. &FilterSetupData 
  14. },
  15. // This entry is for the property page.
  16. { 
  17. L"Saturation Props",
  18. &CLSID_SaturationProp,
  19. CGrayProp::CreateInstance, 
  20. NULL, NULL
  21. }
  22. };

如果你用下面的方式声明全局数组,数组的大小就会自动地得到修改
int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]);
同时,还要在属性页类中添加一个CreateInstance方法

  1. static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr) 
  2. {
  3. CGrayProp *pNewObject = new CGrayProp(pUnk);
  4. if (pNewObject == NULL) 
  5. {
  6. *pHr = E_OUTOFMEMORY;
  7. }
  8. return pNewObject;
  9. } 
  10.  
  11. 如果想测试属性页,可以注册DLL,然后将filter加载到GraphEdit,鼠标右击来查看filter的属性。 
  12.  
  13. 采集参数的设置

采集前需要对要采集的视频格式、图像质量进行设置,如视频的分辨率、帧率和数据格式,图像的亮度、色度和饱和度参数设置等。下面分别对这两种设置进行讲述。

1.视频格式设置

对采集设备的参数进行配置,首先确定设备的类型:VFW、WDM驱动模型。目前流行的采集设备大部分采用WDM模式,采集设备在 DirectShow中被当做一个滤波器组件,所以配置采集设备就是对该滤波器的引脚进行设置。在确保滤波器链表完全建立前,可以使用如下的过程配置视频 格式。

  1. void CCaptureClass::ConfigCameraPin(HWND hwndParent)
  2. {
  3. HRESULT hr;       //返回值
  4. IAMStreamConfig *pSC;    //流配置接口
  5. ISpecifyPropertyPages *pSpec;  //属性页接口
  6. //只有停止后,才能进行引脚属性的设置
  7. m_pMC->Stop();
  8. //首先查询捕获CAPTURE、视频Video接口
  9. hr = m_pCapture->FindInterface(&PIN_CATEGORY_CAPTURE,
  10. &MEDIATYPE_Video, m_pBF,
  11. IID_IAMStreamConfig, (void **)&pSC);
  12. CAUUID cauuid;       //所有属性页结构体
  13. hr = pSC->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
  14. if(hr == S_OK)
  15. {
  16. hr = pSpec->GetPages(&cauuid);  //获取所有属性页
  17. //显示属性页
  18. hr = OleCreatePropertyFrame(hwndParent, 30, 30, NULL, 1, 
  19. (IUnknown **)&pSC, cauuid.cElems,
  20. (GUID *)cauuid.pElems, 0, 0, NULL);
  21. //释放内存、资源
  22. CoTaskMemFree(cauuid.pElems);
  23. pSpec->Release();
  24. pSC->Release();
  25. }
  26. //回复运行
  27. m_pMC->Run();
  28. }

上述代码首先以视频捕获方式获取流配置接口,在该接口下查询属性页接口,然后获取所有属性页,接着显示获取的属性页,最后释放资源。在显示了属性页后用户就可以对其属性页的对话框进行设置了,运行后的结果如图9-1所示。

 
(点击查看大图)图9-1  视频格式属性设置

2.图像参数设置

图像参数设置类似于视频格式配置,根据设备的驱动类型VFW、WDM使用不同的技术配置设备的参数,这里我们同样假定采集设备的类型为WDM,配置过程如下。

  1. void CCaptureClass::ConfigCameraFilter(HWND hwndParent)
  2. {
  3. HRESULT hr=0;
  4. ISpecifyPropertyPages *pProp;
  5. hr = m_pBF->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pProp);
  6. if (SUCCEEDED(hr)) 
  7. {
  8. //获取滤波器名称和IUnknown接口指针
  9. FILTER_INFO FilterInfo;
  10. hr = m_pBF->QueryFilterInfo(&FilterInfo); 
  11. IUnknown *pFilterUnk;
  12. m_pBF->QueryInterface(IID_IUnknown, (void **)&pFilterUnk);
  13. //显示该页
  14. CAUUID caGUID;
  15. pProp->GetPages(&caGUID);
  16. OleCreatePropertyFrame(
  17. hwndParent,    //父窗口
  18. 0, 0,                 //Reserved
  19. FilterInfo.achName, //对话框标题
  20. 1,                    //该滤波器的目标数目
  21. &pFilterUnk,       //目标指针数组
  22. caGUID.cElems,      //属性页数目
  23. caGUID.pElems,      //属性页的CLSID数组
  24. 0,                     //本地标识
  25. 0, NULL             //Reserved
  26. );
  27. //释放内存、资源
  28. CoTaskMemFree(caGUID.pElems);
  29. pFilterUnk->Release();
  30. FilterInfo.pGraph->Release(); 
  31. pProp->Release();
  32. }
  33. m_pMC->Run();
  34. }

捕获滤波器属性是对图像参数进行配置和修改,包括图像参数、白平衡和模式控制。这些参数一旦更改,预览的视频图像马上起作用。当调整到合适的情况下,单击"确定"按钮,关闭该对话框,运行后的结果如图9-2所示。

 
(点击查看大图)图9-2  运行后的结果

如何创建Filter的属性页的更多相关文章

  1. MFC属性页对话框

    属性页对话框 分类 分页和引导 类 CPropertyPage-父亲CDialog类别,所谓的属性页或网页对话框. CPropertySheet-父类是CWnd,称为属性表单. 一个完整的属性页对话框 ...

  2. directshow filter中添加属性页

    directShow 属性页的制作,为CBall filter加了一个属性页 具体为分以下步骤: 1.在要显示属性的类中继承现ISpecifyPropertyPages类,并实现此类的GetPages ...

  3. MFC编程入门之十五(对话框:一般属性页对话框的创建及显示)

    属性页对话框包括向导对话框和一般属性页对话框两类,上一节讲了如何创建并显示向导对话框,本节将继续介绍一般属性页对话框的创建和显示. 实际上,一般属性页对话框的创建和显示过程和向导对话框是很类似的.将上 ...

  4. VS2010/MFC对话框:一般属性页对话框的创建及显示

    一般属性页对话框的创建及显示 本节将介绍一般属性页对话框的创建和显示. 实际上,一般属性页对话框的创建和显示过程和向导对话框是很类似的.鸡啄米将上一节中的向导对话框进行少量修改,使其成为一般属性页对话 ...

  5. VS2010/MFC编程入门之十五(对话框:一般属性页对话框的创建及显示)

    属性页对话框包括向导对话框和一般属性页对话框两类,上一节鸡啄米讲了如何创建并显示向导对话框,本节将继续介绍一般属性页对话框的创建和显示. 实际上,一般属性页对话框的创建和显示过程和向导对话框是很类似的 ...

  6. VS2010-MFC(对话框:一般属性页对话框的创建及显示)

    转自:http://www.jizhuomi.com/software/169.html 属性页对话框包括向导对话框和一般属性页对话框两类,上一节演示了如何创建并显示向导对话框,本节将继续介绍一般属性 ...

  7. MFC编程入门之十三(对话框:属性页对话框及相关类的介绍)

    前面讲了模态对话框和非模态对话框,本节来将一种特殊的对话框--属性页对话框. 属性页对话框的分类 属性页对话框想必大家并不陌生,XP系统中桌面右键点属性,弹出的就是属性页对话框,它通过标签切换各个页面 ...

  8. 坑爹的vector iterators incompatible错误(VS中属性页-->C/C++-->代码生成-->>运行库)

    之前一直被这个错误折磨着,就是不知道问题在那,后来找了很多资料,大概都是说这是因为多个线程同时操作vector的问题(参考这里).可是我这里的代码并没有问题,因为同样的代码在别的解决方案中已经成功运行 ...

  9. VC++在对话框中加入属性页

    当一个基于对话框的程序中有相当多的控件时,你一定会想到使用属性页来将这些控件分类放置.本文针对这种方法来讨论几种可能实现的方案. 方案一本方案的例子请见源代码打包文件中的Property1部分 在对话 ...

随机推荐

  1. C#:导入Excel通用类(CSV格式)

    一.引用插件LumenWorks.Framework.IO.dll(CsvReader) 插件下载地址:https://pan.baidu.com/s/1c3kTKli  提取密码 dz7j 二.定义 ...

  2. ABP官方文档翻译 4.3 校验数据传输对象

    校验数据传输对象 校验简介 使用数据标注 自定义校验 禁用校验 标准化 校验简介 应用的输入首先应该被校验.输入可以是用户的也可以是其他应用的.在一个web应用中,校验通常实现两次:客户端和服务端.客 ...

  3. ABP官方文档翻译 3.3 仓储

     仓储 默认仓储 自定义仓储 自定义仓储接口 自定义仓储实现 基础仓储方法管理数据库连接 查询 获取单个实体 获取实体列表 关于IQueryable 自定义返回值 插入 更新 删除 其他 关于异步方法 ...

  4. LANMP系列教程之php编译安装CentOS7环境

    前提:必须先安装好MySQL以及Apache   1.准备好源码包并配置好yum源,需要的源码包包括: libmcrypt-2.5.8-9.el6.x86_64.rpm libmcrypt-devel ...

  5. zzcms8.2#任意用户密码重置#del.php时间盲注#复现

    00x0 引言 早上起来,发现seebug更新了一批新的洞, 发现zzcms8.2这个洞好多人在挖,于是我就默默的踏上了复现之路(要不是点进去要买详情,我何必这么折腾~) 环境:zzcms8.2(产品 ...

  6. Laravel (5.5.33) 加载过程---instance方法(二)

    在bootstrap/app.php /** * 对于其中的instance register singleton 方法到时候单独拎出来说明 * * 1.设置基础路径 * 2.使用instance 方 ...

  7. Es6 Symbol.iterator

    Symbol.iterator 为每一个对象定义了默认的迭代器.该迭代器可以被 for...of 循环结构使用. --描述 当需要迭代一个对象的时候(比如在 for...of 循环的开始时),它的 @ ...

  8. php读取文件内容的三种方法

    <?php //**************第一种读取方式***************************** 代码如下: header("content-type:text/h ...

  9. 重写equals()和hashCode()

    什么时候需要重写equals()? 只有当一个实例等于它本身的时候,equals()才会返回true值.通俗地说,此时比较的是两个引用是否指向内存中的同一个对象,也可以称做是否实例相 等.而我们在使用 ...

  10. JavaScript使用点滴

    JavaScript使用点滴 一.字符串替换的小插曲 遇到一个小插曲,想要把后台返回的字符串输出给前端视图,字符串中包含\n换行,需要使用javascript对其进行替换成<br />. ...