📄 csdn_文档中心_com深入理解(上)——方法参数类型为cruntimeclass、void等.htm
字号:
style="COLOR: green">// 假设没开任何优化开关</SPAN><BR>}<BR>S_CA_D
s_aa;<BR>S_CA_CA( &s_aa );<BR><SPAN
style="COLOR: blue">long</SPAN> a = ( s_aa.pF->GetA )( &s_aa
);<BR>( s_aa.pF->SetA )( &s_aa, 34 );</P>
<P><BR><FONT face=楷体_GB2312
size=4><STRONG>无MIDL支持的参数传递</STRONG></FONT></P>
<P>
了解了C++中类的实现方式后,因此要传递一个类指针,实际就是传递一个结构指针,而论坛中对此的问题就是MIDL编译时总是说“类型未定义”之类的错误,下面先了解MIDL的作用。<BR>
MIDL只是Microsoft提供的一个对IDL(Interface Definition
Language,接口定义语言)或ODL(Object Definition
Language,对象定义语言)编写的代码进行编译的编译器。此编译器编译.IDL或.ODL文件后,生成类型库(如果有必要)和代理/占位组件(关于代理/占位组件可参考我写的另一篇文章《COM线程模型》)的工程文件。<BR>
当MIDL是为COM(也可是RPC)生成代理/占位组件时,其生成的工程文件包括:XXX_i.c、XXX_p.c、dlldata.c、XXX.h及一个MAKE文件XXXps.mk(VC自代的工具NMAKE程序可使用此文件)以帮助生成代理/占位组件(其中被编译的.idl文件名为XXX.idl)。<BR>
应当注意,此代理/占位组件不是必须的,其只有当需要时(如接口指针需要进行汇集操作,组件请求调用环境等)才发生作用,除此以外的任何时候其都不发生作用。因此,如果可以保证将生成的组件不会被跨套间进行调用或不会要求COM提供附加功能(如调用CoGetCallContext)时,可以不生成代理/占位组件,带来的后果就是此COM组件的适用范围被严重限制(此时相当于一个DLL,但还是具有动态提供服务的功能),当需要汇集时,如果没有编写防御代码,程序很可能崩溃。下面举例说明如何编写相当于DLL,不需要.idl文件的COM组件。<BR>
下面是欲实现的接口:<BR><SPAN style="COLOR: blue">interface</SPAN> IModule :
IUnknown<BR>{<BR> HRESULT GetViewRuntimeClass(
[<SPAN style="COLOR: blue">out</SPAN>] CRuntimeClass **pClass
);<BR>};<BR> 由于已经没有MIDL的唠叨,因此还可以如下:<BR><SPAN
style="COLOR: blue">interface</SPAN> IModule :
IUnknown<BR>{<BR> HRESULT GetViewRuntimeClass(
[<SPAN style="COLOR: blue">out</SPAN>] CRuntimeClass **pClass
);<BR> <SPAN style="COLOR: blue">void</SPAN>
GetModuleName( [<SPAN style="COLOR: blue">out</SPAN>] CString *pName
);<BR>};<BR> 手工生成一个.h文件,如下:<BR><SPAN
style="COLOR: green">/////////////////////ModuleInterface.h///////////////////////////</SPAN><BR><SPAN
style="COLOR: blue">#pragma</SPAN> <SPAN
style="COLOR: blue">once</SPAN><BR><SPAN
style="COLOR: blue">#include</SPAN> <objbase.h><BR><BR><SPAN
style="COLOR: blue">class</SPAN> <SPAN
style="COLOR: blue">__declspec</SPAN>( <SPAN
style="COLOR: blue">uuid</SPAN>(
"0E0042F0-0000-0360-3400-0000EA0030AB" ) )<BR>IModule : <SPAN
style="COLOR: blue">public</SPAN> IUnknown<BR>{<BR><SPAN
style="COLOR: blue">public</SPAN>:<BR> <SPAN
style="COLOR: blue">virtual</SPAN> HRESULT
STDMETHODCALLTYPE<BR>
GetViewRuntimeClass( CruntimeClass **pClass ) =
0;<BR> <SPAN style="COLOR: blue">virtual</SPAN>
<SPAN style="COLOR: blue">void</SPAN>
STDMETHODCALLTYPE<BR>
GetModuleName( CString *pName ) = 0;<BR>};<BR><SPAN
style="COLOR: green">/////////////////////////////////////////////////////////////////</SPAN><BR>
然后在工程中的stdafx.h中添加#include
"ModuleInterface.h"以将IModule导入到工程中。然后就可以在工程中任何欲实现此接口的源文件(.cpp)中就编写的方法(MFC还是ATL或其他组件实现方式)进行相应的编写。下面使用ATL来编写一个实现了IModule接口的组件。<BR><SPAN
style="COLOR: green">/////////////////////PopedomModule.h/////////////////////////////</SPAN><BR><SPAN
style="COLOR: blue">#pragma</SPAN> <SPAN
style="COLOR: blue">once</SPAN><BR><BR><SPAN
style="COLOR: blue">class</SPAN> ATL_NO_VTABLE CPopedomModule
:<BR> <SPAN style="COLOR: blue">public</SPAN>
CComObjectRootEx< CComSingleThreadModel
>,<BR> <SPAN style="COLOR: blue">public</SPAN>
CComCoClass< CPopedomModule, &CLSID_PopedomModule
>,<BR> <SPAN style="COLOR: blue">public</SPAN>
IModule<BR>{<BR><SPAN
style="COLOR: blue">public</SPAN>:<BR><BR>DECLARE_REGISTRY_RESOURCEID(
IDR_POPEDOMMODULE )<BR>BEGIN_COM_MAP( CPopedomModule
)<BR> COM_INTERFACE_ENTRY( IModule
)<BR>END_COM_MAP()<BR>DECLARE_PROTECT_FINAL_CONSTRUCT()<BR><BR><SPAN
style="COLOR: green">// 接口实现</SPAN><BR><SPAN
style="COLOR: blue">public</SPAN>:<BR><SPAN style="COLOR: green">//
IModule</SPAN><BR> STDMETHOD(GetViewRuntimeClass)(
CRuntimeClass **pClass );<BR> <SPAN
style="COLOR: blue">void</SPAN> STDMETHODCALLTYPE GetModuleName(
CString *pName );<BR>};<BR><BR>OBJECT_ENTRY_AUTO( __uuidof(
PopedomModule ), CPopedomModule )<BR><SPAN
style="COLOR: green">/////////////////////////////////////////////////////////////////</SPAN><BR><SPAN
style="COLOR: green">/////////////////////PopedomModule.cpp///////////////////////////</SPAN><BR><SPAN
style="COLOR: blue">#include</SPAN> "stdafx.h"<BR><SPAN
style="COLOR: blue">#include</SPAN>
"PopedomModule.h"<BR><BR>STDMETHODIMP
CPopedomModule::GetViewRuntimeClass( CRuntimeClass **pClass
)<BR>{<BR> if( !pClass
)<BR> <SPAN
style="COLOR: blue">return</SPAN>
E_INVALIDARG;<BR><BR> *pClass = RUNTIME_CLASS(
CPopedomView );<BR> <SPAN
style="COLOR: blue">return</SPAN> S_OK;<BR>}<BR><SPAN
style="COLOR: blue">void</SPAN> STDMETHODCALLTYPE
CPopedomModule::GetModuleName( CString *pName
)<BR>{<BR> if( pName
)<BR> *pName =
L"权限";<BR>}<BR><SPAN
style="COLOR: green">/////////////////////////////////////////////////////////////////</SPAN><BR>
上面的做法是不提倡的,但是可以运行,只要保证不发生汇集操作(如跨套间访问)等,则不会出什么问题。这里使用了MFC特定的类型CRuntimeClass,因此需要保证客户端也加载MFC库文件,并且其使用的参数是MFC特定的类型,很明显,不能和其他语言公用了。并且其由于导出类,必须保证组件和客户是使用同一个编译器编译的,因为不同的编译器对成员函数的名字修饰不同(如上面我就修饰成S_CA_GetA,但其他人可能修饰成CA_GetA)。这也是为什么其相当于一个高级的DLL,而高级也就是还保持着语义,依旧有着COM编程模型中接口的意义(关于COM编程模型,可以参考我写的另一篇文章《COM样例(二)》)。</P>
<P><BR><FONT face=楷体_GB2312 size=4><STRONG>代理对象</STRONG></FONT></P>
<P>
上面的做法说白了其实就是传递一个四字节数字(假设为32位系统开发)而已,所以才被称为高级的DLL。但是,当将这个进程内的一个自定义类CA的对象指针传递给另一个进程,则上面的解决方法就无效了,因为指针所指的内存是进程相关的,不能跨进程使用。<BR>
由于类对象的指针其实就是一个结构实例的指针,因此只需将那个结构实例复制一份,通过任何跨进程手段传递过去就可以了(这里没考虑静态成员变量)。这是传值操作,但类对象的指针这一点已经表明是传址操作。而所谓的传址操作的意义就是引用,也就是说关心的不是类对象的内容,而是类对象的控制。但由于类对象在内存中的所在位置无论如何客户(另一个进程)都无法访问到,这就和Windows提出的内核句柄一样,只能由核心代码访问内核句柄所在内存。因此就必须有人代客户去访问那块内存以表现出客户能够控制那个类对象,而这个人就是有名的“代理对象”。<BR>
因此为了传递自定义类对象的指针以实现引用目的(不是为了效率而使用指针),就必须为那个类提供一个代理类,而客户就通过这个代理类的对象来间接访问真正的类对象。也就是说,代理对象的存在是为了让客户可以操作真正的类对象,如果只是需要类对象的状态,那么代理对象则不是必须的,只需执行一个传值操作即可。<BR>
为了表现出是客户在操作真正的对象,代理对象的工作就是将客户的所有命令原封不动地传到真正对象所在进程,并扮演客户对类对象进行操作。因此代理对象就被分成两部分,一部分在客户进程中运作,扮演类对象,称为代理(proxy);另一部分在类对象所在进程运作,扮演客户,称为占位(stub)。<BR>
如果将欲传递的类对象的指针变成接口形式,就可以利用MIDL帮忙生成代理类的代码(也就是代理/占位组件),而不用自己编写了,但当要传递的类是无法改变的,如一个类库中的类,则只有自己编写代理类的代码了(因为IDL语言中没有类这个概念)。</P>
<P>
出于篇幅和时间的考虑,如何使用MIDL来正确地传递一个自定义类对象的指针,即如何利用MIDL辅助编写代理/占位组件以支持传递自定义类对象的指针,而不是上面的极端做法,留于《COM深入理解(下)》中说明,待续。</P></FONT><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_文档中心_COM深入理解(上)——方法参数类型为CRuntimeClass、void等.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=27324">登陆</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_文档中心_COM深入理解(上)——方法参数类型为CRuntimeClass、void等.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 + -