?? csdn_文檔中心_com深入理解(下)——方法參數類型為cruntimeclass、void等.htm
字號:
style="COLOR: blue">void</SPAN> *pA )<BR>{<BR>
<SPAN style="COLOR: blue">if</SPAN>( !pA
)<BR> <SPAN
style="COLOR: blue">return</SPAN>
E_INVALIDARG;<BR> CA *pAA = <SPAN
style="COLOR: blue">reinterpret_cast</SPAN>< CA* >( pA
);<BR><BR><SPAN style="COLOR: green">//
調用遠程版的代理函數以傳遞參數,由MIDL生成</SPAN><BR> <SPAN
style="COLOR: blue">return</SPAN> IAbc_RemoteGetA_Proxy( This,
&pAA->m_a, &pAA->m_b );<BR>}<BR>HRESULT
STDMETHODCALLTYPE IAbc_GetA_Stub( IAbc *This, <SPAN
style="COLOR: blue">long</SPAN> *pA, <SPAN
style="COLOR: blue">long</SPAN> *pB )<BR>{<BR>
<SPAN style="COLOR: blue">void</SPAN> *p = CoTaskMemAlloc( <SPAN
style="COLOR: blue">sizeof</SPAN>( CA ) );<BR>
<SPAN style="COLOR: blue">if</SPAN>( !p
)<BR> <SPAN
style="COLOR: blue">return</SPAN> E_FAIL;<BR> CA
*pAA = <SPAN style="COLOR: blue">new</SPAN>( p ) CA; <SPAN
style="COLOR: green">// 生成一個類對象</SPAN><BR><BR><SPAN
style="COLOR: green">// 調用對象的本地方法</SPAN><BR>
HRESULT hr = This->GetA( pAA );<BR> <SPAN
style="COLOR: blue">if</SPAN>( SUCCEEDED( hr )
)<BR>
{<BR> *pA =
pAA->m_a;<BR> *pB =
pAA->m_b;<BR> }<BR><BR><SPAN
style="COLOR: green">// 釋放資源</SPAN><BR>
pAA->~CA();<BR> CoTaskMemFree( p
);<BR> <SPAN style="COLOR: blue">return</SPAN>
hr;<BR>}<BR>
最后添加預定義宏REGISTER_PROXY_DLL和_WIN32_WINNT=0x500,并連接rpcrt4.lib庫文件,確保沒有打開/TC或/TP編譯開關以保證對上面的abc.cpp進行C++編譯,而對MIDL生成的.c的源文件進行C編譯。<BR>
使用時如下:<BR>IAbc *pA; <SPAN style="COLOR: green">//
假設已初始化</SPAN><BR>CA a;<BR>pA->GetA( <SPAN
style="COLOR: blue">reinterpret_cast</SPAN>< <SPAN
style="COLOR: blue">void</SPAN>* >( &a )
);<BR> 而組件實現的代碼如下:<BR>STDMETHODIMP CAbc::GetA(
<SPAN style="COLOR: blue">void</SPAN> *pA
)<BR>{<BR> <SPAN style="COLOR: blue">if</SPAN>(
!pA )<BR> <SPAN
style="COLOR: blue">return</SPAN>
E_INVALIDARG;<BR><BR> *<SPAN
style="COLOR: blue">reinterpret_cast</SPAN>< CA* >( pA ) =
m_A;<BR> <SPAN style="COLOR: blue">return</SPAN>
S_OK;<BR>}<BR>
如上就實現了將類CA的對象進行傳值操作,但不是傳址操作。前面已說明,欲進行后者,必須編寫相應的代理類。先使用上面的方法將必要的信息傳遞后,再根據傳遞的信息初始化類CA的代理對象以建立連接。一般如非得已最好不要編寫代理對象,而通過將類轉成接口形式,由MIDL輔助生成代理/占位組件以變相實現。<BR>
下面介紹使用[wire_marshal()]屬性進行傳值操作。</P>
<P><BR><FONT face=楷體_GB2312
size=4><STRONG>[wire_marshal()]</STRONG></FONT></P>
前面使用方法別名機制實現了傳遞自定義數據類型,但是其是以方法為單位進行處理的,當要多次使用某一個數據類型時,如前面的CA*,如果對每個使用到CA*的方法都進行上面的操作,很明顯地效率低下,為此MIDL提供了[wire_marshal()]屬性(當然不止這么一個屬性)。<BR>
[wire_marshal()]屬性只能用于類型定義,即typedef中,使用語法如下:<BR><SPAN
style="COLOR: blue">typedef</SPAN> [wire_marshal(<I>wire_type</I>)]
<I>type-specifier</I> <I>userm-type</I>;<BR>
其將一個線類型(wire-type,即MIDL可以直接處理的類型)和一個描述類型(type-specifier,即不能或不打算被MIDL處理的特殊數據類型)相關聯,并用一個可識別名字(userm-type)標識。其和[transmit_as()]屬性類似,都是將兩個類型進行關聯,就如前面的[local]和[call_as()]將兩個方法進行關聯一樣,只不過[wire_marshal()]是直接將描述類型按IDL的列集格式(網絡數據描述NDR——Network
Data
Representation)列集到指定的緩沖區中,而[transmit_as()]還需匯集代碼在中間再轉換一次,因此[wire_marshal()]的效率要更高,只不過由于需要編寫列集代碼,因此需要了解NDR格式,處理數據對齊等問題,所以顯得麻煩和復雜。最常見的應用就是句柄的定義,如下:<BR><SPAN
style="COLOR: blue">typedef</SPAN> <SPAN
style="COLOR: blue">union</SPAN> _RemotableHandle <SPAN
style="COLOR: blue">switch</SPAN>( <SPAN
style="COLOR: blue">long</SPAN> fContext )
u<BR>{<BR> <SPAN style="COLOR: blue">case</SPAN>
WDT_INPROC_CALL: <SPAN
style="COLOR: blue">long</SPAN>
hInproc;<BR> <SPAN style="COLOR: blue">case</SPAN>
WDT_REMOTE_CALL: <SPAN
style="COLOR: blue">long</SPAN> hRemote;<BR>}
RemotableHandle;<BR><SPAN style="COLOR: blue">typedef</SPAN> [<SPAN
style="COLOR: blue">unique</SPAN>] RemotableHandle *
wireHWND;<BR><SPAN style="COLOR: blue">#define</SPAN>
DECLARE_WIREM_HANDLE(name)
\<BR>
<SPAN style="COLOR: blue">typedef</SPAN> [wire_marshal(wire ##
name)] <SPAN style="COLOR: blue">void</SPAN> *
name<BR>DECLARE_WIREM_HANDLE( HWND );<BR>
也就是說我們常用的HWND類型是:<BR><SPAN style="COLOR: blue">typedef</SPAN>
[wire_marshal( wireHWND )] <SPAN style="COLOR: blue">void</SPAN>*
HWND;<BR>
即其在應用程序中(即客戶或組件,即代理/占位的使用者)是void*類型,當需要傳輸時,實際是傳輸結構RemotableHandle的一個實例,而此結構是一個以fContext為標識的聯合,實際為8字節長。<BR>
為了實現上面提到的void*和RemotableHandle*的關聯,開發人員必須提供下面四個函數的定義:<BR><SPAN
style="COLOR: blue">unsigned long</SPAN> __RPC_USER <
<I>userm-type</I> >_UserSize( <SPAN style="COLOR: green">//
返回欲請求的緩沖區大小</SPAN><BR> <SPAN
style="COLOR: blue">unsigned long</SPAN> __RPC_FAR *pFlags,
<SPAN style="COLOR: green">// 一個標志參數,后敘</SPAN><BR>
<SPAN style="COLOR: green">//
給出當前已經請求的緩沖區大小,返回的大小應該以此作為起點</SPAN><BR> <SPAN
style="COLOR: blue">unsigned long</SPAN>
StartingSize,<BR> < <I>userm-type</I> >
__RPC_FAR * pUser_typeObject ); <SPAN style="COLOR: green">//
欲傳遞的描述類型的實例</SPAN><BR><SPAN style="COLOR: blue">unsigned char</SPAN>
__RPC_FAR * __RPC_USER < <I>userm-type</I>
>_UserMarshal( <SPAN style="COLOR: green">//
列集</SPAN><BR> <SPAN style="COLOR: blue">unsigned
long</SPAN> __RPC_FAR * pFlags, <SPAN
style="COLOR: green">// 標志參數</SPAN><BR> <SPAN
style="COLOR: blue">unsigned char</SPAN> __RPC_FAR *
Buffer, <SPAN style="COLOR: green">//
已分配的緩沖器有效指針</SPAN><BR> < <I>userm-type</I> >
__RPC_FAR * pUser_typeObject ); <SPAN style="COLOR: green">//
欲列集的描述類型的實例</SPAN><BR><SPAN style="COLOR: blue">unsigned char</SPAN>
__RPC_FAR * __RPC_USER < <I>userm-type</I>
>_UserUnmarshal( <SPAN style="COLOR: green">//
散集</SPAN><BR> <SPAN style="COLOR: blue">unsigned
long</SPAN> __RPC_FAR * pFlags, <SPAN
style="COLOR: green">// 標志參數</SPAN><BR> <SPAN
style="COLOR: blue">unsigned char</SPAN> __RPC_FAR *
Buffer, <SPAN style="COLOR: green">//
列集數據的緩沖器指針</SPAN><BR> <SPAN
style="COLOR: green">//
描述類型的實例指針,從列集數據中散集出描述類型后,放在此指針所指內存之中</SPAN><BR>
< <I>userm-type</I> > __RPC_FAR * pUser_typeObject );<BR><SPAN
style="COLOR: blue">void</SPAN> __RPC_USER < <I>userm-type</I>
>_UserFree( <SPAN style="COLOR: green">//
釋放UserUnmarshal中分配的內存</SPAN><BR> <SPAN
style="COLOR: blue">unsigned long</SPAN> __RPC_FAR *
pFlags, <SPAN style="COLOR: green">//
標志參數</SPAN><BR> <SPAN style="COLOR: green">//
UserUnmarshal中的pUser_typeObject參數,一個描述類型的實例的指針</SPAN><BR>
< <I>userm-type</I> > __RPC_FAR * pUser_typeObject
);<BR>
對于前面的HWND,開發人員就必須提供如下四個函數的定義(當然Microsoft是已經提供了的):<BR><SPAN
style="COLOR: blue">unsigned long</SPAN>
__RPC_USER<BR> HWND_UserSize( <SPAN
style="COLOR: blue">unsigned long</SPAN>*, <SPAN
style="COLOR: blue">unsigned long</SPAN>, HWND* );<BR><SPAN
style="COLOR: blue">unsigned char</SPAN>*
__RPC_USER<BR> HWND_UserMarshal</SPAN>( <SPAN
style="COLOR: blue">unsigned long</SPAN>*, <SPAN
style="COLOR: blue">unsigned</SPAN><SPAN style="COLOR: blue">
char</SPAN>*, HWND* );<BR><SPAN style="COLOR: blue">unsigned
char</SPAN>* __RPC_USER<BR>
HWND_UserUnmarshal</SPAN>( <SPAN style="COLOR: blue">unsigned
long</SPAN>*, <SPAN style="COLOR: blue">unsigned char</SPAN>*, HWND*
);<BR><SPAN style="COLOR: blue">void</SPAN>
__RPC_USER<BR> HWND_UserFree( <SPAN
style="COLOR: blue">unsigned long</SPAN>*, HWND*
);<BR>
在MIDL生成的匯集代碼中,遇到方法參數類型為HWND時,發生如下事情:<BR> 1.
調用HWND_UserSize并傳遞應用程序(客戶或組件,視HWND是in參數還是out參數)傳進來的HWND的實例以得到欲傳遞此實例需要的緩沖區大小<BR>
2. 在RPC通道上分配相應的內存塊<BR> 3.
調用HWND_UserMarshal,依舊傳遞前面的HWND實例以及分配到的緩沖區的指針以將此HWND實例列集到緩沖區中<BR>
4. 通過RPC通道將緩沖區內容傳遞到對方進程空間中<BR> 5.
調用HWND_UserUnmarshal,并傳遞通過RPC通道得到的列集數據緩沖區的指針和生成的一臨時HWND實例的指針以記錄散集出來的HWND實例<BR>
6. 以返回的HWND實例為參數調用應用程序的方法<BR> 7.
調用HWND_UserFree,傳遞前面因調用HWND_UserUnmarshal而生成的臨時記錄散集出的HWND實例的指針以釋放因此分配的內存<BR>
以上,就是[wire_marshal()]屬性對線類型和描述類型的綁定的實現。但其中漏了一點,就是標志參數pFlags的使用。此標志參數是一個4字節數字,其高16位是一些關于NDR格式的編碼規則,以使得NDR引擎(將填寫好的緩沖區內容按NDR格式串的規則進行排列以在網上傳輸的程序)能做出正確的數據轉換。其低16位是一個MSHCTX枚舉值,指明調用環境,是進程內還是跨進程、是遠程還是本地(具體信息還請查閱MSDN),因而可以在上面的四個函數中根據此值作出相應的優化。<BR>
下面為上面的CA*實現[wire_marshal()]屬性。<BR>
前面已經了解到,CA*由于在IDL中沒有對應的類型,應該使用void*來進行傳遞,在abc.idl中增加如下代碼:<BR><SPAN
style="COLOR: blue">typedef</SPAN> <SPAN
style="COLOR: blue">struct</SPAN> _SA<BR>{<BR>
<SPAN style="COLOR: blue">long</SPAN> a, b;<BR>} *PSA;<BR><SPAN
style="COLOR: blue">typedef</SPAN> [wire_marshal( PSA )] <SPAN
style="COLOR: blue">void</SPAN>* PA;<BR>
并為接口IAbc增加一個方法:<BR>HRESULT SetA( [<SPAN
style="COLOR: blue">in</SPAN>] PA a );<BR>
接著在abc.cpp中增加如下代碼:<BR><SPAN style="COLOR: blue">unsigned</SPAN>
<SPAN style="COLOR: blue">long</SPAN> __RPC_USER PA_UserSize( <SPAN
style="COLOR: blue">unsigned</SPAN> <SPAN
style="COLOR: blue">long</SPAN>* <SPAN style="COLOR: green">/*
pFlags
*/</SPAN>,<BR>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -