c# ActiveX 控件的开发

版权所有,禁止匿名转载;禁止商业使用。

关于ActiveX控件的开发,网上很多例子,昨天也整整研究一天才捋顺了.


网上大部分例子都是js调用控件的方法,由于要实现在html页面"相应"控件的事件,整整折腾一天.


关键点在于 "创建ActiveX控件" 的 第2,和第7


该技术局限性较大,如浏览器端需安装 .net 框架,仅限于IE浏览器.


关于ActiveX的证书及浏览器安装时设置,可参考 http://www.cnblogs.com/weixing/archive/2013/06/28/3161165.html 这也是我看到比较详细的介绍了.


创建ActiveX控件


1.创建一个类库;


2.项目属性-应用程序-程序集信息-"使程序集COM可见"勾上;


3.项目属性-生成-"为COM互操作注册"勾上.(这个折腾一天,否则注册事件不可用);


4.创建接口: IObjectSafety (注意GUID不能变);


5.创建ActiveX控件的基类并实现IObjectSafety,ActiveX控件可以继承它来减少代码;


6.创建一个ActiveX自定义控件如:ActiveXDemo1;


7.定义ActiveXDemo1的"方法接口"及"事件接口".(如使用自定义事件需用此方式), "事件接口"成员应加上[DispId(x)]标识;


8.创建ActiveX控件完成.


IObjectSafety  接口定义


[ComImport, Guid("4A359FBB-C9A4-494E-B048-AC068DB4FCB2")] //该GUID不能变
  [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);
  }
}

ActiveX控件基类(ActiveXControlBase)


public class ActiveXControlBase : IObjectSafety
  {
    #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 _fSafeForScripting = true;
    private bool _fSafeForInitializing = true;
    public int GetInterfaceSafetyOptions(ref Guid riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions)
    {
      int Rslt = E_FAIL;
      string strGUID = riid.ToString("B");
      pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
      switch (strGUID)
      {
        case _IID_IDispatch:
        case _IID_IDispatchEx:
          Rslt = S_OK;
          pdwEnabledOptions = 0;
          if (_fSafeForScripting == true)
            pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
          break;
        case _IID_IPersistStorage:
        case _IID_IPersistStream:
        case _IID_IPersistPropertyBag:
          Rslt = S_OK;
          pdwEnabledOptions = 0;
          if (_fSafeForInitializing == true)
            pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
          break;
        default:
          Rslt = E_NOINTERFACE;
          break;
      }
      return Rslt;
    }
    public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
    {
      int Rslt = E_FAIL;
      string strGUID = riid.ToString("B");
      switch (strGUID)
      {
        case _IID_IDispatch:
        case _IID_IDispatchEx:
          if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_CALLER) &&
              (_fSafeForScripting == true))
            Rslt = S_OK;
          break;
        case _IID_IPersistStorage:
        case _IID_IPersistStream:
        case _IID_IPersistPropertyBag:
          if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_DATA) &&
              (_fSafeForInitializing == true))
            Rslt = S_OK;
          break;
        default:
          Rslt = E_NOINTERFACE;
          break;
      }
      return Rslt;
    }
    #endregion
  }

自定义ActiveX控件


[ComVisible(true)]
  [Guid("684AAD87-C086-4F27-AE55-941A1AAC7212")]
  [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
  public interface IThreadDemoEvent
  {
    [DispId(1)] //使用事件,必须加上该标识
    void ShowMessage1(string str_Msg);
    [DispId(2)]
    void ShowMessage2(string str_Msg);
  }
  [ComVisible(true)]
  [Guid("4D12136B-9545-423B-A110-B9405ADF8B30")]
  [InterfaceType(ComInterfaceType.InterfaceIsDual)]
  public interface IThreadDemo
  {
    string StartTimer();
    string StopTimer();
  }
  [Guid("2B4FCB85-A3B7-43BD-B104-7380E7F3483F"),
   ClassInterface(ClassInterfaceType.AutoDual),
   ComDefaultInterface(typeof(IThreadDemo)),
   ComSourceInterfaces(typeof(IThreadDemoEvent)),
   ComVisible(true)
  ]
  public class ActivexThreadDemo : ActiveXControlBase, IThreadDemo
  {
    ~ActivexThreadDemo()
    {
      ShowMessage1("释放了啊");
    }
    Thread _th;
    bool _isStop;
    public event ShowMessageHandle ShowMessage1;
    public event ShowMessageHandle ShowMessage2;
    void ThreadMethd()
    {
      while (true)
      {
        Thread.Sleep(3000);
        if (ShowMessage1 != null)
        {
          ShowMessage1.Invoke(DateTime.Now.ToString());
        }
        if (_isStop) break;
      }
      _th.Abort();
      _th = null;
    }
    public string StartTimer()
    {
      if (_th == null)
      {
        _isStop = false;
        _th = new Thread(ThreadMethd);
        _th.IsBackground = false;
        _th.Start();
        return "开起计时";
      }
      if (ShowMessage2 != null)
      {
        ShowMessage2("执行了 StartTimer");
      }
      return "已经开起过计时;";
    }
    public string StopTimer()
    {
      if (_isStop)
      {
        return "已经停止计时了!";
      }
      else
      {
        _isStop = true;
        return "停止计时";
      }
    }
  }

注意:


不能使用泛型委托来声明事件,如:public event Action<T> ShowMessageHandle;


当类里面包含 static成员,刷新页面不会清空


跨线程触发事件: [事件].Invoke(参数1,参数2 ...);


ActiveX控件Setup


1.创建Installer项目;


2.右击项目 添加->项目输出 打开添加项目输出组对话框,并选择ActiveX控件类库;


3.主输出文件的属性 Register 值为 vsdrpCOM (关键),RemovePreviousVersions 设置为true


web页面测试;


1.创建一个object 标签,calassid为控件GUID

<object id="ActiveXObj1" classid="clsid:3BCF612C-91CF-4543-83BB-FD2331FDDCB6" ></object>


2.调用控件方法


var r = document.ActiveXObj1.Test1();


3."注册控件的事件"


<script language="javascript" type="text/javascript" for="ActiveXObj1" event="ShowMessage1(msg)">
alert("ActiveXObj1 :"+ msg )
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>ActiveX测试页面</title>
  <script type="text/javascript">
    function test1() {
      var r = document.ActiveXObj1.Test1();
      window.status = r;
    }
    function StartTimer() {
      alert(document.ActiveThreadEvent);
      var r = document.ActiveThreadEvent.StartTimer();
      window.status = r;
    }
    function StopTimer() {
      var r = document.ActiveThreadEvent.StopTimer();
      window.status = r;
    }
  </script>
  <!--事件的注册-->
  <script language="javascript" type="text/javascript" for="ActiveXObj1" event="ShowMessage1(msg)">   
    alert("ActiveXObj1 :"+ msg )
  </script>
   <script language="javascript" type="text/javascript" for="ActiveXObj1" event="ShowMessage2(msg)">   
    alert("ActiveXObj1:" + msg)
  </script>
  <!--线程事件注册-->
  <script language="javascript" type="text/javascript" for="ActiveThreadEvent" event="ShowMessage1(msg)">   
    alert("ActiveThreadEvent :"+ msg )
  </script>
   <script language="javascript" type="text/javascript" for="ActiveThreadEvent" event="ShowMessage2(msg)">   
    alert("ActiveThreadEvent:" + msg)
  </script>
</head>
<body> 
 <object id="ActiveXObj1" classid="clsid:3BCF612C-91CF-4543-83BB-FD2331FDDCB6" ></object>
 <br />
 <object id="ActiveThreadEvent" classid="clsid:2B4FCB85-A3B7-43BD-B104-7380E7F3483F" ></object>
 <br />
  <br />
  <input type="button" value="测试4-相应事件!" onclick="test1();" /><br />
  <input type="button" value="开始计时!" onclick="StartTimer();" /><br />
  <input type="button" value="停止计时!" onclick="StopTimer();" /><br />
</body>
</html>


0 0