CSharp Tips:讓DotNet實現的COM物件支援IObjectSafety介面 (轉)

amyz發表於2007-08-16
CSharp Tips:讓DotNet實現的COM物件支援IObjectSafety介面 (轉)[@more@]
當我們實現的COM,或者在中的時候,往往會出現警告框,提示不的控制元件正在執行。這是因為瀏覽器安全策略所限定的,瀏覽器認為只有“安全的物件”才能夠被。
所謂安全的物件就是指那些不訪問本地資源的物件,例如不會去讀登錄檔,不會寫等等。一個滿足條件的物件透過支援ISafety介面告訴瀏覽器,自己是合法的。
下面就簡單的介紹一下怎麼在中實現對於IObjectSafety介面的支援。
 
思路
C/C++d的可以直接在SDK中找到IObjectSafety的定義,所以需要支援的話非常容易。C#比較麻煩,因為我們沒有辦法獲得IObjectSafety的定義,不過沒有問題,我們可以按照IObjectSafety在SDK中的定義,在C#的工程中重新定義該介面。
如果大家瞭解COM機制一定會知道,所謂藉口的定義之出現在型別庫中,與實現無關。而判斷一個介面唯一性就是定義介面時指定的UUID。此外自己重新定義時需要保證介面中沒有的引數與返回值必須與原定義一致即可。
我們的做法就是,找到ObjSafe.idl,然後複製其中的UUID,利用這個UUID在C#中定一個interface IObjectSafety,並且申明其中的兩個函式;定義完成之後,讓需要檢查安全介面的繼承該介面,並在該元件內部實現IObjectSafety的兩個函式,按照要求做適當的返回,那麼用這個元件包裝的COM物件在IE中呼叫就被認為是安全的了。
 
第一次嘗試
按照上面的思路,我們開始進行嘗試
idl中的介面定義
[
 object,
 uuid(CB5BDC81-93C1-11cf-8F20-00805F2CD064),
 pointer_default(unique)
]
interface IObjectSafety : IUnknown
{
 HRESULT GetInterfaceSafetyOptions(
 [in]  REFIID riid, // Interface that we want options for
 [out] D * pdwSupportedOptions, // Options meaningful on this interface
 [out] DWORD * pdwEnabledOptions); // current option values on this interface
 
 HRESULT SetInterfaceSafetyOptions(
 [in]  REFIID riid, // Interface to set options for
 [in]  DWORD dwOptionSetMask, // Options to change
 [in]  DWORD dwEnabledOptions); // New option values
}
IObjectSafety介面定義
  因為介面中存在指標,所以直接採用Int32的整型形式,用到了unsafe code。
 [Guid("CB5BDC81-93C1-11cf-8F20-00805F2CD064")]
 public interface IObjectSafety
 {
 // methods
 unsafe void GetInterfacceSafyOptions(
 System.Int32 riid,
 System.Int32* pdwSupportedOptions,
 System.Int32* pdwEnabledOptions);
 void SetInterfaceSafetyOptions(
 System.Int32 riid,
 System.Int32 dwOptionsSetMask,
 System.Int32 dwEnabledOptions);
 }
繼承
 public class MyControl : System..Forms.UserControl,IObjectSafety
實現
 // implement functions of IObjectSafety
 public unsafe void GetInterfacceSafyOptions(System.Int32 riid,System.Int32* pdwSupportedOptions,System.Int32* pdwEnabledOptions)
 {
 ...
 }
 public void SetInterfaceSafetyOptions(System.Int32 riid,System.Int32 dwOptionsSetMask,System.Int32 dwEnabledOptions)
 {
 ... 
 }
  一切正常編譯透過,但是透過IE呼叫測試頁面,在裝載頁面的時候卻產生了一個關閉應用程式異常,仔細察看內容Error Report是的地址訪問。無語中...
 
第二次嘗試
  由於是非法的記憶體地址訪問,很自然的聯想到是介面定義的問題,因為存在unsafe code,查查文件發現根本無需使用unsafe code這麼誇張,可以透過out這個引數修飾符解決。
  修改定義和實現如下
IObjectSafety介面定義
 [Guid("CB5BDC81-93C1-11cf-8F20-00805F2CD064")]
 public interface IObjectSafety
 {
 // methods
 void GetInterfacceSafyOptions(
 System.Int32 riid,
 out System.Int32 pdwSupportedOptions,
 out System.Int32 pdwEnabledOptions);
 void SetInterfaceSafetyOptions(
 System.Int32 riid,
 System.Int32 dwOptionsSetMask,
 System.Int32 dwEnabledOptions);
 }
實現
 // implement functions of IObjectSafety
 public unsafe void GetInterfacceSafyOptions(System.Int32 riid,out System.Int32 pdwSupportedOptions,out System.Int32 pdwEnabledOptions)
 {
 ...
 }
 public void SetInterfaceSafetyOptions(System.Int32 riid,System.Int32 dwOptionsSetMask,System.Int32 dwEnabledOptions)
 {
 ... 
 }
  編譯透過,不錯;IE呼叫測試頁面,同樣的錯誤!鬱悶,無鬥志,回家。
 
第三次嘗試
  睡了一覺,飽餐戰飯,繼續思考。
  自己比較了生成的型別庫,發現一些很奇怪的現象,在型別庫中IObjectSafety居然被定義了兩次interface IObjectSafety : IUnknown,以及dispinterface IObjectSafety : IDispatch。而偏偏MyControl是從dispinterface IObjectSafety上繼承的。這就與正確的IObjectSafety的介面說明相違背,問題應該出在這裡。
  MSDN,查文件。System.Runtime.InteropService下有很多關於描述介面的屬性,從中可以找到產生問題的原因。有一個屬性InterfaceTypeAttribute,就是用來說明定義的介面是從IUnknown繼承還是IDispatch繼承,預設情況下是Dual的,所以是兩份。
  再次定義如下:
 [Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064"),InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 public interface IObjectSafety
 {
 // methods
 void GetInterfacceSafyOptions(
 System.Int32 riid,
 out System.Int32 pdwSupportedOptions,
 out System.Int32 pdwEnabledOptions);
 void SetInterfaceSafetyOptions(
 System.Int32 riid,
 System.Int32 dwOptionsSetMask,
 System.Int32 dwEnabledOptions);
 }
  其餘程式碼不變,重新編譯,透過;察看匯出型別庫,果然少了很多垃圾;呼叫測試頁面,正確。激動中...
 
怎樣讓IE認為你的物件安全
  實現了這個介面,剩下的事情就很簡單了。前面提到過如果按照正規的途徑你需要確保你的程式碼沒有訪問系統的本地自然,然後按照文件要求,當該物件被不同的介面呼叫查詢的時候,做不同的反饋。具體實現可以在MSDN的Sample中找到。
  當然我們可以寫一個物件讀寫本地檔案,但是支援IObjectSafety介面,並且始終宣告自己是合法的,這樣來欺騙瀏覽器,那麼程式碼就很簡單了,如下:
 // implement functions of IObjectSafety
 public void GetInterfacceSafyOptions(System.Int32 riid,out System.Int32 pdwSupportedOptions,out System.Int32 pdwEnabledOptions)
 {
 pdwSupportedOptions = CLsObjectSafety.INTERFACESAFE_FOR_UNTRUSTED_CALLER;
 pdwEnabledOptions = CLsObjectSafety.INTERFACESAFE_FOR_UNTRUSTED_DATA;
 }
 public void SetInterfaceSafetyOptions(System.Int32 riid,System.Int32 dwOptionsSetMask,System.Int32 dwEnabledOptions)
 {
 }
  只要這麼些,就不會再有討厭的對話方塊彈出了。
 
  如果你的元件是在客戶端,在瀏覽器中呼叫,那麼所有的工作已經完成;所以是希望透過Codebase的方式釋出,你還需要去搞一個數字簽字,已經不是本文討論的範圍了,就到這裡,結束了。
 
 
參考文件
1、HOWTO: Implement IObjectSafety in Controls(.com/default.x?scid=kb;EN-US;q182598">)
2、Exposing Components to (/html/cpconexposietframeworkcomponentstocom.asp">)
3、System.Runtime.InteropServices()

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-962632/,如需轉載,請註明出處,否則將追究法律責任。

CSharp Tips:讓DotNet實現的COM物件支援IObjectSafety介面 (轉)
請登入後發表評論 登入
全部評論

相關文章