一個技術研究工作,需要使用native代碼構建一個dll,並實現其中的函數:
HRESULT WMCreateStreamForURL( LPCWSTR pwszURL,
BOOL* pfCorrectSource,
IStream** ppStream
)
因為未來項目準備使用C#編程,所以很自然想到了C++/CLI來實現這個dll,並且將調用轉寄到C#的實現裡。做起來很容易,我用C#實現了一個StreamManager,負責檢查傳入的url並返回相應的IStream實現。
這裡環境為VS2005。用C#實現的工程叫做protocol,用C++/CLI實現的工程名字為protocol_stub(必須使用/cli參數編譯)。
首先遇到的是C++/CLI中的Managed 程式碼和Unmanaged 程式碼的相互調用問題,查閱了一下協助,發現使用#pragma managed和#pragma unmanaged就可以劃定函數的編譯方式到底是native還是cli。
其次,在C++中調用StreamManager時候報告找不到。經過檢查,發現要在C++工程中添加對protocol的引用,同時,要在C++代碼的managed部分使用指令
#pragma managed
#import "protocol.tlb" raw_interfaces_only
using namespace protocol;
using namespace System;
using namespace System::Runtime::InteropServices;
這樣才可以正常編譯代碼StreamManager^ mgr = StreamManager::Instance;其中,protocol.tlb是用VS內建工具TlbExp產生的。
第三個問題是匯出.net對象的問題。兩個工程可以正常編譯,但是測試回合時候卻報告異常。經過檢查,原來是System::Runtime::InteropServices::ComTypes::IStream雖然就是IStream的實現,但是卻無法進行強制轉換。用如下的代碼雖然編譯可以過去,但是不能工作
ComTypes::IStream^ s = mgr->getStream(url);
cli::pin_ptr<ComTypes::IStream^> p = &s;
*ppStream = (IStream*)p;
經過查詢,發現解決的方法很簡單,代碼如下:
ComTypes::IStream^ s = mgr->getStream(url);
IntPtr^ handle = Marshal::GetComInterfaceForObject(s, ComTypes::IStream::typeid);
*ppStream = (IStream*)handle->ToPointer();
這樣,整個工作順利完成。
總結:C++/CLI經過vs2003之後變動很大,從醜小鴨終於變成了白天鵝,不僅實現了C++和CLI的無縫整合,而且保留了原先C++文法的優美和簡潔。因為可以支援本地代碼和CLI代碼的混合調用,不僅可以充分利用Windows系統和.net的資源,而且因為本地代碼的不可反編譯性,使得C++/CLI天生就有了安全保密性。看來要在項目中的一些關鍵演算法部分使用C++了。
另外,幾個要注意的地方:
C#中xxx.getType()或者typeof(ClassXXX)在C++/CLI中對應代碼是 ClassXXX::typeid
在C++/CLI中#import 比較重要,要注意
在C#中,所有對象天生就是COM對象(好在我一直沒有弄錯)