C#窗体内嵌EXCEL应用程序,构造二重JSON通过RFC获取SAP系统数据

0.1.1.     目的

为什么要做?

在做接口的时候,多个系统的数据交互,要求对各种字段和表关联比较熟悉,比如这个字段来自哪张表,它使用的数据元素是什么,它是否有固定值,是否有搜索帮助,对应的表有哪些主键。如果对表和字段不熟悉,就要频繁的使用SE11去查询和验证每一个字段是否在对应的表中存在,对于初学者而言,如果不了解SAP系统,则会花费很多时间在找表、验证表等毫无意义和重复性工作之中。

做了什么?

为了解决这个问题,我用C#写了一个内嵌EXCEL的应用程序,通过微软提供的OFFICE类库创建了内嵌的EXCEL,可以直接像操作EXCEL一样操作内嵌的EXCEL,操作EXCEL的每一个事件几乎都能在程序中捕捉到,该程序具有很大的可扩展性。

怎么做的?

将需要查询的表名、字段名、数据元素名、域名填在程序自动生成的模板EXCEL中,点击“获取”,程序会获取EXCEL输入的数据并转换为JSON,将构建好的动态程序JSON结构和参数JSON组成二重JSON传到SAP系统;SAP对二重JSON进行解析,生成对应的类,方法和表结构,并动态调用类方法,将执行的结果重装为二重JSON传回C#;C#对回传的二重JSON进行解析,获取输出参数,并写入到内嵌EXCEL,然后可以对EXCEL数据进行二次操作并保存备份。

具体实现

0.1.1.    ABAP创建一个类,并正常调用

REPORT z14143_14.
CLASS da DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS: getrelationship IMPORTING i_json TYPE string
                                   EXPORTING o_json TYPE string.
ENDCLASS.

CLASS da IMPLEMENTATION.
  METHOD getrelationship.
*定义类型ty_field创建时间06.05.2018 01:28:21
    TYPES: BEGIN OF ty_field,
             tablename   TYPE dd03l-tabname,     "表名
             istable     TYPE dd03l-tabletype,     "是表?
             fieldname   TYPE dd03l-fieldname,    "字段名
             iskey       TYPE dd03l-keyflag,      "主键?
             elementname TYPE dd03l-rollname,  "数据元素
             checktable  TYPE dd03l-checktable, "检查表
             datatype    TYPE dd03l-datatype,     "数据类型
             length      TYPE dd03l-leng,           "长度
             decimals    TYPE dd03l-decimals,     "精度
             domainname  TYPE dd03l-domname,       "域名
             shlporigin  TYPE dd07t-ddtext,
             comptype    TYPE dd07t-ddtext,
           END OF ty_field.

    DATA:lt_field TYPE TABLE OF ty_field,
         ls_field TYPE ty_field.

    DATA:rt_tab TYPE RANGE OF dd03l-tabname,
         rt_fid TYPE RANGE OF dd03l-fieldname,
         rt_ele TYPE RANGE OF dd03l-rollname,
         rt_dom TYPE RANGE OF dd03l-domname.
    DATA:rs_tab LIKE LINE OF rt_tab,
         rs_fid LIKE LINE OF  rt_fid,
         rs_ele LIKE LINE OF  rt_ele,
         rs_dom LIKE LINE OF rt_dom.

    /ui2/cl_json=>deserialize( EXPORTING json = i_json pretty_name = /ui2/cl_json=>pretty_mode-none CHANGING data = lt_field ).
    LOOP AT lt_field INTO ls_field.
      IF ls_field-tablename IS NOT INITIAL.
        rs_tab-low = ls_field-tablename.
        TRANSLATE rs_tab-low TO UPPER CASE.
        rs_tab-option = 'EQ'.
        rs_tab-sign = 'I'.
        APPEND rs_tab TO rt_tab.
      ENDIF.

      IF ls_field-fieldname IS NOT INITIAL.
        rs_fid-low = ls_field-fieldname.
        TRANSLATE rs_fid-low TO UPPER CASE.
        rs_fid-option = 'EQ'.
        rs_fid-sign = 'I'.
        APPEND rs_fid TO rt_fid.
      ENDIF.

      IF ls_field-elementname IS NOT INITIAL.
        rs_ele-low = ls_field-elementname..
        TRANSLATE rs_ele-low TO UPPER CASE.
        rs_ele-option = 'EQ'.
        rs_ele-sign = 'I'.
        APPEND rs_ele TO rt_ele.
      ENDIF.

      IF ls_field-domainname IS NOT INITIAL.
        rs_dom-low = ls_field-domainname..
        TRANSLATE rs_dom-low TO UPPER CASE.
        rs_dom-option = 'EQ'.
        rs_dom-sign = 'I'.
        APPEND rs_dom TO rt_dom.
      ENDIF.
    ENDLOOP.

IF rt_tab is INITIAL and rt_fid is INITIAL and rt_ele is INITIAL and rt_dom is INITIAL.
      o_json = '请输入数据'.
      RETURN.
    ENDIF.

    SELECT dd03l~tabname AS tablename
    tabletype AS istable
    fieldname
    keyflag AS iskey
    rollname AS elementname
    checktable
    datatype
    leng AS length
    decimals
    dd03l~domname AS domainname
    dd07t_1~ddtext AS shlporigin
    dd07t_2~ddtext AS comptype
    FROM dd03l
    LEFT JOIN dd07t AS dd07t_1 ON dd07t_1~domname =  'SHLPORIGIN' AND dd07t_1~ddlanguage =  '1' AND dd07t_1~domvalue_l = dd03l~shlporigin
    LEFT JOIN dd07t AS dd07t_2 ON dd07t_2~domname =  'COMPTYPE' AND dd07t_2~ddlanguage =  '1' AND dd07t_2~domvalue_l = dd03l~comptype
    INTO TABLE lt_field
    WHERE dd03l~tabname IN rt_tab
    AND dd03l~fieldname IN rt_fid
    AND dd03l~rollname IN rt_ele
    AND dd03l~domname IN rt_dom
    AND dd03l~as4local = 'A'.

    IF lt_field IS NOT INITIAL.
      o_json = /ui2/cl_json=>serialize( data = lt_field compress = '' pretty_name = /ui2/cl_json=>pretty_mode-none ).
    ENDIF.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.

  DATA i_json TYPE string.
  DATA o_json TYPE string.
  i_json = '[{"tablename":"makt","istable":"","fieldname":"","iskey":"","elementname":"","checktable":"",' &&
      '"datatype":"","length":"","decimals":"","domainname":"","shlporigin":"","comptype":""}]'.

  CALL METHOD da=>getrelationship
    EXPORTING
      i_json = i_json
    IMPORTING
      o_json = o_json.
cl_demo_output=>display_json( o_json ).

实现的功能就是表名,字段名,数据元素名,域名的联合查询,

0.1.2.    改写成JSON


0.1.3.    C#主要程序

0.1.3.1.    创建一个连接SAP系统的类

usingSAP.Middleware.Connector;

using System;

usingSystem.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace SAPconnector

{

    classConnectSAP

    {

        RfcDestination rfcDest;

        RfcConfigParameters Parms;

        RfcRepository rfcrep;

        IRfcFunction func;

        string FFMName;

        string InputName;

        string OutputName;

        publicclassConnectStruct

        {

            public ConnectStruct(bool p_isconnect, bool p_isconnectsuccessful,string p_connectinfo)

            {

                isconnect = p_isconnect;

                isconnectsuccessful =p_isconnectsuccessful;

                connectinfo = p_connectinfo;

            }

            publicbool isconnect =false;//连接状态:true正在连接,false连接结束

            publicbool isconnectsuccessful =false;//是否成功连接,ture成功,false失败

            publicstring connectinfo ="Without Connection";//连接信息

        };

 

        ConnectStruct connstru;

        delegatevoidSetTextCallback(ConnectStruct info);//具有一个传入参数的委托

        Func<ConnectStruct> func_sys;

        Func<ConnectStruct> func_rfm;

        IAsyncResult cookie;

 

        Form1 form1;

        public ConnectSAP(Form1 form1)

        {

            this.form1 = form1;

        }

        publicvoid XXXXXSystem()

        {

            form1.label1.Text ="Wait...";

            connstru =newConnectStruct(false,false,"Without Connection");

 

            RfcConfigParameters parms =newRfcConfigParameters();

 

            parms.Add(RfcConfigParameters.Name,"XXX");

            parms.Add(RfcConfigParameters.AppServerHost,"XXXXXXXXXXXXXX");

            parms.Add(RfcConfigParameters.SystemNumber,"XX");

            parms.Add(RfcConfigParameters.Client,"XXX");

            parms.Add(RfcConfigParameters.User,"XXXXXXXX");

            parms.Add(RfcConfigParameters.Password,"XXXXXXXXXXXX");

            parms.Add(RfcConfigParameters.Language,"XX");

            parms.Add(RfcConfigParameters.PoolSize,"X");

            parms.Add(RfcConfigParameters.PeakConnectionsLimit,"XX");

            parms.Add(RfcConfigParameters.IdleTimeout,"X");

            parms.Add(RfcConfigParameters.MaxPoolWaitTime,"X");

            parms.Add(RfcConfigParameters.SAPRouter,"XXXXXXXXXXXXXX");

            FFMName ="ZZTEST_WEBSERVICE_02";

            InputName ="ZZIMSEG";

            OutputName ="ZZEMSEG";

            this.SetConnectandRFM(parms);

        }

 

        publicstring ExecuteRFM(string i_param)

        {

            //先检查服务器是否连接成功(需要检查服务器是否真正连接),不成功不允许调用参数

            if (connstru.isconnect ==false)

            {

                return"正在连接RFM中,请勿重复点击,当然我也不会处理!";//正在连接中,直接返回,不让调用RFC函数

            }

            else

            {

                if (connstru.isconnectsuccessful ==false)

                {

                    return"连接RFM失败,请勿重复点击,当然我也不会处理!";//连接失败,直接返回,不让调用RFC函数

                }

            }

            func.SetValue(InputName, i_param);//SAP里面的传入参数

            func.Invoke(rfcDest);

            //return func.GetValue(1).ToString();

            return func.GetString(OutputName);

        }

        publicvoid SetRFM(string name,string inputname,string outputname)

        {

            if (rfcrep ==null)//如果没有创建数据对象仓库

            {

                return;

            }

            if (rfcrep.CachedFunctionMetadata.Count == 0)//如果数据仓库中没有添加函数对象

            {

                return;

            }

            if (rfcrep.CachedFunctionMetadata.Find(s =>s.Name.Equals(name)) !=null)//如果数据仓库中找到函数名为name的对象,则不设置函数

            {

                return;

            }

            FFMName = name;

            InputName = inputname;

            OutputName = outputname;

            func_rfm = SetRFM;//泛型func<out ConnectStruct>设置了一个返回参数,不能用Acion

            cookie = func_rfm.BeginInvoke((IAsyncResult res) => {this.SetLableText(func_rfm.EndInvoke(cookie));},null);//回调函数,当异步调用完成之后触发,第二个参数是传入参数

        }

        privateConnectStruct SetRFM()

        {

            try

            {

                func =rfcrep.CreateFunction(FFMName);//RFM名称

            }

            catch (Exception ex)

            {

                returnnewConnectStruct(true,false, ex.ToString());

            }

            returnnewConnectStruct(true,true,"Connect RFM successful!");

        }

        publicvoid SetConnectandRFM(RfcConfigParameters param)

        {

            Parms = param;

            func_sys = SetConnectandRFM;//泛型func<out string>设置了一个返回参数,不能用Acion

            cookie = func_sys.BeginInvoke((IAsyncResult res) => {this.SetLableText(func_sys.EndInvoke(cookie));},null);//回调函数,当异步调用完成之后触发,第二个参数是传入参数

        }

        privateConnectStruct SetConnectandRFM()

        {

            try

            {

                rfcDest =RfcDestinationManager.GetDestination(Parms);

                rfcrep = rfcDest.Repository;

                func =rfcrep.CreateFunction(FFMName);//RFM名称

            }

            catch (Exception ex)

            {

                returnnewConnectStruct(true,false, ex.ToString());

            }

            returnnewConnectStruct(true,true,"Connect successful!");

        }

        privatevoid SetLableText(ConnectStruct info)

        {

            // InvokeRequired required compares the thread IDof the

            // calling thread to the thread ID of the creatingthread.

            // If these threads are different, it returns true.

            if (this.form1.label1.InvokeRequired)

            {

                SetTextCallback d =newSetTextCallback(SetLableText);

                this.form1.Invoke(d,newobject[] { info });

            }

            else

            {

                this.form1.label1.Text +="\r\n"+ info.connectinfo;

                connstru = info;

            }

        }

    }

}


0.1.3.2.    在主类的载入事件中连接系统和获取数据仓库指定RFM

privatevoid Form1_Load(object sender, EventArgs e)

        {

            connectSAP =newConnectSAP(this);

            connectSAP.XXXXXSystem();

          System.Threading.Thread.Sleep(100);

          connectSAP.SetRFM("ZZTEST_WEBSERVICE_03","I_JSON","O_JSON");

            System.Threading.Thread.Sleep(100);

        }


0.1.3.3.    载入内嵌EXCEL的方法

        

private void载入表字段关联模板ToolStripMenuItem_Click(object sender, EventArgs e)

        {

            app =new Microsoft.Office.Interop.Excel.Application();

            if (app ==null)

            {

                return;

            }

            Workbooks workbooks = app.Workbooks;

            Workbook workbook = workbooks.Add(Type.Missing);

            worksheet = (Worksheet)workbook.Sheets[1];

            worksheet.Select();

           Range range1 =worksheet.get_Range("A1","L1");

            if (range1 ==null)

            {

                return;

            }

            object[,] tabdata =newobject[1, 12];

            tabdata[0, 0] ="表名";

            tabdata[0, 1] ="是表?";

            tabdata[0, 2] ="字段名";

            tabdata[0, 3] ="主键?";

            tabdata[0, 4] ="数据元素";

            tabdata[0, 5] ="检查表";

            tabdata[0, 6] ="数据类型";

            tabdata[0, 7] ="长度";

            tabdata[0, 8] ="精度";

            tabdata[0, 9] ="域名";

            tabdata[0, 10] ="搜索帮助类型";

            tabdata[0, 11] ="数据元素类型";

            range1.Value2 = (dynamic)tabdata;

            range1.Activate();

            IntPtr hwnd = (IntPtr)app.Hwnd;

           SetParent(hwnd,this.Filltable.Handle);

            MoveWindow((IntPtr)app.Hwnd,this.Filltable.ClientRectangle.Left,this.Filltable.ClientRectangle.Top+ 25, this.Filltable.ClientRectangle.Width,this.Filltable.ClientRectangle.Height- 25, false);

            app.EditDirectlyInCell =true;//设置单元格直接可编辑,无意中发现,否者只能双击单元格,十分麻烦

            app.DisplayAlerts =false;

            app.Visible =true;

        }

0.1.3.4.    内嵌EXCEL大小随着父容器大小变化而变化

private void Filltable_Resize(object sender, EventArgs e)

        {

            if (app !=null && (IntPtr)app.Hwnd !=IntPtr.Zero)

            {

                MoveWindow((IntPtr)app.Hwnd,this.Filltable.ClientRectangle.Left,this.Filltable.ClientRectangle.Top+ 25, this.Filltable.ClientRectangle.Width,this.Filltable.ClientRectangle.Height- 25, false);

           }

           

        }


0.1.3.5.    构建动态类方法的类结构

namespace SAPconnector

{

    classProgramStru

    {

        [JsonProperty("METHODS")]

        publicList<METHODSTRU> METHODS { get;set; }

        publicstructMETHODSTRU

        {

            publicstring METHODNAME {get;set; }

            publicList<PARAM> INPUTDATA { get;set; }

            publicList<PARAM> OUTPUTDATA { get;set; }

            publicstring BODY {get;set; }

        }

        publicstructPARAM

        {

            public string DATATYPE {get;set; }

            public string DATANAME {get;set; }

            publicstring DATAVALUE {get;set; }

        }

    }

}


0.1.3.6.    获取和处理内嵌EXCEL数据并调用RFM回写EXCEL

privatevoid获取ToolStripMenuItem_Click(object sender, EventArgs e)

        {

            try

            {

                connectSAP.SetRFM("ZZTEST_WEBSERVICE_03","I_JSON","O_JSON");

                System.Threading.Thread.Sleep(100);

                Range range1 = ((_Worksheet)app.Workbooks.get_Item(1).Worksheets.get_Item(1)).UsedRange;

                range1 = ((_Worksheet)app.Workbooks.get_Item(1).Worksheets.get_Item(1)).UsedRange;

                object[,] data = range1.get_Value();

                TabTableStru.Clear();

                for (int i = 2; i <= data.GetLength(0); i++)

                {

                    indexTableStru = newTableFieldStru();

                    indexTableStru.tablename =convertStr(data[i, 1]);

                    indexTableStru.istable =convertStr(data[i, 2]);

                    indexTableStru.fieldname =convertStr(data[i, 3]);

                    indexTableStru.iskey =convertStr(data[i, 4]);

                    indexTableStru.elementname= convertStr(data[i, 5]);

                    indexTableStru.checktable =convertStr(data[i, 6]);

                    indexTableStru.datatype =convertStr(data[i, 7]);

                    indexTableStru.length =convertStr(data[i, 8]);

                    indexTableStru.decimals =convertStr(data[i, 9]);

                    indexTableStru.domainname =convertStr(data[i, 10]);

                    indexTableStru.shlporigin =convertStr(data[i, 11]);

                    indexTableStru.comptype =convertStr(data[i, 12]);

                   TabTableStru.Add(indexTableStru);

                }

                if (TabTableStru.Count == 0)

                {

                    this.label1.Text +="\r\n" +"获取Excel数据失败";

                    return;

                }

                string AAA =JsonConvert.SerializeObject(TabTableStru);

               

                this.label1.Text = AAA;

                ProgramStru programStru =newProgramStru();

                ProgramStru.PARAM param;

                List<ProgramStru.METHODSTRU> TABMETHODSTRU = newList<ProgramStru.METHODSTRU>();

                ProgramStru.METHODSTRU methods =newProgramStru.METHODSTRU();

                methods.METHODNAME = "METHOD1";

                param =newProgramStru.PARAM();

                param.DATANAME = "I_JSON";

                param.DATATYPE = "STRING";

                param.DATAVALUE = AAA;

                List<ProgramStru.PARAM> TABPARAM = newList<ProgramStru.PARAM>();

                TABPARAM.Clear();

                TABPARAM.Add(param);

                methods.INPUTDATA = TABPARAM;

                param = newProgramStru.PARAM();

                param.DATANAME = "O_JSON";

                param.DATATYPE = "STRING";

                param.DATAVALUE ="";

                TABPARAM = newList<ProgramStru.PARAM>();

                TABPARAM.Clear();

                TABPARAM.Add(param);

                methods.OUTPUTDATA = TABPARAM;

                methods.BODY = "TYPES: BEGIN OFty_field, tablename TYPE dd03l-tabname, istable TYPE dd03l-tabletype, fieldnameTYPE dd03l-fieldname, iskey TYPE dd03l-keyflag, elementname TYPEdd03l-rollname, checktable TYPEdd03l-checktable, datatype TYPE dd03l-datatype, length TYPE dd03l-leng, decimalsTYPE dd03l-decimals, domainname  TYPEdd03l-domname, shlporigin  TYPEdd07t-ddtext, comptype TYPE dd07t-ddtext, END OF ty_field. DATA:lt_field TYPETABLE OF ty_field, ls_field TYPE ty_field. DATA:rt_tab TYPE RANGE OFdd03l-tabname, rt_fid TYPE RANGE OF dd03l-fieldname, rt_ele TYPE RANGE OFdd03l-rollname, rt_dom TYPE RANGE OF dd03l-domname. DATA:rs_tab LIKE LINE OFrt_tab, rs_fid LIKE LINE OF rt_fid, rs_ele LIKE LINE OF  rt_ele, rs_dom LIKE LINE OF rt_dom. /ui2/cl_json=>deserialize(EXPORTING json = i_json pretty_name = /ui2/cl_json=>pretty_mode-noneCHANGING data = lt_field ). LOOP AT lt_field INTO ls_field. IFls_field-tablename IS NOT INITIAL. rs_tab-low = ls_field-tablename. TRANSLATErs_tab-low TO UPPER CASE. rs_tab-option = 'EQ'. rs_tab-sign = 'I'. APPENDrs_tab TO rt_tab. ENDIF. IF ls_field-fieldname IS NOT INITIAL. rs_fid-low =ls_field-fieldname. TRANSLATE rs_fid-low TO UPPER CASE. rs_fid-option = 'EQ'.rs_fid-sign = 'I'. APPEND rs_fid TO rt_fid. ENDIF. IF ls_field-elementname ISNOT INITIAL. rs_ele-low = ls_field-elementname. TRANSLATE rs_ele-low TO UPPERCASE. rs_ele-option = 'EQ'. rs_ele-sign = 'I'. APPEND rs_ele TO rt_ele. ENDIF.IF ls_field-domainname IS NOT INITIAL. rs_dom-low = ls_field-domainname.TRANSLATE rs_dom-low TO UPPER CASE. rs_dom-option = 'EQ'. rs_dom-sign = 'I'.APPEND rs_dom TO rt_dom. ENDIF. ENDLOOP. IF rt_tab IS INITIAL AND rt_fid ISINITIAL AND rt_ele IS INITIAL AND rt_dom IS INITIAL. o_json = '请输入数据'. RETURN. ENDIF. SELECTdd03l~tabname AS tablename tabletype AS istable fieldname keyflag AS iskeyrollname AS elementname checktable datatype leng AS length decimalsdd03l~domname AS domainname dd07t_1~ddtext AS shlporigin dd07t_2~ddtext AScomptype FROM dd03l LEFT JOIN dd07t AS dd07t_1 ON dd07t_1~domname =  'SHLPORIGIN' AND dd07t_1~ddlanguage =  '1' AND dd07t_1~domvalue_l = dd03l~shlporiginLEFT JOIN dd07t AS dd07t_2 ON dd07t_2~domname = 'COMPTYPE' ANDdd07t_2~ddlanguage = '1' AND dd07t_2~domvalue_l = dd03l~comptype INTO TABLElt_field WHERE dd03l~tabname IN rt_tab AND dd03l~fieldname IN rt_fid ANDdd03l~rollname IN rt_ele AND dd03l~domname IN rt_dom AND dd03l~as4local = 'A'.IF lt_field IS NOT INITIAL. o_json = /ui2/cl_json=>serialize( data =lt_field compress = ' ' pretty_name = /ui2/cl_json=>pretty_mode-none ).ENDIF.";

                TABMETHODSTRU.Add(methods);

                programStru.METHODS =TABMETHODSTRU;

                string sss =JsonConvert.SerializeObject(programStru);

                string str_SAPOutput = connectSAP.ExecuteRFM(sss);

                this.label1.Text = str_SAPOutput;

                dynamic model =JsonConvert.DeserializeObject(str_SAPOutput);

                string outstring = model[0].OUTPUTDATA[0].DATAVALUE.ToString();

                model = JsonConvert.DeserializeObject(outstring);

                int row = model.Count;

                Range range2 = worksheet.get_Range("A2","L" + (row+1));

                if (range2 ==null)

                {

                    return;

                }

                object[,] tabdata =newobject[model.Count, 12];

                for (int i = 0; i < model.Count; i++)

                {

                    tabdata[i, 0] =model[i].TABLENAME.ToString();

                    tabdata[i, 1] =model[i].ISTABLE.ToString();

                    tabdata[i, 2] =model[i].FIELDNAME.ToString();

                    tabdata[i, 3] =model[i].ISKEY.ToString();

                    tabdata[i, 4] =model[i].ELEMENTNAME.ToString();

                    tabdata[i, 5] =model[i].ELEMENTNAME.ToString();

                    tabdata[i, 6] =model[i].DATATYPE.ToString();

                    tabdata[i, 7] =model[i].DATATYPE.ToString();

                    tabdata[i, 8] =model[i].DECIMALS.ToString();

                    tabdata[i, 9] =model[i].DOMAINNAME.ToString();

                    tabdata[i, 10] =model[i].SHLPORIGIN.ToString();

                    tabdata[i, 11] =model[i].COMPTYPE.ToString();

                }

 

                range2.Value2 = (dynamic)tabdata;

            }

            catch (Exception)

            {

            }

        }


0.1.3.7.    关闭应用程序同时注销EXCEL进程

//关闭窗体触发

        protectedoverridevoid OnHandleDestroyed(EventArgs e)

        {

            // Stop the application

            if (app !=null )

            {

                // Post a colse message

                //PostMessage((IntPtr)app.Hwnd,WM_CLOSE, 0, 0);

                app.DisplayAlerts = false;

                app.Quit();

                // Delay for it to get the message

                System.Threading.Thread.Sleep(1000);

                // Clear internal handle

           }

            base.OnHandleDestroyed(e);

        }


0.1.4.    结果



猜你喜欢

转载自blog.csdn.net/qq_16635325/article/details/80241344