B/S 架構需呼叫客戶端本地資源 (USB Key、印表機、掃描儀、硬體加密裝置) 時,ActiveX 為傳統但有效的解決方案。本文示範以 C# Windows Forms 控制項庫開發、簽署、部署 ActiveX。
using System.Security;
[assembly: AllowPartiallyTrustedCallers()] // 關鍵:允許網頁沙箱呼叫
[assembly: ComVisible(true)]
[Guid("YOUR-GUID-HERE")] // 使用工具 → Create GUID 產生
[ClassInterface(ClassInterfaceType.AutoDual)] // 支援晚期繫結
[ComVisible(true)]
public partial class MyActiveXControl : UserControl
{
// 必須實作 IObjectSafety 讓 IE 信任腳本存取
// 介面定義見下方完整程式碼
}
using System;
using System.Runtime.InteropServices;
[ComImport, Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IObjectSafety
{
[PreserveSig]
int GetInterfaceSafetyOptions(ref Guid riid,
[MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions,
[MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions);
[PreserveSig]
int SetInterfaceSafetyOptions(ref Guid riid,
[MarshalAs(UnmanagedType.U4)] int dwOptionSetMask,
[MarshalAs(UnmanagedType.U4)] int dwEnabledOptions);
}
// 在控制項類別中實作
#region IObjectSafety 實作
private const string IID_IDispatch = "{00020400-0000-0000-C000-000000000046}";
private const string IID_IDispatchEx = "{A6EF9860-C720-11D0-9337-00A0C90DCAA9}";
private const string IID_IPersistStorage = "{0000010A-0000-0000-C000-000000000046}";
private const string IID_IPersistStream = "{00000109-0000-0000-C000-000000000046}";
private const string IID_IPersistPropertyBag = "{37D84F60-42CB-11CE-8135-00AA004BB851}";
private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
private const int S_OK = 0;
private const int E_FAIL = unchecked((int)0x80004005);
private const int E_NOINTERFACE = unchecked((int)0x80004002);
private bool _safeForScripting = true;
private bool _safeForInitializing = true;
public int GetInterfaceSafetyOptions(ref Guid riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions)
{
int hr = E_FAIL;
string strGuid = riid.ToString("B");
pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
switch (strGuid)
{
case IID_IDispatch:
case IID_IDispatchEx:
hr = S_OK;
pdwEnabledOptions = _safeForScripting ? INTERFACESAFE_FOR_UNTRUSTED_CALLER : 0;
break;
case IID_IPersistStorage:
case IID_IPersistStream:
case IID_IPersistPropertyBag:
hr = S_OK;
pdwEnabledOptions = _safeForInitializing ? INTERFACESAFE_FOR_UNTRUSTED_DATA : 0;
break;
default:
hr = E_NOINTERFACE;
break;
}
return hr;
}
public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
{
int hr = E_FAIL;
string strGuid = riid.ToString("B");
switch (strGuid)
{
case IID_IDispatch:
case IID_IDispatchEx:
if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_CALLER) && _safeForScripting)
hr = S_OK;
break;
case IID_IPersistStorage:
case IID_IPersistStream:
case IID_IPersistPropertyBag:
if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_DATA) && _safeForInitializing)
hr = S_OK;
break;
default:
hr = E_NOINTERFACE;
break;
}
return hr;
}
#endregion
public partial class MyActiveXControl : UserControl
{
// 供網頁 JavaScript 呼叫的公共方法
public string ReadUsbKeyCertificate()
{
try
{
// 實際邏輯:操作 CryptoAPI、PKCS#11 等
// 這裡僅示範回傳字串
return "SUCCESS|Certificate data here...";
}
catch (Exception ex)
{
return $"ERROR|{ex.Message}";
}
}
// UI 互動範例:按鈕點擊觸發
private void btnReadCert_Click(object sender, EventArgs e)
{
string result = ReadUsbKeyCertificate();
lblStatus.Text = result;
// 也可觸發事件通知網頁
OnCertificateRead(result);
}
// 定義事件供網頁訂閱
[ComVisible(true)]
public delegate void CertificateReadHandler(string result);
public event CertificateReadHandler CertificateRead;
protected virtual void OnCertificateRead(string result)
{
CertificateRead?.Invoke(result);
}
}
| 現象 | 原因 | 解決方案 |
|---|---|---|
| 網頁顯示 "無法建立物件" / 空白 | CLSID 不符、未註冊、架構不符 (x86/x64) | 確認 classid 指定正確 GUID、以管理員執行 `regasm /codebase Your.dll`、目標 IE 位元數與 DLL 一致 |
| JavaScript 呼叫方法報 "存取被拒絕" | 未實作 IObjectSafety 或 AllowPartiallyTrustedCallers 缺失 | 檢查 AssemblyInfo.cs 及介面實作完整性 |
| 部署專案建置錯誤 regcap.exe | VS 2015/2017 已知 Bug | 用 VS 2019+ 或複製 VS 2022 的 regcap.exe 覆蓋 |
| 客戶端安裝需 .NET Framework | ActiveX 依賴 CLR | 安裝包勾選 .NET Framework 4.8 必要條件,或引導使用者安裝 |
⚠️ ActiveX 僅支援 IE/Edge IE 模式,現代專案強烈建議採用上述替代方案。