本文將會開始以 C# 實作控制項,讓你可以有開發控制項的經驗。
本文將會開始以 C# 實作控制項,讓你可以有開發控制項的經驗。
準備工作
在我們開始用開發ActiveX控制項前,必須要先做一些準備工作,以便在開發完成時順利測試控制項,最重要的是Internet Explorer的安全性設定,在開發時期基本上我們不會進行元件的安全設定(例如簽章),為了要讓測試工作可以順利,我們要對IE的安全性做一些設定。
首先,我們要先把網站加入受信任的網站,我們可以由控制項的網際網路選項(或是Internet選項)中,在『安全性』頁籤中,選取『信任的網站』,然後按『網站』按鈕:
將本機的網址(本文為http://localhost),加入信任網站中:
加入完成後,一樣在『安全性』頁籤的『信任的網站』,按下『自訂等級』按鈕,會顯示安全等級的細部設定視窗:
請依下表設定安全層級:
選項 |
值 |
下載未簽署的ActiveX控制項 |
提示(Prompt) |
允許程式碼片段 |
提示(Prompt) |
啟始不標示為安全的ActiveX控制項 |
提示(Prompt) |
設定好以後,我們就可以來開始開發我們的控制項了。
使用C#開發ActiveX控制項-基本功能
本文將使用C#以及Visual Studio 2008作為開發環境,但這個方法可以在Visual Studio 2005以後的開發工具使用(但我只試過VS2008)。同時,我不會在這裡說明細部的操作程序,因此我假定你對Visual Studio的操作已經有基本的瞭解。
首先,先建立一個空白的方案:
然後,在方案中新增一個C#的類別庫專案(我的專案命名是MyControl),這個專案會產生DLL檔案,也是作為控制項的專案之一。您也可以用Windows Forms控制項專案來做,但因為我們不一定會需要使用者介面,因此由類別庫專案來開發會比較簡單。
當專案產生後,請將專案內容打開,並在『建置』頁籤中,將『註冊COM Interop』核取起來後存檔。然後把預設的Class1.cs檔案更名為MyDateControl.cs(若Visual Studio提示更改類別名稱,請接受它),接著將它打開,加入下列程式碼:
using System.Runtime.InteropServices;
namespace MyControl
{
[Guid("C90E96C1-8534-4243-9530-960D9AF982CB")]
[ComVisible(true)]
public class MyDateControl
{
public DateTime Today { get { return DateTime.Today; } }
public string GetTodayDateString() {
return DateTime.Today.ToString("yyyy/MM/dd HH:mm:ss");
}
}
}
這段程式碼會把MyDateControl類別登錄到COM Registry Database中,設定它的GUID,並且開放一個屬性與方法。加入完成後請建置它,此時組件會被登錄,要檢查它是否被登錄,你可以開啟Visual Studio 2008命令提示字元:
並輸入oleview指令:
此時會出現OLE View應用程式視窗,請由左邊展開Type Libraries,你可以在節點中找到MyControl這個節點,表示組件已經登錄到COM Registry Database內:
現在,我們再於方案中新增一個ASP.NET網站或應用程式專案(作法我就不贅述),或是打開一個既有的Web應用程式專案,然後在根目錄中加入一個新的HTML網頁,並加入下列HTML碼:
<html>
<head>
<title></title>
<script language="javascript" type="text/javascript">
function displayDateFromProperty() {
alert(document.getElementById("myControl").Today);
}
function displayDate() {
alert(document.getElementById("myControl").GetTodayDateString());
}
</script>
</head>
<body>
<input type="button" value="Display Property" onclick="displayDateFromProperty()" /><input type="button" value="Invoke Method" onclick="displayDate()" /><br />
<object id="myControl" classid="clsid:C90E96C1-8534-4243-9530-960D9AF982CB" width="0px" height="0px" />
</body>
</html>
此段HTML碼是一個簡單的網頁,並在網頁內放了一個ActiveX控制項,這個控制項的CLSID要和類別所設定的GUID一致,否則IE會無法載入此控制項。在這個網頁中內含了兩個JavaScript指令碼,它會呼叫在控制項內開放的屬性和方法。
NOTE
classid=”clsid:{GUID}”內的GUID值必須要和類別的GUID一致。
現在,我們可以來測試這個控制項了,請用瀏覽器瀏覽這個網頁,你應該會看到這個畫面:
當你按下Display Property和Invoke Method時,應該都會看到顯示今天日期的訊息對話盒,但第一次按時,應該會看到這個訊息:
這表示你目前的控制項是不安全的,要按下『是』,才可以順利執行:
NOTE
我們會在後面告訴你怎麼讓這個交互作用警告的對話盒不再出現。
使用C#開發ActiveX控制項-產生使用者介面
在完成了上面的控制項後,你應該會對用C#開發ActiveX控制項有一點感覺了,我們再來寫一個具有使用者介面的控制項,基本上作法差不多,但這次要新增的是Windows Forms使用者控制項(我的控制項命名為MyDateControlUI)。
新增完成時,Visual Studio會開啟Designer視窗,請在上面放兩個按鈕,如同前一個範例一樣,其中一個是顯示今日的日期(cmdDisplayToday),但另一個會讀取本機上的登錄資料庫(cmdGetOSVer),以取得目前的作業系統資訊:
畫面設定完成後,請開啟控制項的程式碼,加入對COM Interoperability以及Microsoft.Win32的命名空間參考:
using System.Runtime.InteropServices;
using Microsoft.Win32;
請為按鈕填入下列程式碼(分別是cmdDisplayToday按鈕以及cmdGetOSVer按鈕的Click事件常式):
private void cmdDisplayToday_Click(object sender, EventArgs e)
{
MessageBox.Show(DateTime.Today.ToString("yyyy/MM/dd"), "Today");
}
private void cmdGetOSVer_Click(object sender, EventArgs e)
{
RegistryKey key = Registry.LocalMachine.OpenSubKey(
@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");
MessageBox.Show(key.GetValue("BuildLabEx").ToString(), "OS Build Ver");
key = null;
}
然後依照上一步的程序,在類別上加入ComVisible和GUID的設定:
namespace MyControl
{
[Guid("1C267377-9034-4919-A2F8-C1D8961DDDB8")]
[ComVisible(true)]
public partial class MyDateControlUI : UserControl
{
public MyDateControlUI()
{
InitializeComponent();
}
...
}
}
接著,我們修改上一程序所用的HTML網頁,先將原本的<body></body>內的指令移除或註解起來,再加入下面的指令(CLSID值一樣要和類別相同),這裡用的雖然也是<object>標籤,但有設定寬度和高度,以配合控制項。
<object id="myControl" classid="clsid:1C267377-9034-4919-A2F8-C1D8961DDDB8" width="325px" height="97px" />
現在,我們再以瀏覽器來瀏覽此網頁,你看到的應該會是這樣:
同時,按下Display Today會得到今天的日期,而按下Get OS Version,會出現作業系統的Build版本資訊,此版本資訊是來自於登錄資料庫,因此可證明ActiveX控制項具有與作業系統互動的能力(Flash和Silverlight in-browser application就沒辦法做到):
宣告控制項是安全的
我們一開始設計的MyControl控制項在執行時都會出現潛在安全性問題的提示訊息,這是因為Internet Explorer內建的安全性機制使然,就算把提示的設定改為啟用(Enabled),仍然會出現這個訊息,要將此訊息關閉,你必須要在你的控制項實作一個特別的介面,向IE宣告你的控制項與JavaScript互動時是安全的。
NOTE
只要你的控制項會和JavaScript互動,就必須要實作這個介面。
這個介面是IObjectSafety,它是COM原生函式庫內的一個介面,當IE在載入你的控制項時,會在控制項內搜尋是否有這個介面的存在(即控制項是否有實作此介面),如果沒有找到的話,就會顯示潛在安全性問題的訊息,因此我們需要實作這個介面。請在專案內加入一個類別檔案,命名為IObjectSafety.cs檔,並將下列程式加入:
/// <summary>
/// 針對 ActiveX Control 的 IObjectSafety 介面引用。
/// </summary>
[ComImport]
[Guid("CB5BDC81-93C1-11cF-8F20-00805F2CD064")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IObjectSafety
{
/// <summary>
/// 取得物件的安全選項。
/// </summary>
/// <param name="riid">介面代碼。</param>
/// <param name="pdwSupportedOptions">支援的安全性選項。</param>
/// <param name="pdwEnabledOptions">啟用選項。</param>
void GetInterfaceSafetyOptions(int riid, out int pdwSupportedOptions, out int pdwEnabledOptions);
/// <summary>
/// 設定物件的安全選項。
/// </summary>
/// <param name="riid">介面代碼。</param>
/// <param name="pdwSupportedOptions">支援的安全性選項。</param>
/// <param name="pdwEnabledOptions">啟用選項。</param>
void SetInterfaceSafetyOptions(int riid, int pdwSupportedOptions, int pdwEnabledOptions);
}
/// <summary>
/// IObjectSafety 使用的列舉值。
/// </summary>
public class IObjectSafetyEnums
{
/// <summary>
/// 對未受信任的呼叫者宣布此介面為安全。
/// </summary>
public const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x0000001;
/// <summary>
/// 對未受信任的資料宣布此介面為安全。
/// </summary>
public const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x0000002;
}
這個介面有兩個方法:
方法 |
功能 |
GetInterfaceSafetyOptions |
由COM用戶端取得此元件的安全選項。 |
SetInterfaceSafetyOptions |
由COM用戶端設定此元件的安全選項。 |
如果控制項內沒有特別要針對不同的介面做設定,那麼我們可以直接以下列程式實作:
public void GetInterfaceSafetyOptions(int riid, out int pdwSupportedOptions, out int pdwEnabledOptions)
{
pdwSupportedOptions = IObjectSafetyEnums.INTERFACESAFE_FOR_UNTRUSTED_CALLER | IObjectSafetyEnums.INTERFACESAFE_FOR_UNTRUSTED_DATA;
pdwEnabledOptions = IObjectSafetyEnums.INTERFACESAFE_FOR_UNTRUSTED_CALLER | IObjectSafetyEnums.INTERFACESAFE_FOR_UNTRUSTED_DATA;
}
public void SetInterfaceSafetyOptions(int riid, int pdwSupportedOptions, int pdwEnabledOptions)
{
}
如同上面的說明,請在MyControl程式中指示要實作IObjectSafety,並且加入上面的實作,完成的類別程式碼應該會像這樣:
[Guid("C90E96C1-8534-4243-9530-960D9AF982CB")]
[ComVisible(true)]
public class MyDateControl : IObjectSafety
{
public DateTime Today { get { return DateTime.Today; } }
public string GetTodayDateString() { return DateTime.Today.ToString("yyyy/MM/dd HH:mm:ss"); }
public void GetInterfaceSafetyOptions(int riid, out int pdwSupportedOptions, out int pdwEnabledOptions)
{
pdwSupportedOptions = IObjectSafetyEnums.INTERFACESAFE_FOR_UNTRUSTED_CALLER | IObjectSafetyEnums.INTERFACESAFE_FOR_UNTRUSTED_DATA;
pdwEnabledOptions = IObjectSafetyEnums.INTERFACESAFE_FOR_UNTRUSTED_CALLER | IObjectSafetyEnums.INTERFACESAFE_FOR_UNTRUSTED_DATA;
}
public void SetInterfaceSafetyOptions(int riid, int pdwSupportedOptions, int pdwEnabledOptions)
{
}
}
完成後請編譯此組件,然後修改HTML網頁使用MyControl控制項,然後以瀏覽器瀏覽該網頁,並按下網頁中的按鈕,你會發現潛在安全性的訊息不再出現。
(To be continued…)