winform excel导入--自带office.interop.excel方式

用npoi方式,遇到一个问题,有的excel用加密软件(盖章,只读等)生成的解析不了,所以换成自带的方式,可以解决。

需要引用系统自带Microsoft.office.interop.excel

public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string f = @"D:kintest.xlsx";
            CreateExcelFile(f);
            WriteToExcel(f, "file2", "编号2", "姓名2");

        }
        private void CreateExcelFile(string FileName)
        {
            if (File.Exists(FileName))
            {
                File.Delete(FileName);
            }
            //create
            object Nothing = System.Reflection.Missing.Value;
            var app = new Microsoft.Office.Interop.Excel.Application();
            app.Visible = false;
            Microsoft.Office.Interop.Excel.Workbook workBook = app.Workbooks.Add(Nothing);
            Microsoft.Office.Interop.Excel.Worksheet worksheet = (Microsoft.Office.Interop.Excel.Worksheet)workBook.Sheets[1];
            worksheet.Name = "Work";
            //headline
            worksheet.Cells[1, 1] = "员工编号";
            worksheet.Cells[1, 2] = "姓名";
            worksheet.Cells[1, 3] = "A卡";
            worksheet.Cells[1, 4] = "B卡";
            worksheet.Cells[1, 5] = "开通餐卡";
            worksheet.Cells[1, 6] = "备注";

            worksheet.SaveAs(FileName, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, Type.Missing, Type.Missing, Type.Missing);
            workBook.Close(false, Type.Missing, Type.Missing);
            app.Quit();
            KillExcel.Kill(new IntPtr(app.Hwnd));

            app = null;
        }
        private void WriteToExcel(string excelName, string filename, string findString, string replaceString)
        {
            //open
            object Nothing = System.Reflection.Missing.Value;
            var app = new Microsoft.Office.Interop.Excel.Application();
            app.Visible = false;
            Microsoft.Office.Interop.Excel.Workbook mybook = app.Workbooks.Open(excelName, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing);
            Microsoft.Office.Interop.Excel.Worksheet mysheet = (Microsoft.Office.Interop.Excel.Worksheet)mybook.Worksheets[1];
            mysheet.Activate();
            //get activate sheet max row count
            int maxrow = mysheet.UsedRange.Rows.Count + 1;
            mysheet.Cells[maxrow, 1] = filename;
            mysheet.Cells[maxrow, 2] = findString;
            mysheet.Cells[maxrow, 3] = replaceString;
            mybook.Save();
            mybook.Close(false, Type.Missing, Type.Missing);
            mybook = null;
            //quit excel app
            app.Quit();
            KillExcel.Kill(new IntPtr(app.Hwnd));

            app = null;
        }

        private void OpenExcel(string strFileName)
        {
            object missing = System.Reflection.Missing.Value;
            Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();//lauch excel application
            if (excel == null)
            {
                //Response.Write("<script>alert('Can't access excel')</script>");
            }
            else
            {
                excel.Visible = false; excel.UserControl = true;
                // 以只读的形式打开EXCEL文件
                Workbook wb = excel.Application.Workbooks.Open(strFileName, missing, false, missing, missing, missing,
                 missing, missing, missing, true, missing, missing, missing, missing, missing);
                //取得第一个工作薄
                //Worksheet ws = (Worksheet)wb.Worksheets.get_Item(2);
                Worksheet ws = (Worksheet)excel.ActiveSheet;

                //取得总记录行数   (包括标题列)
                int rowsint = ws.UsedRange.Cells.Rows.Count; //得到行数
                //int columnsint = mySheet.UsedRange.Cells.Columns.Count;//得到列数


                //取得数据范围区域 (不包括标题列) 
                Range rng1 = ws.Cells.get_Range("B2", "B" + rowsint);   //item

                Range rng2 = ws.Cells.get_Range("E2", "E" + rowsint); //Customer
                object[,] arryItem = (object[,])rng1.Value2;   //get range's value
                object[,] arryCus = (object[,])rng2.Value2;
                //将新值赋给一个数组
                string[,] arry = new string[rowsint - 1, 2];
                for (int i = 1; i <= rowsint - 1; i++)
                {
                    //Item_Code列
                    //arry[i - 1, 0] = arryItem[i, 1].ToString();
                    //Customer_Name列
                    //arry[i - 1, 1] = arryCus[i, 1].ToString();

                    string i1 = arryItem[i, 1] == null ? "" : arryItem[i, 1].ToString();
                    string i2 = arryCus[i, 1] == null ? "" : arryCus[i, 1].ToString();
                    Console.WriteLine(i1 + "--" + i2);
                }
                //Response.Write(arry[0, 0] + " / " + arry[0, 1] + "#" + arry[rowsint - 2, 0] + " / " + arry[rowsint - 2, 1]);
                Console.WriteLine("wb.ReadOnly--" + wb.ReadOnly);
                wb.Close();
            }
   
            excel.DisplayAlerts = false;
            excel.Quit();
            KillExcel.Kill(new IntPtr(excel.Hwnd));
     
            excel = null;
            ////Process[] procs = Process.GetProcessesByName("excel");

            ////foreach (Process pro in procs)
            ////{
            ////    pro.Kill();//没有更好的方法,只有杀掉进程
            ////}
    
            
        }

        //选择excel
        private void button3_Click(object sender, EventArgs e)
        {
            OpenFileDialog f = new OpenFileDialog();
            f.Multiselect = true;
            f.ShowDialog();
            string[] filenames = f.FileNames;

            if (filenames.Length > 0)
            {
                this.label1.Text = filenames[0];
                string strFile = System.IO.Path.GetFileName(filenames[0]);
                System.Data.DataTable dt = GetDataBySQLNew("McsDW", "select 1 from [McsDW].[dbo].[UserGateWayNumber] where ExcelFileName='" + strFile + "'");
                if (dt != null)
                {
                    if (dt.Rows.Count > 0)
                    {
                        MessageBox.Show("数据库里已经导入名称为 " + strFile + " 的excel表,请核对!");
                        this.label1.Text = "";
                    }
                }
            }
        }

        //对比数据
        private void button2_Click(object sender, EventArgs e)
        {
            if (this.label1.Text.Length < 1)
            {
                MessageBox.Show("请先选择excel文件!");
                return;
            }
            string sheetIndex = texBoxSheet.Text;
            int sheetInt = 1;
            int.TryParse(sheetIndex, out sheetInt);
            if (sheetInt < 1)
            {
                MessageBox.Show("sheet序号请填写整数!");
                return;
            }

            ReadFromExcelFile(this.label1.Text, sheetInt);
        }

        private void ReadFromExcelFile(string filePath, int sheetInt)
        {
            string extension = System.IO.Path.GetExtension(filePath);
            try
            {
                object missing = System.Reflection.Missing.Value;
                Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();//lauch excel application
                if (excel == null)
                {
                    //Response.Write("<script>alert('Can't access excel')</script>");
                }
                else
                {
                    excel.Visible = false; excel.UserControl = true;
                    // 以只读的形式打开EXCEL文件
                    Workbook wb = excel.Application.Workbooks.Open(filePath, missing, false, missing, missing, missing,
                     missing, missing, missing, true, missing, missing, missing, missing, missing);
                    //取得第一个工作薄
                    //Worksheet ws = (Worksheet)wb.Worksheets.get_Item(2);
                    Worksheet ws = (Worksheet)excel.ActiveSheet;

                    //取得总记录行数   (包括标题列)
                    int rowsint = ws.UsedRange.Cells.Rows.Count; //得到行数
                    //int columnsint = mySheet.UsedRange.Cells.Columns.Count;//得到列数

                    //取得数据范围区域 (不包括标题列) 
                    Range rngxuhao = ws.Cells.get_Range("A3", "A" + rowsint);
                    Range rngdanwei = ws.Cells.get_Range("B3", "B" + rowsint);
                    Range rngbumen = ws.Cells.get_Range("C3", "C" + rowsint);
                    Range rngbianhao = ws.Cells.get_Range("D3", "D" + rowsint);
                    Range rngname = ws.Cells.get_Range("E3", "E" + rowsint);
                    Range rngAka = ws.Cells.get_Range("F3", "F" + rowsint);
                    Range rngBka = ws.Cells.get_Range("G3", "G" + rowsint);
                    Range rngkai = ws.Cells.get_Range("H3", "H" + rowsint);

                    object[,] arryxuhao = (object[,])rngxuhao.Value2;   //get range's value
                    object[,] arrydanwei = (object[,])rngdanwei.Value2;
                    object[,] arrybumen = (object[,])rngbumen.Value2;
                    object[,] arrybianhao = (object[,])rngbianhao.Value2;
                    object[,] arryname = (object[,])rngname.Value2;
                    object[,] arryAka = (object[,])rngAka.Value2;
                    object[,] arryBka = (object[,])rngBka.Value2;
                    object[,] arrykai = (object[,])rngkai.Value2;

                    //将新值赋给一个数组
                    string[,] arry = new string[rowsint - 1, 2];
                    string userCount = "";
                    Dictionary<string, List<string>> userDic = new Dictionary<string, List<string>>();
                    for (int i = 1; i <= rowsint - 1; i++)
                    {
                        //Item_Code列
                        //arry[i - 1, 0] = arryItem[i, 1].ToString();
                        //Customer_Name列
                        //arry[i - 1, 1] = arryCus[i, 1].ToString();

                        string iA = arryxuhao[i, 1] == null ? "" : arryxuhao[i, 1].ToString();
                        string iB = arrydanwei[i, 1] == null ? "" : arrydanwei[i, 1].ToString();
                        string iC = arrybumen[i, 1] == null ? "" : arrybumen[i, 1].ToString();
                        string iD = arrybianhao[i, 1] == null ? "" : arrybianhao[i, 1].ToString();
                        string iE = arryname[i, 1] == null ? "" : arryname[i, 1].ToString();
                        string iF = arryAka[i, 1] == null ? "" : arryAka[i, 1].ToString();
                        string iG = arryBka[i, 1] == null ? "" : arryBka[i, 1].ToString();
                        string iH = arrykai[i, 1] == null ? "" : arrykai[i, 1].ToString();

                        if (iA.Contains("计数"))
                        {
                            int uxuhaoInt = 0;
                            int.TryParse(iH, out uxuhaoInt);

                            userCount = uxuhaoInt.ToString();
                        }
                        if (iA.Equals("") || iA.Equals("序号"))
                        {
                            continue;
                        }
                        if (iD.Equals(""))
                        {
                            continue;
                        }
                        if (iA.Contains("计数") || iA.Contains("负责人") || iA.Contains("开通餐卡"))
                        {
                            break;
                        }

                        List<string> ulist = new List<string>();
                        ulist.Add(iB);
                        ulist.Add(iC);
                        ulist.Add(iE);
                        ulist.Add(iH);
                        ulist.Add(iF);
                        ulist.Add(iG);
                        userDic.Add(iD, ulist);

                        Console.WriteLine(iA + "--" + iB + "--" + iC + "--" + iD + "--" + iE + "--" + iF + "--" + iG + "--" + iH);
                    }
                    //Response.Write(arry[0, 0] + " / " + arry[0, 1] + "#" + arry[rowsint - 2, 0] + " / " + arry[rowsint - 2, 1]);
                    Console.WriteLine("wb.ReadOnly--" + wb.ReadOnly);
                    wb.Close();

                    if (userCount.Equals("") && userCount.Equals("0"))
                        userCount = "没有计数";
                    DoUser(userDic, filePath, userCount, sheetInt);
                }
                
                excel.DisplayAlerts = false;
                excel.Quit();
                KillExcel.Kill(new IntPtr(excel.Hwnd));

                excel = null;
            }

            catch (Exception e)
            {
                //只在Debug模式下才输出
                Console.WriteLine(e.Message);
                MessageBox.Show(e.Message);
            }
        }

        private void DoUser(Dictionary<string, List<string>> userDic, string filePath, string userCount, int sheetInt)
        {
            if (userDic.Count < 1)
                return;
            string namev = "";
            string tip = "";
            System.Data.DataTable dtNew = new System.Data.DataTable();
            try
            {
                //
                System.Data.DataTable dt = GetDataBySQLNew("McsDW", "select * from UserViewAll_DW ");

                dtNew.Columns.Add("员工编号", Type.GetType("System.String"));
                dtNew.Columns.Add("姓名", Type.GetType("System.String"));
                dtNew.Columns.Add("A卡", Type.GetType("System.String"));
                dtNew.Columns.Add("B卡", Type.GetType("System.String"));
                dtNew.Columns.Add("开通餐卡", Type.GetType("System.String"));
                dtNew.Columns.Add("备注", Type.GetType("System.String"));

                string sql = "", sqlabnull = "";
                string remark = "";
                int passCount = 0;
                System.Data.DataTable dtNewABNull = dtNew.Clone();

                foreach (var d in userDic)
                {
                    //Console.Write(d.Key+"----"+d.Value);
                    //Console.WriteLine("
");
                    //namev = d.Value;
                    remark = "";
                    bool isExcel = false;
                    DataRow[] rows = dt.Select("PostionLevel='" + d.Key + "'");
                    //if (d.Value[3].Trim().Equals("否"))
                    //    continue;
                    //开通餐卡为空,默认为不开通
                    if (d.Value[3].Trim().Equals(""))
                    {
                        //Console.WriteLine(d.Key + "----" + d.Value + "----开通餐卡为空");
                        //isExcel = true;
                        //remark = "开通餐卡为空!";
                        continue;
                    }
                    //开通餐卡,填的不是A,B
                    if (!(d.Value[3].Trim().ToUpper().Equals("A") || d.Value[3].Trim().ToUpper().Equals("B")))
                    {
                        isExcel = true;
                        remark += "开通餐卡项填写错误,填的不是A,B";
                    }
                    //如果开通A卡但是A卡为空,B卡不为空,也提示
                    else if (d.Value[3].Trim().ToUpper().Equals("A") && d.Value[4].Trim().Equals("") && !d.Value[5].Trim().Equals(""))
                    {
                        isExcel = true;
                        remark += "需要开通A卡但是A卡为空,B卡不为空,请填B卡";
                    }
                    //如果开通B卡但是B卡为空,A卡不为空,也提示
                    else if (d.Value[3].Trim().ToUpper().Equals("B") && d.Value[5].Trim().Equals("") && !d.Value[4].Trim().Equals(""))
                    {
                        isExcel = true;
                        remark += "需要开通B卡但是B卡为空,A卡不为空,请填A卡";
                    }
                    else if (rows == null)
                    {
                        isExcel = true;
                        remark += "数据库为空";
                    }
                    else if (rows.Length < 1)
                    {
                        isExcel = true;
                        remark += "数据库找不到该用户";
                    }
                    else
                    {
                        //判断用户名,中文名字
                        if (!(d.Value[2].Trim().Equals(rows[0]["UserDisplayName"].ToString().Trim())))
                        {
                            isExcel = true;
                            remark += "数据库里该用户名不匹配:" + rows[0]["UserDisplayName"].ToString();
                        }
                        //如果开通A卡,判断A卡和数据库是否匹配
                        if (d.Value[3].Trim().ToUpper().Equals("A") && !(d.Value[4].Trim().Equals(rows[0]["GatewayNumber"].ToString().Trim())))
                        {
                            isExcel = true;
                            remark += "开通A卡,但数据库里该用户A卡不匹配:" + rows[0]["GatewayNumber"].ToString();
                        }
                        //如果开通B卡,判断B卡和数据库是否匹配
                        if (d.Value[3].Trim().ToUpper().Equals("B") && !(d.Value[5].Trim().Equals(rows[0]["GatewayNumber2"].ToString().Trim())))
                        {
                            isExcel = true;
                            remark += "开通B卡,但数据库里该用户B卡不匹配:" + rows[0]["GatewayNumber2"].ToString();
                        }
                        //string kastr = GetKa(rows[0]["GatewayNumber2"], rows[0]["GatewayNumber"]);
                        //if (kastr.Equals("") || kastr.Equals("没有卡号"))
                        //{
                        //    isExcel = true;
                        //    remark += "数据库里该用户卡号有误:" + kastr;
                        //}
                    }

                    string Gender = GetGender(d.Key);
                    //如果A卡和B卡都为空,也提示
                    if (d.Value[4].Trim().Equals("") && d.Value[5].Trim().Equals(""))
                    {
                        string abnull = "A,B卡都为空,请尽快启动办卡流程";
                        dtNewABNull.Rows.Add(new object[] { d.Key, d.Value[2], d.Value[4], d.Value[5], d.Value[3], abnull });

                        sqlabnull += string.Format("  insert into UserGateWayNumberABNull(UnitName,DepartmentName,PostionLevel,UserName,GatewayNumber,ExcelFileName,AorBcard,Remark,Gender) values('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}')",
                            d.Value[0], d.Value[1], d.Key, d.Value[2], "", System.IO.Path.GetFileName(filePath), d.Value[3], abnull, Gender);
                        passCount++;
                        continue;
                    }

                    if (isExcel)
                    {
                        DataRow r = dtNew.NewRow();
                        r["员工编号"] = d.Key;
                        r["姓名"] = d.Value[2];
                        r["A卡"] = d.Value[4];
                        r["B卡"] = d.Value[5];
                        r["开通餐卡"] = d.Value[3];
                        r["备注"] = remark;

                        dtNew.Rows.Add(r);
                    }
                    else
                    {
                        string gNum = "";
                        if (d.Value[3].Trim().ToUpper().Equals("A"))
                            gNum = d.Value[4];
                        else
                            gNum = d.Value[5];

                        sql += string.Format("  insert into UserGateWayNumber(UnitName,DepartmentName,PostionLevel,UserName,GatewayNumber,ExcelFileName,AorBcard,Gender) values('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}')",
                            d.Value[0], d.Value[1], d.Key, d.Value[2], gNum, System.IO.Path.GetFileName(filePath), d.Value[3], Gender);
                        passCount++;
                    }
                }
                
                int ucount = 0;
                int.TryParse(userCount, out ucount);
                if (dtNew.Rows.Count > 0)
                {
                    //DataRow r = dtNew.NewRow();
                    //r["员工编号"] = filePath;
                    //r["姓名"] = "";
                    //r["A卡"] = "";
                    //r["B卡"] = "";
                    //r["开通餐卡"] = "";
                    //r["备注"] = "";

                    //dtNew.Rows.Add(r);
                    //加入AB都为空的用户
                    foreach (DataRow ro in dtNewABNull.Rows)
                    {
                        dtNew.Rows.Add(ro.ItemArray);
                    }
                    //加入excel名
                    dtNew.Rows.Add(new object[] { filePath, "", "", "", "", "" });

                    DataTableToExcel2(dtNew);
                    tip = "总共处理了" + userDic.Count + "条数据,其中异常的有" + (dtNew.Rows.Count - 1).ToString() + "条。请查看D:/食堂餐卡统计.xlsx";
                }
                else if (ucount != passCount && !userCount.Equals("没有计数"))
                {
                    tip = "总共处理了" + userDic.Count + "条数据,其中合格数据有" + passCount + "条, excel表中计数的有" + ucount + "条。数据不匹配,请核查!";
                }
                else
                {
                    GetDataBySQLNew("McsDW", sql + sqlabnull);
                    tip = "总共处理了" + userDic.Count + "条数据,其中合格数据有" + (passCount - dtNewABNull.Rows.Count).ToString() + "条,数据已经录入到[McsDW].[dbo].[UserGateWayNumber]表中, AB卡都为空但是需要开通卡的用户数据有" + dtNewABNull.Rows.Count + "条,此数据录入到表UserGateWayNumberABNull";
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message + "--" + namev);
            }

            Console.Write("总共" + userDic.Count);
            MessageBox.Show(tip);
        }
        private string GetGender(string pl)
        {
            string sql = "select Gender from [McsDW].[dbo].[UserViewAll_DW] where PostionLevel='" + pl + "'";
            System.Data.DataTable dt = GetDataBySQLNew("McsDW", sql);
            if (dt == null || dt.Rows.Count < 1)
                return "";
            return dt.Rows[0][0] == null ? "" : dt.Rows[0][0].ToString();
        }
        private bool DataTableToExcel2(System.Data.DataTable dt)
        {
            bool result = false;

            string f = @"D:食堂餐卡统计.xlsx";
            CreateExcelFile(f);
            WriteToExcel(f, dt);

            return result;
        }
        private void WriteToExcel(string excelName, System.Data.DataTable dt)
        {
            //open
            object Nothing = System.Reflection.Missing.Value;
            var app = new Microsoft.Office.Interop.Excel.Application();
            app.Visible = false;
            Microsoft.Office.Interop.Excel.Workbook mybook = app.Workbooks.Open(excelName, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing);
            Microsoft.Office.Interop.Excel.Worksheet mysheet = (Microsoft.Office.Interop.Excel.Worksheet)mybook.Worksheets[1];
            mysheet.Activate();
            //get activate sheet max row count
            int maxrow = mysheet.UsedRange.Rows.Count + 1;
            for (int i = maxrow; i < dt.Rows.Count+maxrow; i++)
            {
                mysheet.Cells[i, 1] = dt.Rows[i-maxrow][0];
                mysheet.Cells[i, 2] = dt.Rows[i - maxrow][1];
                mysheet.Cells[i, 3] = dt.Rows[i - maxrow][2];
                mysheet.Cells[i, 4] = dt.Rows[i - maxrow][3];
                mysheet.Cells[i, 5] = dt.Rows[i - maxrow][4];
                mysheet.Cells[i, 6] = dt.Rows[i - maxrow][5];
            }
            
            mybook.Save();
            mybook.Close(false, Type.Missing, Type.Missing);
            mybook = null;
            //quit excel app
            app.Quit();
            KillExcel.Kill(new IntPtr(app.Hwnd));

            app = null;
        }
    
        private System.Data.DataTable GetDataBySQLNew(string database, string sql)
        {
            System.Data.DataTable dt = new System.Data.DataTable();
            string strServer = "Server=127.0.0.0; DataBase=McsDW; user id=sqluser; password=Pass@word123;";
            if (database.Equals("McsDW"))
            {
                strServer = "Server=127.0.0.0; DataBase=McsDW; user id=sqluser; password=Pass@word123;";
            }
            else if (database.Equals("McsSP"))
            {
                strServer = "Server=127.0.0.0; DataBase=McsSP; user id=sqluser; password=Pass@word123;";
            }
            using (System.Data.SqlClient.SqlConnection SqlCnn = new System.Data.SqlClient.SqlConnection(strServer))
            {
                SqlCnn.Open();
                System.Data.SqlClient.SqlDataAdapter SqlDa = new System.Data.SqlClient.SqlDataAdapter(sql, SqlCnn);
                DataSet DS = new DataSet();
                SqlDa.Fill(DS);
                SqlDa.Dispose();
                SqlCnn.Close();
                if (DS == null || DS.Tables == null || DS.Tables.Count < 1)
                    return null;
                dt = DS.Tables[0];
            }
            return dt;
        }

这种方式的话,导入一个excel,进程里就多了一个excel进程,必须kill掉,找到进程ID再kill

 1 public class KillExcel
 2     {
 3         [DllImport("User32.dll", CharSet = CharSet.Auto)]
 4         public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);
 5 
 6 
 7         /// <summary>
 8         /// 强制关闭当前Excel进程
 9         /// </summary>
10         public static void Kill(IntPtr intPtr)
11         {
12             try
13             {
14                 Process[] ps = Process.GetProcesses();
15                 int ExcelID = 0;
16                 GetWindowThreadProcessId(intPtr, out ExcelID); //得到本进程唯一标志k   
17                 foreach (Process p in ps)
18                 {
19                     if (p.ProcessName.ToLower().Equals("excel"))
20                     {
21                         if (p.Id == ExcelID)
22                         {
23                             p.Kill();
24                         }
25                     }
26                 }
27             }
28             catch
29             {
30                 //不做任何处理   
31             }
32         }
33     }
原文地址:https://www.cnblogs.com/hpbkin/p/7144344.html