C#制作ActiveX控件中如何调用海康SDK的问题解决

互联网 17-9-6
  这个事情就是一个坑,耽误了两周时间,之前并没有做过ActiveX这玩意,现在客户需求如此,只能说是在网上看着教程做了。

  事情是这样的,有一台海康威视的摄像头,客户需要一个ActiveX控件嵌入到网页中,通过点击按钮开始录制和结束录制来进行视频的录制和保存,关于海康摄像头的二次开发在此就不多说了,可以参考SDK中的说明。

  直接上流程:

  1.开发环境:

    VS2010,这个打包方便,之前用VS2013打包的,总是调用不了,不知道原因是什么;SDK是32位的,用64位的在Winform中可以正常使用,在网页中使用控件时会报错。

  2.新建项目:

    新建一个类库项目,如下:

    右键点击项目,添加“用户控件”,如下:

    界面拖控件,如下:

    控件代码如下,其中Guid是“工具”->“创建GUID”自动生成的,#region->#endregion折叠部分是实现的IObjectSafety接口

using System;    namespace VideoHelper  {      [System.Security.SecuritySafeCritical]      public class Videos      {          private bool m_initSDK = false;          /// <summary>          /// 正在录制          /// </summary>          private bool m_Record = false;          private uint LastErr = 0;          private Int32 m_RealHandle = -1;          private Int32 m_lUserID = -1;          public IntPtr handle { get; set; }          public bool Initialize(string ip = "192.168.1.64", int port = 8000, string username = "admin", string password = "8910jqk#")          {              try              {                  m_initSDK = CHCNetSDK.NET_DVR_Init();                  if (m_initSDK)                  {                      CHCNetSDK.NET_DVR_SetLogToFile(3, "C:\\SdkLog\\", true);                      //设备参数结构体                      CHCNetSDK.NET_DVR_DEVICEINFO_V30 DeviceInfo = new CHCNetSDK.NET_DVR_DEVICEINFO_V30();                      //注册设备                      m_lUserID = CHCNetSDK.NET_DVR_Login_V30(ip, port, username, password, ref DeviceInfo);                      return m_lUserID >= 0;                  }                  return false;              }              catch (Exception ex)              {                  System.Windows.Forms.MessageBox.Show("Initialize:" + ex.Message);                  return false;              }          }            public bool Start(IntPtr handle, string filename)          {              try              {                  CHCNetSDK.NET_DVR_PREVIEWINFO lpPreviewInfo = new CHCNetSDK.NET_DVR_PREVIEWINFO();                  lpPreviewInfo.lChannel = 1;                  lpPreviewInfo.dwLinkMode = 0;                  lpPreviewInfo.dwStreamType = 0;                  lpPreviewInfo.bBlocked = true;                  lpPreviewInfo.dwDisplayBufNum = 15;                  lpPreviewInfo.hPlayWnd = handle;                  IntPtr pUser = IntPtr.Zero;//new IntPtr();                           //获取实时视频流                  m_RealHandle = CHCNetSDK.NET_DVR_RealPlay_V40(m_lUserID, ref lpPreviewInfo, null, pUser);                  if (m_Record == false)                  {                      CHCNetSDK.NET_DVR_MakeKeyFrame(m_lUserID, 1);                      if (!CHCNetSDK.NET_DVR_SaveRealData(m_RealHandle, filename))                      {                          LastErr = CHCNetSDK.NET_DVR_GetLastError();                          return false;                      }                      else                      {                          m_Record = true;                          return true;                      }                  }                  else                  {                      return false;                  }              }              catch              {                  return false;              }          }            public bool End()          {              if (m_Record)              {                  if (!CHCNetSDK.NET_DVR_StopSaveRealData(m_RealHandle))                  {                      LastErr = CHCNetSDK.NET_DVR_GetLastError();                      return false;                  }                  m_Record = false;                  m_RealHandle = -1;                  return true;              }              else              {                  return false;              }          }            public void Dispose()          {              try              {                  if (m_lUserID >= 0)                  {                      CHCNetSDK.NET_DVR_Logout_V30(m_lUserID);                      m_lUserID = -1;                  }                    if (m_RealHandle >= 0)                  {                      CHCNetSDK.NET_DVR_StopRealPlay(m_RealHandle);                      m_RealHandle = -1;                  }                    CHCNetSDK.NET_DVR_Cleanup();              }              catch              { }          }      }  }    录制视频操作类

录制视频操作类

using System;  using System.Runtime.InteropServices;    namespace VideoHelper  {      [ComImport, GuidAttribute("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]      [InterfaceTypeAttribute(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);      }  }    接口代码
using System;  using System.Windows.Forms;  using System.IO;  using System.Runtime.InteropServices;  namespace VideoHelper  {      [System.Security.SecuritySafeCritical]      [Guid("79629620-3C0C-4D47-B93B-2D36AEF8EF31")]      public partial class VideoControl : UserControl,IObjectSafety      {          public VideoControl()          {              InitializeComponent();          }          string videopath = Environment.CurrentDirectory;          Videos video;          IntPtr handle;          private void btnLogin_Click(object sender, EventArgs e)          {              if (btnLogin.Text == "登录")              {                  try                  {                      if (string.IsNullOrWhiteSpace(this.txtIP.Text))                      {                          MessageBox.Show("IP地址不能为空!");                          return;                      }                      if (string.IsNullOrWhiteSpace(this.txtUserID.Text))                      {                          MessageBox.Show("用户名不能为空!");                          return;                      }                      if (string.IsNullOrWhiteSpace(this.txtPwd.Text))                      {                          MessageBox.Show("密码不能为空!");                          return;                      }                      video = new Videos();                      if (video.Initialize(this.txtIP.Text, Convert.ToInt32(this.numericUpDown1.Value), this.txtUserID.Text, this.txtPwd.Text))                      {                          this.btnLogin.Text = "注销";                          MessageBox.Show("登录成功!");                          this.btnStart.Enabled = true;                          this.btnSave.Enabled = true;                      }                      else                      {                          MessageBox.Show("登录失败!");                      }                  }                  catch (Exception ee)                  {                      MessageBox.Show("登录异常:" + ee.Message);                  }              }              else if (btnLogin.Text == "注销")              {                  try                  {                      video.Dispose();                      this.btnLogin.Text = "登录";                      this.btnStart.Enabled = false;                      this.btnSave.Enabled = false;                  }                  catch (Exception ee)                  {                      MessageBox.Show("注销异常:" + ee.Message);                  }              }          }            private void btnStart_Click(object sender, EventArgs e)          {              try              {                  string filename = txtFile.Text.Trim();                  if (filename.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 || string.IsNullOrWhiteSpace(filename))                  {                      MessageBox.Show("文件名含有非法字符或空格,请重新输入");                      txtFile.Focus();                      return;                  }                  video.Start(handle, filename + comboBox1.SelectedItem.ToString());                  this.btnStart.Enabled = false;                  this.btnSave.Enabled = true;              }              catch (Exception ee)              {                  MessageBox.Show("异常:" + ee.Message);              }          }            private void btnSave_Click(object sender, EventArgs e)          {              try              {                  if (video.End())                  {                      MessageBox.Show("视频已保存!");                      this.btnStart.Enabled = true;                      this.btnSave.Enabled = false;                  }                  else                  {                      MessageBox.Show("保存失败!");                      this.btnStart.Enabled = true;                      this.btnSave.Enabled = true;                  }              }              catch (Exception ee)              { MessageBox.Show("异常:" + ee.Message); }          }            private void button1_Click(object sender, EventArgs e)          {              try              {                  System.Diagnostics.Process.Start(videopath);                }              catch              { }          }            private void VideoControl_Load(object sender, EventArgs e)          {              this.comboBox1.SelectedItem = ".mp4";              this.handle = pictureBox1.Handle;              this.btnStart.Enabled = false;              this.btnSave.Enabled = false;          }            #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      }  }    控件代码
namespace VideoHelper  {      partial class VideoControl      {          /// <summary>           /// 必需的设计器变量。          /// </summary>          private System.ComponentModel.IContainer components = null;            /// <summary>           /// 清理所有正在使用的资源。          /// </summary>          /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>          protected override void Dispose(bool disposing)          {              if (disposing && (components != null))              {                  components.Dispose();              }              base.Dispose(disposing);          }            #region 组件设计器生成的代码            /// <summary>           /// 设计器支持所需的方法 - 不要          /// 使用代码编辑器修改此方法的内容。          /// </summary>          private void InitializeComponent()          {              this.button1 = new System.Windows.Forms.Button();              this.comboBox1 = new System.Windows.Forms.ComboBox();              this.label4 = new System.Windows.Forms.Label();              this.txtFile = new System.Windows.Forms.TextBox();              this.btnSave = new System.Windows.Forms.Button();              this.btnStart = new System.Windows.Forms.Button();              this.btnLogin = new System.Windows.Forms.Button();              this.label3 = new System.Windows.Forms.Label();              this.txtPwd = new System.Windows.Forms.TextBox();              this.label2 = new System.Windows.Forms.Label();              this.txtUserID = new System.Windows.Forms.TextBox();              this.label1 = new System.Windows.Forms.Label();              this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();              this.IP = new System.Windows.Forms.Label();              this.txtIP = new System.Windows.Forms.TextBox();              this.pictureBox1 = new System.Windows.Forms.PictureBox();              ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();              ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();              this.SuspendLayout();              //               // button1              //               this.button1.Cursor = System.Windows.Forms.Cursors.Hand;              this.button1.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));              this.button1.Location = new System.Drawing.Point(377, 360);              this.button1.Name = "button1";              this.button1.Size = new System.Drawing.Size(138, 22);              this.button1.TabIndex = 58;              this.button1.Text = "打开视频存放位置";              this.button1.UseVisualStyleBackColor = true;              this.button1.Click += new System.EventHandler(this.button1_Click);              //               // comboBox1              //               this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;              this.comboBox1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;              this.comboBox1.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));              this.comboBox1.FormattingEnabled = true;              this.comboBox1.Items.AddRange(new object[] {              ".mp4",              ".avi",              ".wmv",              ".3gp",              ".flv"});              this.comboBox1.Location = new System.Drawing.Point(303, 361);              this.comboBox1.Name = "comboBox1";              this.comboBox1.Size = new System.Drawing.Size(55, 25);              this.comboBox1.TabIndex = 57;              //               // label4              //               this.label4.AutoSize = true;              this.label4.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));              this.label4.Location = new System.Drawing.Point(14, 360);              this.label4.Name = "label4";              this.label4.Size = new System.Drawing.Size(116, 17);              this.label4.TabIndex = 56;              this.label4.Text = "请输入视频文件名:";              //               // txtFile              //               this.txtFile.Location = new System.Drawing.Point(136, 360);              this.txtFile.Name = "txtFile";              this.txtFile.Size = new System.Drawing.Size(161, 21);              this.txtFile.TabIndex = 55;              //               // btnSave              //               this.btnSave.Cursor = System.Windows.Forms.Cursors.Hand;              this.btnSave.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));              this.btnSave.Location = new System.Drawing.Point(490, 298);              this.btnSave.Name = "btnSave";              this.btnSave.Size = new System.Drawing.Size(57, 45);              this.btnSave.TabIndex = 54;              this.btnSave.Text = "保存";              this.btnSave.UseVisualStyleBackColor = true;              this.btnSave.Click += new System.EventHandler(this.btnSave_Click);              //               // btnStart              //               this.btnStart.Cursor = System.Windows.Forms.Cursors.Hand;              this.btnStart.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));              this.btnStart.Location = new System.Drawing.Point(421, 298);              this.btnStart.Name = "btnStart";              this.btnStart.Size = new System.Drawing.Size(57, 45);              this.btnStart.TabIndex = 53;              this.btnStart.Text = "录制";              this.btnStart.UseVisualStyleBackColor = true;              this.btnStart.Click += new System.EventHandler(this.btnStart_Click);              //               // btnLogin              //               this.btnLogin.Cursor = System.Windows.Forms.Cursors.Hand;              this.btnLogin.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));              this.btnLogin.Location = new System.Drawing.Point(352, 298);              this.btnLogin.Name = "btnLogin";              this.btnLogin.Size = new System.Drawing.Size(57, 45);              this.btnLogin.TabIndex = 52;              this.btnLogin.Text = "登录";              this.btnLogin.UseVisualStyleBackColor = true;              this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click);              //               // label3              //               this.label3.AutoSize = true;              this.label3.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));              this.label3.Location = new System.Drawing.Point(172, 325);              this.label3.Name = "label3";              this.label3.Size = new System.Drawing.Size(44, 17);              this.label3.TabIndex = 51;              this.label3.Text = "密码:";              //               // txtPwd              //               this.txtPwd.Location = new System.Drawing.Point(221, 322);              this.txtPwd.Name = "txtPwd";              this.txtPwd.PasswordChar = '*';              this.txtPwd.Size = new System.Drawing.Size(115, 21);              this.txtPwd.TabIndex = 50;              this.txtPwd.Text = "8910jqk#";              this.txtPwd.UseSystemPasswordChar = true;              //               // label2              //               this.label2.AutoSize = true;              this.label2.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));              this.label2.Location = new System.Drawing.Point(8, 322);              this.label2.Name = "label2";              this.label2.Size = new System.Drawing.Size(44, 17);              this.label2.TabIndex = 49;              this.label2.Text = "用户名";              //               // txtUserID              //               this.txtUserID.Location = new System.Drawing.Point(66, 322);              this.txtUserID.Name = "txtUserID";              this.txtUserID.Size = new System.Drawing.Size(100, 21);              this.txtUserID.TabIndex = 48;              this.txtUserID.Text = "admin";              //               // label1              //               this.label1.AutoSize = true;              this.label1.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));              this.label1.Location = new System.Drawing.Point(172, 295);              this.label1.Name = "label1";              this.label1.Size = new System.Drawing.Size(44, 17);              this.label1.TabIndex = 47;              this.label1.Text = "端口:";              //               // numericUpDown1              //               this.numericUpDown1.Location = new System.Drawing.Point(222, 295);              this.numericUpDown1.Maximum = new decimal(new int[] {              65535,              0,              0,              0});              this.numericUpDown1.Minimum = new decimal(new int[] {              1,              0,              0,              0});              this.numericUpDown1.Name = "numericUpDown1";              this.numericUpDown1.Size = new System.Drawing.Size(114, 21);              this.numericUpDown1.TabIndex = 46;              this.numericUpDown1.Value = new decimal(new int[] {              8000,              0,              0,              0});              //               // IP              //               this.IP.AutoSize = true;              this.IP.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));              this.IP.Location = new System.Drawing.Point(20, 295);              this.IP.Name = "IP";              this.IP.Size = new System.Drawing.Size(19, 17);              this.IP.TabIndex = 45;              this.IP.Text = "IP";              //               // txtIP              //               this.txtIP.Location = new System.Drawing.Point(66, 295);              this.txtIP.Name = "txtIP";              this.txtIP.Size = new System.Drawing.Size(100, 21);              this.txtIP.TabIndex = 44;              this.txtIP.Text = "192.168.1.64";              //               // pictureBox1              //               this.pictureBox1.Location = new System.Drawing.Point(5, 5);              this.pictureBox1.Name = "pictureBox1";              this.pictureBox1.Size = new System.Drawing.Size(542, 269);              this.pictureBox1.TabIndex = 43;              this.pictureBox1.TabStop = false;              //               // VideoControl              //               this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);              this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;              this.Controls.Add(this.button1);              this.Controls.Add(this.comboBox1);              this.Controls.Add(this.label4);              this.Controls.Add(this.txtFile);              this.Controls.Add(this.btnSave);              this.Controls.Add(this.btnStart);              this.Controls.Add(this.btnLogin);              this.Controls.Add(this.label3);              this.Controls.Add(this.txtPwd);              this.Controls.Add(this.label2);              this.Controls.Add(this.txtUserID);              this.Controls.Add(this.label1);              this.Controls.Add(this.numericUpDown1);              this.Controls.Add(this.IP);              this.Controls.Add(this.txtIP);              this.Controls.Add(this.pictureBox1);              this.Name = "VideoControl";              this.Size = new System.Drawing.Size(556, 398);              this.Load += new System.EventHandler(this.VideoControl_Load);              ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();              ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();              this.ResumeLayout(false);              this.PerformLayout();            }            #endregion            private System.Windows.Forms.Button button1;          private System.Windows.Forms.ComboBox comboBox1;          private System.Windows.Forms.Label label4;          private System.Windows.Forms.TextBox txtFile;          private System.Windows.Forms.Button btnSave;          private System.Windows.Forms.Button btnStart;          private System.Windows.Forms.Button btnLogin;          private System.Windows.Forms.Label label3;          private System.Windows.Forms.TextBox txtPwd;          private System.Windows.Forms.Label label2;          private System.Windows.Forms.TextBox txtUserID;          private System.Windows.Forms.Label label1;          private System.Windows.Forms.NumericUpDown numericUpDown1;          private System.Windows.Forms.Label IP;          private System.Windows.Forms.TextBox txtIP;          private System.Windows.Forms.PictureBox pictureBox1;      }  }    控件设计器代码

控件设计器代码

    至此,此项目结束。

    右键点击解决方案,添加新项目,如下,至于为什么建立两个项目,我一会儿在下面解释,

    在HkHelper项目中添加类CHCNetSDK.cs,此类是海康提供的,可以在官网找到

    接下来,最重要的,项目属性设置如下,两个项目都要设置:

    至此,自定义控件已经完成,接下来就是打包,新建一个安装项目:

    右键点击安装项目,“添加”->“项目输出”,并选择自定义控件的项目,然后确定

    然后添加海康提供的SDK的库文件文件夹下的所有文件和文件夹到项目中,如下:

    然后生成项目,会生成setup.exe和SetupVideo.msi两个文件,然后用打包文件,把这两个文件打包称cab文件就OK了

打包文件一共三个cabarc.exe、build.bat、install.inf

build.bat文件:

"cabarc.exe"  n VideoSetup.cab SetupVideo.msi install.inf

install.inf文件:

[version]  signature="$CHICAGO$"AdvancedINF=2.0[Setup Hooks]  hook1=hook1    [hook1]  run=msiexec.exe /i "%EXTRACT_DIR%\SetupVideo.msi" /qn

cabarc.exe是微软提供的工具

最后说一下为什么要分为两个项目去实现控件,那是因为如果在一个项目中的话,调用海康动态库的类CHCNetSDK.cs不能进行COM注册

以上就是C#制作ActiveX控件中如何调用海康SDK的问题解决的详细内容,更多内容请关注技术你好其它相关文章!

来源链接:
免责声明:
1.资讯内容不构成投资建议,投资者应独立决策并自行承担风险
2.本文版权归属原作所有,仅代表作者本人观点,不代表本站的观点或立场
标签: csharp
上一篇:php获取远程图片并下载保存到本地的方法分析 下一篇:C#中面向对象设计七个原则介绍

相关资讯