📄 csdn_文档中心_activex control and it's container.htm
字号:
strServerPath;<BR>CString strString;<BR>//
首先,清空列表框,并用m_aImplementCategories的数据填充pcatidImpl,
作为m_pCatInfo函数<BR>EnumClassedOfCategories的第二个参数,来获取CLSID的枚举器<BR>m_lbControls.ResetContent();<BR>nImplementCategories
= m_aImplementCategories.GetSize();<BR>if (nImplementCategories ==
0) <BR>{<BR> nImplementCategories = (ULONG)-1;<BR>
pcatidImpl = NULL;<BR>}<BR>else<BR>{<BR>//
为pcatidImpl分配内存,将m_aImplementCategories数据传给pcatidImpl<BR>
pcatidImpl = (CATID*)_alloca(nImplementCategories *
sizeof(CATID));<BR> for ( iCategory = 0; iCategory <
nImplementCategories; iCategory++)<BR> {<BR>
pcatidImpl[iCategory] = m_aImplementCategories[iCategory];<BR>
}<BR>}<BR>// 获取CLSID的枚举器<BR>hResult =
m_pCatInfo->EnumClassesOfCategories(nImplementCategories,
pcatidImpl, <BR>0, NULL, &pEnum);<BR>if
(FAILED(hResult))<BR> return;<BR>// 然后通过枚举器枚举所有ActiveX
Control的CLSID, 并取得相应的用户类型名称,加入到列表框<BR>中.<BR>bDone = FALSE;<BR>while
(!bDone)<BR>{<BR> hResult = pEnum->Next(1, &clsid,
NULL); // 获得下一个ActiveX Control的CLSID<BR> if (hResult ==
S_OK)<BR> {<BR> pszName = NULL;<BR> hResult =
OleRegGetUserType(clsid, USERCLASSTYPE_FULL,
&pszName);//得到相应的用户<BR>类型名称<BR> if
(SUCCEEDED(hResult))<BR> {<BR> strName =
pszName;<BR> CoTaskMemFree(pszName);<BR>
pszName = NULL;<BR> iItem =
m_lbControls.AddString(strName);<BR> posControl =
m_lControls.AddTail(clsid);<BR>
m_lbControls.SetItemDataPtr(iItem, posControl);<BR>
}<BR> }<BR> else<BR> {<BR> bDone =
TRUE;<BR>
}<BR>}<BR>OnControlsSelChange();<BR>}<BR><BR>上面这个函数演示了如何从众多的COM组件中提取ActiveX控件并把它们添加到一个列表框中,同时保<BR>留了它们的CLSID.
其中m_pCatInfo在InitDialog中调用CreateInstance创建自己的实例.<BR>
<BR> 在我们的到了某个ActiveX控件的CLSID以后,
我们就可以利用CoCreateInstanse函数来生成该<BR>控件的实例. 如上面所说的, 每一个ActiveX控件都有一个包装类,
我们在创建控件的时候, 实际上<BR>都是通过这个包装类来进行,下面我们看一下,
在这个包装类中创建控件的代码(删除了一些不是很重<BR>要的代码).<BR><BR><BR>BOOL
CActiveXContainerCntrItem::CreateControl(REFCLSID
clsid)<BR>{<BR>IUnknown* pUnknown;<BR>// 1. 创建控件自己的实例,
在下面的步骤将对控件的一些状态进行初始化 <BR>HRESULT hResult =
CoCreateInstance(clsid, NULL, CLSCTX_INPROC¦CLSCTX_SERVER,
IID_IUnknown, (void**)&pUnknown);<BR>if
(FAILED(hResult))<BR> return FALSE;<BR><BR>// 2.
在控件中请求IOleObject接口, <BR>hResult =
pUnknown->QueryInterface(IID_IOleObject,
(void**)&m_lpObject);<BR>if (FAILED(hResult))<BR>{<BR>
pUnknown->Release();<BR> return
FALSE;<BR>}<BR>pUnknown->Release();<BR><BR>CString
strUserType;<BR>GetUserType(USERCLASSTYPE_SHORT, strUserType);<BR>//
3. 创建一个唯一的名称,
用来维护每个控件实例的唯一性<BR>GetDocument()->CreateUniqueItemName(this,
strUserType, m_strDisplayName);<BR><BR>// 4.
初始化控件的某些基本信息.<BR>InitControlInfo();<BR><BR>BOOL bQuickActivate =
FALSE;<BR>// 5. 如果控件支持IQuickActivate接口,
利用IQuickActive接口激活控件<BR>bQuickActivate = QuickActivate();<BR>if
(!bQuickActivate)<BR>{<BR> // 6. 如果控件不支持IQuickActiveX接口,
通过IOleObject接口设置控件的ClientSite.<BR>
m_lpObject->GetMiscStatus(DVASPECT_CONTENT,
&m_dwMiscStatus);<BR> if (m_dwMiscStatus &
OLEMISC_SETCLIENTSITEFIRST)<BR> hResult =
m_lpObject->SetClientSite(GetClientSite());<BR> if
(FAILED(hResult))<BR> TRACE0("Can't SetClientSite for the
Control\n");<BR>}<BR><BR>if (SUCCEEDED(hResult))<BR>{<BR> //
7. 支持IQuickActivate接口的控件必须使用下面的步骤.<BR> IPersistStreamInitPtr
pPersistStreamInit;<BR> IPersistStoragePtr
pPersistStorage;<BR><BR> pPersistStreamInit =
m_lpObject;<BR> if (pPersistStreamInit != NULL)<BR>
{<BR> hResult = pPersistStreamInit->InitNew();<BR> if
(hResult == E_NOTIMPL)<BR> hResult = S_OK;<BR>
}<BR> else<BR> {<BR> pPersistStorage =
m_lpObject;<BR> if (pPersistStorage != NULL)<BR>
{<BR> hResult =
pPersistStorage->InitNew(m_lpStorage);<BR> }<BR>
else<BR> {<BR> hResult = S_OK;<BR>
}<BR> }<BR>}<BR><BR>return FinishCreate(hResult); // 8.
在此处设置对控件的事件处理和属性处理信息<BR>}<BR><BR>下面针对上面注释中提到的一些内容进行说明, <BR>
注释2请求IOleObject接口是用来对后面的一些设置做准备, 因为这个接口要在很多的地方使<BR>用,
所以被保存在一个成员变量中. <BR> 注释3是用来区别一个控件的多个实例, 这个方法被文档类实现,
它根据控件的名称和一个数字<BR>来维护同一种控件的实例.<BR>
注释4是用来初始化控件的一些基本信息,这些信息是通过读取控件类型库将控件的属性和事件放<BR>在各自的列表中,
以后好用来对控件的属性变化和事件进行响应.<BR> 注释5是针对QuickActivate()方法的,
QuickActivate()方法首先向控件请求IQuickActivate<BR>接口, 如果控件不支持该接口, 返回FALSE,
如果控件支持该接口, 则初始化两个结构QACONTAINER<BR>和QACONTROL,
然后用这两个结构调用IQuickActivate接口的QuickActivate方法,
<BR>IQuickActivate接口是为了提高ActiveX控件的加载速度而设计的.在调用了IQuickActivate接口的<BR>QuickActivate()方法之后,
IPersist*::Init和IPersist*::InitNew方法必须被调用,
控件应该<BR>在QuickActivate方法中建立它的连接点与容器的接收器之间的连接,
如果没有调用<BR>IPersist*::Init和IPersist*::InitNew,
那么这些连接就不会生效.<BR><BR> <BR> 到这里控件已经被建立了,
但是这里并没有涉及到与容器有关的内容. 下面讲述具体的容器的实<BR>现. 我们在例子中可以看到,
程序的文档类继承自COleDocument, 在COleDocument中文档为我们实<BR>现了作为容器所必须实现的一个接口,
IOleContainer, 我们在程序中可以通过GetStartPosition<BR>(),
GetNextItem()等方法来使用这个接口,
这个接口的主要作用是用来遍历容器中的控件或其他的<BR>OLE对象.另外还有一些必须实现的接口实际上已经在MFC中实现,
我们在一般情况下只要简单的使用<BR>这些经过封装的函数就可以了, 这里主要讲述一些与控件的属性和事件处理相关的一些问题,
在<BR>ActiveX控件及实现中我们提到, 控件的属性和事件一般是通过IDispatch来实现, 在Test
<BR>Container中我们可以看到下面的一段用来实现接口映射的代码.<BR><BR>BEGIN_INTERFACE_MAP(
CTestContainer98Item, COleClientItem )<BR> INTERFACE_PART(
CTestContainer98Item, IID_IServiceProvider, ServiceProvider
)<BR><BR> INTERFACE_PART( CTestContainer98Item,
IID_IPropertyNotifySink, PropertyNotifySink )<BR>
INTERFACE_PART( CTestContainer98Item, IID_IDispatch,
AmbientProperties )<BR> INTERFACE_PART( CTestContainer98Item,
IID_IOleControlSite, OleControlSite )<BR>// INTERFACE_PART(
CTestContainer98Item, IID_IOleInPlaceSiteEx,
OleInPlaceSiteWindowless )<BR>// INTERFACE_PART(
CTestContainer98Item, IID_IOleInPlaceSiteWindowless,
OleInPlaceSiteWindowless )<BR>END_INTERFACE_MAP()<BR>
<BR>我们可以看到, 在上面的接口映射中一共出现了6个接口, 但是有两个入口是被注释的.
下面我们逐<BR>一解释这些接口:<BR>
第一个IServiceProvider在这里主要是用来提供IBindHost接口的. 实际上在实现一个容器的时<BR>候,
这个接口并不是必须的. <BR><BR>
第二个IPropertyNotifySink是用来实现控件的属性变化通知的接收器.
如果希望你的容器能在<BR>其中的控件的属性改变时得到相应的通知,
就要实现这个接口,在这个接口的OnChange方法中你可以<BR>得到相应的被改编的属性的DISPID,
有了这个DISPID,你就可以更进一步的控制控件的某些属性特征<BR>了.<BR>
第三个接口是用来为控件提供环境属性的. 为控件提供环境属性这个功能是由IDispatch接口实<BR>现的,
每一个环境属性都具有特定的DISPID, 所以当控件调用GetAmbientxxx方法时,
控件就会要求<BR>容器提供相应的属性的实现,这些属性都是被IDispatch接口实现的.<BR><BR>
第四个接口是IOleControlSite. 这个接口的主要作用是提供一些在容器内部的Site对象对内嵌<BR>在其中的控件的管理.
实现这个接口是可选的.<BR><BR> 在上面的接口映射中, 我们并没有看到对控件的事件的处理的接口映射,
在Test Container的<BR>代码中我们可以看到下面这段代码.<BR> BEGIN_INTERFACE_PART(
EventHandler, IDispatch )<BR> STDMETHOD( GetIDsOfNames )(
REFIID iid, LPOLESTR* ppszNames, UINT nNames, LCID <BR>lcid, DISPID*
pDispIDs );<BR> STDMETHOD( GetTypeInfo )( UINT iTypeInfo, LCID
lcid, ITypeInfo** ppTypeInfo );<BR> STDMETHOD(
GetTypeInfoCount )( UINT* pnInfoCount );<BR> STDMETHOD( Invoke
)( DISPID dispidMember, REFIID iid, LCID lcid, WORD wFlags,
<BR>DISPPARAMS* pdpParams, VARIANT* pvarResult, EXCEPINFO*
pExceptionInfo, UINT* <BR>piArgError );<BR>
END_INTERFACE_PART( EventHandler )<BR>很显然这段代码是用来处理事件的,
但是为什么在接口的映射部分没有它呢?
如果你查看<BR>CTestContainer98Item类的代码时你会发现一个叫做GetInterfaceHook()的方法,
这个方法有一个<BR>类型为const void*的参数pv,
这个参数实际上是一个IID类型的指针,看看下面的代码:<BR> piid = (const
IID*)pv;<BR> if( *piid == m_infoEvents.GetIID() )<BR>
{<BR> return( &m_xEventHandler );<BR>
}<BR>现在我们知道了控件的事件是怎么处理的, GetInterfaceHook()方法是CCmdTarget的一个方法,
但<BR>是在MSDN中却并没有文档说明.在这个方法中同样也实现了其他几个接口的映射关系.<BR><BR>
到这里我们已经可以了解到要实现一个ActiveX控件的容器所需要实现的接口及相关的一些问题<BR>了,
MFC的类库为我们做了许多的工作, 它们实现了一些作为控件容器所必须实现的接口,
使我们在<BR>开发这类应用程序的时候有了很好的起点.<BR><BR>4.总结<BR><BR>
上面所谈到的只是一些基本的概念及简单的实现, 开发一个ActiveX控件或者是它的容器都需要<BR>很多的知识和技术,
因为COM本身就是一项十分庞大的技术规范, 它涉及了很多方面的知识,
而这些<BR>又往往是其它基于COM的技术的基础.<BR><BR></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE align=center bgColor=#006699 border=0 cellPadding=0 cellSpacing=0
width=770>
<TBODY>
<TR bgColor=#006699>
<TD align=middle bgColor=#006699 id=white><FONT
color=#ffffff>对该文的评论</FONT></TD>
<TD align=middle>
<SCRIPT
src="CSDN_文档中心_ActiveX Control and it's Container.files/readnum.htm"></SCRIPT>
</TD></TR></TBODY></TABLE><BR>
<DIV align=center>
<TABLE align=center bgColor=#cccccc border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TH bgColor=#006699 id=white><FONT
color=#ffffff>我要评论</FONT></TH></TR></TBODY></TABLE></DIV>
<DIV align=center>
<TABLE border=0 width=770>
<TBODY>
<TR>
<TD>你没有登陆,无法发表评论。 请先<A
href="http://www.csdn.net/member/login.asp?from=/Develop/read_article.asp?id=767">登陆</A>
<A
href="http://www.csdn.net/expert/zc.asp">我要注册</A><BR></TD></TR></TBODY></TABLE></DIV><BR>
<HR noShade SIZE=1 width=770>
<TABLE border=0 cellPadding=0 cellSpacing=0 width=500>
<TBODY>
<TR align=middle>
<TD height=10 vAlign=bottom><A
href="http://www.csdn.net/intro/intro.asp?id=2">网站简介</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=5">广告服务</A> - <A
href="http://www.csdn.net/map/map.shtm">网站地图</A> - <A
href="http://www.csdn.net/help/help.asp">帮助信息</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=2">联系方式</A> - <A
href="http://www.csdn.net/english">English</A> </TD>
<TD align=middle rowSpan=3><A
href="http://www.hd315.gov.cn/beian/view.asp?bianhao=010202001032100010"><IMG
border=0 height=48
src="CSDN_文档中心_ActiveX Control and it's Container.files/biaoshi.gif"
width=40></A></TD></TR>
<TR align=middle>
<TD vAlign=top>百联美达美公司 版权所有 京ICP证020026号</TD></TR>
<TR align=middle>
<TD vAlign=top><FONT face=Verdana>Copyright © CSDN.net, Inc. All rights
reserved</FONT></TD></TR>
<TR>
<TD height=15></TD>
<TD></TD></TR></TBODY></TABLE></DIV>
<DIV></DIV><!--内容结束//--><!--结束//--></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -