• C# 手动添加响应函数

    2014-12-25 17:34:04
    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;...name
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;


    namespace UItest
    {
        public partial class Form1 : Form
        {
            private readonly TabControl tabControl1;


            public Form1()
            {
                tabControl1 = new TabControl();
                tabControl1.Parent = this;
                var tabpage = new TabPage();
                tabpage.Text = "page";
                tabControl1.TabPages.Add(tabpage);
                tabControl1.TabPages.Add(tabpage);
                tabControl1.Dock = DockStyle.Fill;


                //注册响应函数
                tabControl1.Selected += new System.Windows.Forms.TabControlEventHandler(tabControl1_Selected);
                InitializeComponent();       
            }


            private void tabControl1_Selected(object sender, TabControlEventArgs e)
            {
                if (tabControl1.TabCount - 1 == e.TabPageIndex)
                {
                    tabControl1.TabPages.Add(e.TabPage.Text);
                    e.TabPage.Text = "选项" + e.TabPageIndex;
                }
            }






        }

    }



    展开全文
  • C#窗口程序中,如果在主线程里调用Sleep,在Sleep完成之前, 界面呈现出假死状态,不能响应任何操作! 下边实现的是非独占性延时函数,延时过时中界面仍可响应消息: public static void Delay(int milliSecond) { int...

    在C#窗口程序中,如果在主线程里调用Sleep,在Sleep完成之前, 界面呈现出假死状态,不能响应任何操作!
    下边实现的是非独占性延时函数,延时过时中界面仍可响应消息:

    public static void Delay(int milliSecond)
    {
        int start = Environment.TickCount;
        while (Math.Abs(Environment.TickCount - start) < milliSecond)
        {
            Application.DoEvents();
        }
    }

    关于Math.Abs():

      Environment.TickCount,内部API是用DWORD GetTickCount()来实现的,该属性的值从系统计时器派生,并以 32 位有符号整数的形式存储。因此,如果系统连续运行,TickCount 将在约 24.9 天内从零递增至 Int32. MaxValue ,然后跳至 Int32. MinValue (这是一个负数),再在接下来的 24.9 天内递增至零。DWORD是无符号的,而 Environment.TickCount属性返回的值是有符号的,所以有一半的值用负数表示!

    其他:
    1.用sleep()使线程休眠。
    直接在需要延时的地方插入 System.Threading.Thread.Sleep(1000); 即可。
    这种方式最方便,但是延时过程中会停止其他响应,如果用在主线程中会造成程序的假死。如果有异步操作的话也会暂停,例如用webBrowser加载网页,本来希望延时一段时间等待网页加载完毕,但用sleep的话同时会暂停网页的加载过程。

    用Thread和Timer控件都可以实现,如果用Timer_Tick(…)的话,因为不能在其他函数中调用Timer_Tick(…),所以用起来不方便,其实这两种都是用到了线程,微软推荐的方法是用委托。
    用C#中的线程来实现:
    using System.Threading;//引入命名空间
    1.延时10ms代码为:
    Thread.Sleep(10);
    2.延时1s代码为:
    Thread.Sleep(1000);

    平时我们在做winform开发的时候,有时候需要让程序休眠几秒钟,但是,如果我们直接使用 thread.sleep()函数,页面ui就会停止响应。怎么样解决呢,你可以把页面涉及到表现ui的代码放到一个单线程处理,也可以采用我下面的做法,加一个小函数ok了。

    /// <summary>
            /// 延时函数
            /// </summary>
            /// <param name="delayTime">需要延时多少秒</param>
            /// <returns></returns>
            public static bool Delay(int delayTime)
            {
                DateTime now = DateTime.Now;
                int s;
                do
                {
                    TimeSpan spand = DateTime.Now - now;
                    s = spand.Seconds;
                    Application.DoEvents();
                }
                while (s < delayTime);
                return true;
            }
    展开全文
  • C#事件的订阅与触发

    2017-05-05 15:47:19
    C#有关事件的使用-进阶版

    C#有关事件的使用-进阶版
    用猫和老鼠的示例
    一、简单的情况,无参数订阅事件

    //定义一个事件委托
    public delegate void mcEventHandler();
    //定义一个猫类
    class Cat
        {
            string cName;
            //定义一个猫叫事件
            public event mcEventHandler CatCryEvent; 
            public Cat(string name)
            {
                cName = name;
            }
            //当猫叫时候,触发事件
            public void Cry()
            {
                Console.WriteLine(cName+"来了");
                Console.ReadLine();
                //触发事件
                CatCryEvent();
            }
        }
    
        //定义一个鼠类
        class Mouse
        {
            public string mName;
            //在构造函数中进行订阅
            public Mouse(Cat cat)
            {
                //订阅事件的两种形式
                cat.CatCryEvent += Run;
                cat.CatCryEvent += new mcEventHandler(See);
            }
    
            private void Run()
            {
                Console.WriteLine("猫来了,"+mName+"先走一步");
            }
            private void See()
            {
                Console.WriteLine("看看猫还在不在");
                Console.ReadLine();
            }
        }
    
        //主函数中实例化对象
        class Program
        {
            static void Main(string[] args)
            {
                Cat cat1 = new Cat("Tom");
                Mouse m1 = new Mouse(cat1);
                //调用函数,以触发猫叫事件
                cat1.Cry();
            }
        }
    
    

    执行结果如下:这里写图片描述

    二、带有参数的事件订阅
    首先定义一个传递参数的类,可以是EventArgs类的派生类(继承该类),也可以是string、int这种简单类,或者是其他自定义类型
    这里定义一个CryEventArgs类传递参数

    class CryEventArgs:EventArgs
        {
            //存储一个字符串  
            public string CatName
            {
                get;
                set;
            }
        }

    接下来设计Cat类和Mouse类

      class Cat
        {
            string cName;
            public event mcEventHandler CatCryEvent;
            //定义带有参数的事件,此处CryEventArgs可以为其他简单类,如是,下面订阅的函数的签名需要相应地改变
            public event EventHandler<CryEventArgs> CatCryEvent1;
            public Cat(string name)
            {
                cName = name;
            }
    
            public void Cry()
            {
                Console.WriteLine(cName+"来了");
                Console.ReadLine();
                //用这个保存参数
                CryEventArgs e = new CryEventArgs();
                e.CatName = cName;
                //触发事件
                //CatCryEvent();
                CatCryEvent1(this, e);
            }
        }
    
        class Mouse
        {
            public Mouse(Cat cat)
            {
                //订阅事件的两种形式
                //cat.CatCryEvent1 += Run;
                cat.CatCryEvent1 += new EventHandler<CryEventArgs>(Run);
            }
    
            private void Run(object sender,CryEventArgs e)
            {
                if(e.CatName=="Tom")
                {
                    Console.WriteLine("别怕,是Tom这只傻猫");
                    Console.ReadLine();
                }
                else
                {
                    Console.WriteLine("快跑啊,是其他猫!");
                    Console.ReadLine();
                }
            }
        }
    
        //主函数,模拟事件发生
        public static void Main()
        {
             Cat c1 = new Cat("Tom");
             Cat c2 = new Cat("Ben");
             //两只老鼠,分别见到两只猫
             Mouse m = new Mouse(c1);
             Mouse m2 = new Mouse(c2);
             c1.Cry();
             Console.WriteLine("//-----------------而另一边---------------------//");
             c2.Cry();
        }
    

    结果如下图

    “`
    这里写图片描述

    展开全文
  • 总所周知:C#是.NET Framework平台的相伴语言,用它本身的类库和编译器提供的方法是无法实现全局钩子的。但实际上对于非托管代码的调用在C#中是成立的,使用DllImport属性可以引用非...钩子函数存在于user32.dll中

    文章来源:http://www.cnblogs.com/Johness/archive/2012/12/28/2837977.html


    总所周知:C#是.NET Framework平台的相伴语言,用它本身的类库和编译器提供的方法是无法实现全局钩子的。但实际上对于非托管代码的调用在C#中是成立的,使用DllImport属性可以引用非托管代码类库中的方法。钩子函数存在于user32.dll中,函数原型如下:


      HHOOK WINAPI SetWindowsHookEx(


        __in int idHook,


        __in HOOKPROC lpfn,


        __in HINSTANCE hMod,


        __in DWORD dwThreadId);


      使用它可以向操作系统(Windows)注册一个特定类型的消息拦截处理方法,例如我们可以注册一个拦截全局键盘消息的钩子,那么所有的键盘按下、抬起事件都可以被我们感知和处理(不排除有前端钩子将消息丢弃的情况)。


      我们在C#中可以如下声明来引用这个函数:


        [DllImport("user32.dll")]
            public static extern int SetWindowsHookEx(
                HookType idHook,
                HookProc lpfn,
                IntPtr hInstance,
                int threadId
                );


      值得一提的是上面的HookType和HookProc是我自定义的类型,这无关紧要(因为程序运行时传递的是内存地址嘛),但必须符合一定规范。


      函数的参数从上到下依次为:


         idHook钩子类型,此处用整形的枚举表示
           lpfn钩子发挥作用时的回调函数
           hInstance应用程序实例的模块句柄(一般来说是你钩子回调函数所在的应用程序实例模块句柄)
           threadId与安装的钩子子程相关联的线程的标识符


      钩子的类型有以下几种:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace HookINCS
    {
        /// <summary>
        /// 设置的钩子类型
        /// </summary>
        public enum HookType : int
        {
            /// <summary>
            /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 
            ///条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 
            ///WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 
            ///过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook 
            ///监视所有应用程序消息。 
            /// 
            ///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 
            ///过滤消息,这等价于在主消息循环中过滤消息。 
            ///    
            ///通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 
            ///个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 
            ///环里一样
            /// </summary>
            WH_MSGFILTER = -1,
            /// <summary>
            /// WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这 
            ///个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook 
            ///来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样 
            ///使用。WH_JOURNALRECORD是system-wide local hooks,它们不会被注射到任何行 
            ///程地址空间
            /// </summary>
            WH_JOURNALRECORD = 0,
            /// <summary>
            /// WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可 
            ///以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠 
            ///标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘 
            ///事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定 
            ///Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处 
            ///理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实 
            ///时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它们不会被 
            ///注射到任何行程地址空间
            /// </summary>
            WH_JOURNALPLAYBACK = 1,
            /// <summary>
            /// 在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and  
            ///WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使 
            ///用这个Hook来监视输入到消息队列中的键盘消息
            /// </summary>
            WH_KEYBOARD = 2,
            /// <summary>
            /// 应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函 
            ///数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及 
            ///其它发送到消息队列中的消息
            /// </summary>
            WH_GETMESSAGE = 3,
            /// <summary>
            /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之前调用
            /// </summary>
            WH_CALLWNDPROC = 4,
            /// <summary>
            /// 在以下事件之前,系统都会调用WH_CBT Hook子过程,这些事件包括: 
            ///1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件; 
            ///2. 完成系统指令; 
            ///3. 来自系统消息队列中的移动鼠标,键盘事件; 
            ///4. 设置输入焦点事件; 
            ///5. 同步系统消息队列事件。
            ///Hook子过程的返回值确定系统是否允许或者防止这些操作中的一个
            /// </summary>
            WH_CBT = 5,
            /// <summary>
            /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 
            ///条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 
            ///WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 
            ///过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook 
            ///监视所有应用程序消息。 
            /// 
            ///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 
            ///过滤消息,这等价于在主消息循环中过滤消息。 
            ///    
            ///通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 
            ///个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 
            ///环里一样
            /// </summary>
            WH_SYSMSGFILTER = 6,
            /// <summary>
            /// WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。 
            ///使用这个Hook监视输入到消息队列中的鼠标消息
            /// </summary>
            WH_MOUSE = 7,
            /// <summary>
            /// 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时
            /// </summary>
            WH_HARDWARE = 8,
            /// <summary>
            /// 在系统调用系统中与其它Hook关联的Hook子过程之前,系统会调用 
            ///WH_DEBUG Hook子过程。你可以使用这个Hook来决定是否允许系统调用与其它 
            ///Hook关联的Hook子过程
            /// </summary>
            WH_DEBUG = 9,
            /// <summary>
            /// 外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是 
            ///激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子过程。 
            ///WH_SHELL 共有5钟情况: 
            ///1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁; 
            ///2. 当Taskbar需要重画某个按钮; 
            ///3. 当系统需要显示关于Taskbar的一个程序的最小化形式; 
            ///4. 当目前的键盘布局状态改变; 
            ///5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。 
            ///
            ///按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接 
            ///收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自 
            ///己
            /// </summary>
            WH_SHELL = 10,
            /// <summary>
            /// 当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE  
            ///Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就 
            ///会调用WH_FOREGROUNDIDLE Hook子过程
            /// </summary>
            WH_FOREGROUNDIDLE = 11,
            /// <summary>
            /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之后调用
            /// </summary>
            WH_CALLWNDPROCRET = 12,
            /// <summary>
            /// 监视输入到线程消息队列中的键盘消息
            /// </summary>
            WH_KEYBOARD_LL = 13,
            /// <summary>
            /// 监视输入到线程消息队列中的鼠标消息
            /// </summary>
            WH_MOUSE_LL = 14
        }
    }

    我们一般会使用13拦截键盘消息,14拦截鼠标消息。


      回调函数的声明我们在C#里需要用到委托,声明如下:
        public delegate int HookProc(int nCode, int wParam, IntPtr lParam);


      从上而下参数意义为:nCode钩子链传递回来的参数,0表示此消息(被之前的消息钩子)丢弃,非0表示此消息继续有效


                wParam消息参数


                lParam消息参数


      值得一提的是wParam和lParam在不同的消息类型中是不一样的类型,不过wParam的类型大概可以用下面的枚举表示:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace HookINCS
    {
        /// <summary>
        /// 消息类型
        /// 作为SendMessage和PostMessage的参数
        /// </summary>
        public enum MsgType : uint
        {
            WM_KEYFIRST = 0x0100,
    
            //Msg参数常量值:
            /// <summary>
            /// 按下一个键
            /// </summary>
            WM_KEYDOWN = 0x0100,
            /// <summary>
            /// 释放一个键
            /// </summary>
            WM_KEYUP = 0x0101,
            /// <summary>
            /// 按下某键,并已发出WM_KEYDOWN, WM_KEYUP消息
            /// </summary>
            WM_CHAR = 0x102,
            /// <summary>
            /// 当用translatemessage函数翻译WM_KEYUP消息时发送此消息给拥有焦点的窗口
            /// </summary>
            WM_DEADCHAR = 0x103,
            /// <summary>
            /// 当用户按住ALT键同时按下其它键时提交此消息给拥有焦点的窗口
            /// </summary>
            WM_SYSKEYDOWN = 0x104,
            /// <summary>
            /// 当用户释放一个键同时ALT 键还按着时提交此消息给拥有焦点的窗口
            /// </summary>
            WM_SYSKEYUP = 0x105,
            /// <summary>
            /// 当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后提交此消息给拥有焦点的窗口
            /// </summary>
            WM_SYSCHAR = 0x106,
            /// <summary>
            /// 当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后发送此消息给拥有焦点的窗口
            /// </summary>
            WM_SYSDEADCHAR = 0x107,
            /// <summary>
            /// 在一个对话框程序被显示前发送此消息给它,通常用此消息初始化控件和执行其它任务
            /// </summary>
            WM_INITDIALOG = 0x110,
            /// <summary>
            /// 当用户选择一条菜单命令项或当某个控件发送一条消息给它的父窗口,一个快捷键被翻译
            /// </summary>
            WM_COMMAND = 0x111,
            /// <summary>
            /// 当用户选择窗口菜单的一条命令或//当用户选择最大化或最小化时那个窗口会收到此消息
            /// </summary>
            WM_SYSCOMMAND = 0x112,
            /// <summary>
            /// 发生了定时器事件
            /// </summary>
            WM_TIMER = 0x113,
            /// <summary>
            /// 当一个窗口标准水平滚动条产生一个滚动事件时发送此消息给那个窗口,也发送给拥有它的控件
            /// </summary>
            WM_HSCROLL = 0x114,
            /// <summary>
            /// 当一个窗口标准垂直滚动条产生一个滚动事件时发送此消息给那个窗口也,发送给拥有它的控件
            /// </summary>
            WM_VSCROLL = 0x115,
            /// <summary>
            /// 当一个菜单将要被激活时发送此消息,它发生在用户菜单条中的某项或按下某个菜单键,它允许程序在显示前更改菜单
            /// </summary>
            WM_INITMENU = 0x116,
            /// <summary>
            /// 当一个下拉菜单或子菜单将要被激活时发送此消息,它允许程序在它显示前更改菜单,而不要改变全部
            /// </summary>
            WM_INITMENUPOPUP = 0x117,
            /// <summary>
            /// 当用户选择一条菜单项时发送此消息给菜单的所有者(一般是窗口)
            /// </summary>
            WM_MENUSELECT = 0x11F,
            /// <summary>
            /// 当菜单已被激活用户按下了某个键(不同于加速键),发送此消息给菜单的所有者
            /// </summary>
            WM_MENUCHAR = 0x120,
            /// <summary>
            /// 当一个模态对话框或菜单进入空载状态时发送此消息给它的所有者,一个模态对话框或菜单进入空载状态就是在处理完一条或几条先前的消息后没有消息它的列队中等待
            /// </summary>
            WM_ENTERIDLE = 0x121,
            /// <summary>
            /// 在windows绘制消息框前发送此消息给消息框的所有者窗口,通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置消息框的文本和背景颜色
            /// </summary>
            WM_CTLCOLORMSGBOX = 0x132,
            /// <summary>
            /// 当一个编辑型控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置编辑框的文本和背景颜色
            /// </summary>
            WM_CTLCOLOREDIT = 0x133,
    
            /// <summary>
            /// 当一个列表框控件将要被绘制前发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置列表框的文本和背景颜色
            /// </summary>
            WM_CTLCOLORLISTBOX = 0x134,
            /// <summary>
            /// 当一个按钮控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置按纽的文本和背景颜色
            /// </summary>
            WM_CTLCOLORBTN = 0x135,
            /// <summary>
            /// 当一个对话框控件将要被绘制前发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置对话框的文本背景颜色
            /// </summary>
            WM_CTLCOLORDLG = 0x136,
            /// <summary>
            /// 当一个滚动条控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置滚动条的背景颜色
            /// </summary>
            WM_CTLCOLORSCROLLBAR = 0x137,
            /// <summary>
            /// 当一个静态控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以 通过使用给定的相关显示设备的句柄来设置静态控件的文本和背景颜色
            /// </summary>
            WM_CTLCOLORSTATIC = 0x138,
            /// <summary>
            /// 当鼠标轮子转动时发送此消息个当前有焦点的控件
            /// </summary>
            WM_MOUSEWHEEL = 0x20A,
            /// <summary>
            /// 双击鼠标中键
            /// </summary>
            WM_MBUTTONDBLCLK = 0x209,
            /// <summary>
            /// 释放鼠标中键
            /// </summary>
            WM_MBUTTONUP = 0x208,
            /// <summary>
            /// 移动鼠标时发生,同WM_MOUSEFIRST
            /// </summary>
            WM_MOUSEMOVE = 0x200,
            /// <summary>
            /// 按下鼠标左键
            /// </summary>
            WM_LBUTTONDOWN = 0x201,
            /// <summary>
            /// 释放鼠标左键
            /// </summary>
            WM_LBUTTONUP = 0x202,
            /// <summary>
            /// 双击鼠标左键
            /// </summary>
            WM_LBUTTONDBLCLK = 0x203,
            /// <summary>
            /// 按下鼠标右键
            /// </summary>
            WM_RBUTTONDOWN = 0x204,
            /// <summary>
            /// 释放鼠标右键
            /// </summary>
            WM_RBUTTONUP = 0x205,
            /// <summary>
            /// 双击鼠标右键
            /// </summary>
            WM_RBUTTONDBLCLK = 0x206,
            /// <summary>
            /// 按下鼠标中键
            /// </summary>
            WM_MBUTTONDOWN = 0x207,
    
            //WM_USER = 0x0400;
            //public static int MK_LBUTTON = 0x0001;
            //public static int MK_RBUTTON = 0x0002;
            //public static int MK_SHIFT = 0x0004;
            //public static int MK_CONTROL = 0x0008;
            //public static int MK_MBUTTON = 0x0010;
            //public static int MK_XBUTTON1 = 0x0020;
            //public static int MK_XBUTTON2 = 0x0040;
            /// <summary>
            /// 创建一个窗口
            /// </summary>
            WM_CREATE = 0x01,
            /// <summary>
            /// 当一个窗口被破坏时发送
            /// </summary>
            WM_DESTROY = 0x02,
            /// <summary>
            /// 移动一个窗口
            /// </summary>
            WM_MOVE = 0x03,
            /// <summary>
            /// 改变一个窗口的大小
            /// </summary>
            WM_SIZE = 0x05,
            /// <summary>
            /// 一个窗口被激活或失去激活状态
            /// </summary>
            WM_ACTIVATE = 0x06,
            /// <summary>
            /// 一个窗口获得焦点
            /// </summary>
            WM_SETFOCUS = 0x07,
            /// <summary>
            /// 一个窗口失去焦点
            /// </summary>
            WM_KILLFOCUS = 0x08,
            /// <summary>
            /// 一个窗口改变成Enable状态
            /// </summary>
            WM_ENABLE = 0x0A,
            /// <summary>
            /// 设置窗口是否能重画
            /// </summary>
            WM_SETREDRAW = 0x0B,
            /// <summary>
            /// 应用程序发送此消息来设置一个窗口的文本
            /// </summary>
            WM_SETTEXT = 0x0C,
            /// <summary>
            /// 应用程序发送此消息来复制对应窗口的文本到缓冲区
            /// </summary>
            WM_GETTEXT = 0x0D,
            /// <summary>
            /// 得到与一个窗口有关的文本的长度(不包含空字符)
            /// </summary>
            WM_GETTEXTLENGTH = 0x0E,
            /// <summary>
            /// 要求一个窗口重画自己
            /// </summary>
            WM_PAINT = 0x0F,
            /// <summary>
            /// 当一个窗口或应用程序要关闭时发送一个信号
            /// </summary>
            WM_CLOSE = 0x10,
            /// <summary>
            /// 当用户选择结束对话框或程序自己调用ExitWindows函数
            /// </summary>
            WM_QUERYENDSESSION = 0x11,
            /// <summary>
            /// 用来结束程序运行
            /// </summary>
            WM_QUIT = 0x12,
            /// <summary>
            /// 当用户窗口恢复以前的大小位置时,把此消息发送给某个图标
            /// </summary>
            WM_QUERYOPEN = 0x13,
            /// <summary>
            /// 当窗口背景必须被擦除时(例在窗口改变大小时)
            /// </summary>
            WM_ERASEBKGND = 0x14,
            /// <summary>
            /// 当系统颜色改变时,发送此消息给所有顶级窗口
            /// </summary>
            WM_SYSCOLORCHANGE = 0x15,
            /// <summary>
            /// 当系统进程发出WM_QUERYENDSESSION消息后,此消息发送给应用程序,通知它对话是否结束
            /// </summary>
            WM_ENDSESSION = 0x16,
            /// <summary>
            /// 当隐藏或显示窗口是发送此消息给这个窗口
            /// </summary>
            WM_SHOWWINDOW = 0x18,
            /// <summary>
            /// 发此消息给应用程序哪个窗口是激活的,哪个是非激活的
            /// </summary>
            WM_ACTIVATEAPP = 0x1C,
            /// <summary>
            /// 当系统的字体资源库变化时发送此消息给所有顶级窗口
            /// </summary>
            WM_FONTCHANGE = 0x1D,
            /// <summary>
            /// 当系统的时间变化时发送此消息给所有顶级窗口
            /// </summary>
            WM_TIMECHANGE = 0x1E,
            /// <summary>
            /// 发送此消息来取消某种正在进行的摸态(操作)
            /// </summary>
            WM_CANCELMODE = 0x1F,
            /// <summary>
            /// 如果鼠标引起光标在某个窗口中移动且鼠标输入没有被捕获时,就发消息给某个窗口
            /// </summary>
            WM_SETCURSOR = 0x20,
            /// <summary>
            /// 当光标在某个非激活的窗口中而用户正按着鼠标的某个键发送此消息给//当前窗口
            /// </summary>
            WM_MOUSEACTIVATE = 0x21,
            /// <summary>
            /// 发送此消息给MDI子窗口//当用户点击此窗口的标题栏,或//当窗口被激活,移动,改变大小
            /// </summary>
            WM_CHILDACTIVATE = 0x22,
            /// <summary>
            /// 此消息由基于计算机的训练程序发送,通过WH_JOURNALPALYBACK的hook程序分离出用户输入消息
            /// </summary>
            WM_QUEUESYNC = 0x23,
            /// <summary>
            /// 此消息发送给窗口当它将要改变大小或位置
            /// </summary>
            WM_GETMINMAXINFO = 0x24,
            /// <summary>
            /// 发送给最小化窗口当它图标将要被重画
            /// </summary>
            WM_PAINTICON = 0x26,
            /// <summary>
            /// 此消息发送给某个最小化窗口,仅//当它在画图标前它的背景必须被重画
            /// </summary>
            WM_ICONERASEBKGND = 0x27,
            /// <summary>
            /// 发送此消息给一个对话框程序去更改焦点位置
            /// </summary>
            WM_NEXTDLGCTL = 0x28,
            /// <summary>
            /// 每当打印管理列队增加或减少一条作业时发出此消息 
            /// </summary>
            WM_SPOOLERSTATUS = 0x2A,
            /// <summary>
            /// 当button,combobox,listbox,menu的可视外观改变时发送
            /// </summary>
            WM_DRAWITEM = 0x2B,
            /// <summary>
            /// 当button, combo box, list box, list view control, or menu item 被创建时
            /// </summary>
            WM_MEASUREITEM = 0x2C,
            /// <summary>
            /// 此消息有一个LBS_WANTKEYBOARDINPUT风格的发出给它的所有者来响应WM_KEYDOWN消息 
            /// </summary>
            WM_VKEYTOITEM = 0x2E,
            /// <summary>
            /// 此消息由一个LBS_WANTKEYBOARDINPUT风格的列表框发送给他的所有者来响应WM_CHAR消息 
            /// </summary>
            WM_CHARTOITEM = 0x2F,
            /// <summary>
            /// 当绘制文本时程序发送此消息得到控件要用的颜色
            /// </summary>
            WM_SETFONT = 0x30,
            /// <summary>
            /// 应用程序发送此消息得到当前控件绘制文本的字体
            /// </summary>
            WM_GETFONT = 0x31,
            /// <summary>
            /// 应用程序发送此消息让一个窗口与一个热键相关连 
            /// </summary>
            WM_SETHOTKEY = 0x32,
            /// <summary>
            /// 应用程序发送此消息来判断热键与某个窗口是否有关联
            /// </summary>
            WM_GETHOTKEY = 0x33,
            /// <summary>
            /// 此消息发送给最小化窗口,当此窗口将要被拖放而它的类中没有定义图标,应用程序能返回一个图标或光标的句柄,当用户拖放图标时系统显示这个图标或光标
            /// </summary>
            WM_QUERYDRAGICON = 0x37,
            /// <summary>
            /// 发送此消息来判定combobox或listbox新增加的项的相对位置
            /// </summary>
            WM_COMPAREITEM = 0x39,
            /// <summary>
            /// 显示内存已经很少了
            /// </summary>
            WM_COMPACTING = 0x41,
            /// <summary>
            /// 发送此消息给那个窗口的大小和位置将要被改变时,来调用setwindowpos函数或其它窗口管理函数
            /// </summary>
            WM_WINDOWPOSCHANGING = 0x46,
            /// <summary>
            /// 发送此消息给那个窗口的大小和位置已经被改变时,来调用setwindowpos函数或其它窗口管理函数
            /// </summary>
            WM_WINDOWPOSCHANGED = 0x47,
            /// <summary>
            /// 当系统将要进入暂停状态时发送此消息
            /// </summary>
            WM_POWER = 0x48,
            /// <summary>
            /// 当一个应用程序传递数据给另一个应用程序时发送此消息
            /// </summary>
            WM_COPYDATA = 0x4A,
            /// <summary>
            /// 当某个用户取消程序日志激活状态,提交此消息给程序
            /// </summary>
            WM_CANCELJOURNA = 0x4B,
            /// <summary>
            /// 当某个控件的某个事件已经发生或这个控件需要得到一些信息时,发送此消息给它的父窗口 
            /// </summary>
            WM_NOTIFY = 0x4E,
            /// <summary>
            /// 当用户选择某种输入语言,或输入语言的热键改变
            /// </summary>
            WM_INPUTLANGCHANGEREQUEST = 0x50,
            /// <summary>
            /// 当平台现场已经被改变后发送此消息给受影响的最顶级窗口
            /// </summary>
            WM_INPUTLANGCHANGE = 0x51,
            /// <summary>
            /// 当程序已经初始化windows帮助例程时发送此消息给应用程序
            /// </summary>
            WM_TCARD = 0x52,
            /// <summary>
            /// 此消息显示用户按下了F1,如果某个菜单是激活的,就发送此消息个此窗口关联的菜单,否则就发送给有焦点的窗口,如果//当前都没有焦点,就把此消息发送给//当前激活的窗口
            /// </summary>
            WM_HELP = 0x53,
            /// <summary>
            /// 当用户已经登入或退出后发送此消息给所有的窗口,//当用户登入或退出时系统更新用户的具体设置信息,在用户更新设置时系统马上发送此消息
            /// </summary>
            WM_USERCHANGED = 0x54,
            /// <summary>
            /// 公用控件,自定义控件和他们的父窗口通过此消息来判断控件是使用ANSI还是UNICODE结构
            /// </summary>
            WM_NOTIFYFORMAT = 0x55,
            /// <summary>
            /// 当用户某个窗口中点击了一下右键就发送此消息给这个窗口
            /// </summary>
            WM_CONTEXTMENU = 0x7B,
            /// <summary>
            /// 当调用SETWINDOWLONG函数将要改变一个或多个 窗口的风格时发送此消息给那个窗口
            /// </summary>
            WM_STYLECHANGING = 0x7C,
            /// <summary>
            /// 当调用SETWINDOWLONG函数一个或多个 窗口的风格后发送此消息给那个窗口
            /// </summary>
            WM_STYLECHANGED = 0x7D,
            /// <summary>
            /// 当显示器的分辨率改变后发送此消息给所有的窗口
            /// </summary>
            WM_DISPLAYCHANGE = 0x7E,
            /// <summary>
            /// 此消息发送给某个窗口来返回与某个窗口有关连的大图标或小图标的句柄
            /// </summary>
            WM_GETICON = 0x7F,
            /// <summary>
            /// 程序发送此消息让一个新的大图标或小图标与某个窗口关联
            /// </summary>
            WM_SETICON = 0x80,
            /// <summary>
            /// 当某个窗口第一次被创建时,此消息在WM_CREATE消息发送前发送
            /// </summary>
            WM_NCCREATE = 0x81,
            /// <summary>
            /// 此消息通知某个窗口,非客户区正在销毁 
            /// </summary>
            WM_NCDESTROY = 0x82,
            /// <summary>
            /// 当某个窗口的客户区域必须被核算时发送此消息
            /// </summary>
            WM_NCCALCSIZE = 0x83,
            /// <summary>
            /// 移动鼠标,按住或释放鼠标时发生
            /// </summary>
            WM_NCHITTEST = 0x84,
            /// <summary>
            /// 程序发送此消息给某个窗口当它(窗口)的框架必须被绘制时
            /// </summary>
            WM_NCPAINT = 0x85,
            /// <summary>
            /// 此消息发送给某个窗口仅当它的非客户区需要被改变来显示是激活还是非激活状态
            /// </summary>
            WM_NCACTIVATE = 0x86,
            /// <summary>
            /// 发送此消息给某个与对话框程序关联的控件,widdows控制方位键和TAB键使输入进入此控件通过应
            /// </summary>
            WM_GETDLGCODE = 0x87,
            /// <summary>
            /// 当光标在一个窗口的非客户区内移动时发送此消息给这个窗口 非客户区为:窗体的标题栏及窗 的边框体
            /// </summary>
            WM_NCMOUSEMOVE = 0xA0,
            /// <summary>
            /// 当光标在一个窗口的非客户区同时按下鼠标左键时提交此消息
            /// </summary>
            WM_NCLBUTTONDOWN = 0xA1,
            /// <summary>
            /// 当用户释放鼠标左键同时光标某个窗口在非客户区十发送此消息
            /// </summary>
            WM_NCLBUTTONUP = 0xA2,
            /// <summary>
            /// 当用户双击鼠标左键同时光标某个窗口在非客户区十发送此消息
            /// </summary>
            WM_NCLBUTTONDBLCLK = 0xA3,
            /// <summary>
            /// 当用户按下鼠标右键同时光标又在窗口的非客户区时发送此消息
            /// </summary>
            WM_NCRBUTTONDOWN = 0xA4,
            /// <summary>
            /// 当用户释放鼠标右键同时光标又在窗口的非客户区时发送此消息
            /// </summary>
            WM_NCRBUTTONUP = 0xA5,
            /// <summary>
            /// 当用户双击鼠标右键同时光标某个窗口在非客户区十发送此消息
            /// </summary>
            WM_NCRBUTTONDBLCLK = 0xA6,
            /// <summary>
            /// 当用户按下鼠标中键同时光标又在窗口的非客户区时发送此消息
            /// </summary>
            WM_NCMBUTTONDOWN = 0xA7,
            /// <summary>
            /// 当用户释放鼠标中键同时光标又在窗口的非客户区时发送此消息
            /// </summary>
            WM_NCMBUTTONUP = 0xA8,
            /// <summary>
            /// 当用户双击鼠标中键同时光标又在窗口的非客户区时发送此消息
            /// </summary>
            WM_NCMBUTTONDBLCLK = 0xA9
        }
    }
    而lParam一般被封装为结构,因消息类型而异,如下的两个结构分别是鼠标和键盘消息的lParam结构:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace HookINCS
    {
        /// <summary>
        /// 声明鼠标钩子的封送结构类型
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public class MOUSEHOOKSTRUCT
        {
    
            /// <summary>
            /// POINT结构对象,保存鼠标在屏幕上的x,y坐标
            /// </summary>
            public POINT pt;
            /// <summary>
            /// 接收到鼠标消息的窗口的句柄
            /// </summary>
            public IntPtr hWnd;
            /// <summary>
            /// hit-test值,详细描述参见WM_NCHITTEST消息
            /// </summary>
            public int wHitTestCode;
            /// <summary>
            /// 指定与本消息联系的额外消息
            /// </summary>
            public int dwExtraInfo;
        }
    }

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace HookINCS
    {
        /// <summary>
        /// 键盘Hook结构函数
        /// 即钩子发挥作用时能够得到的一些参数
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public class KBDLLHOOKSTRUCT
        {
            /// <summary>
            /// 虚拟按键码(1--254)
            /// </summary>
            public int vkCode;
            /// <summary>
            /// 硬件按键扫描码
            /// </summary>
            public int scanCode;
            /// <summary>
            /// 键按下:128 抬起:0
            /// </summary>
            public int flags;
            /// <summary>
            /// 消息时间戳间
            /// </summary>
            public int time;
            /// <summary>
            /// 额外信息
            /// </summary>
            public int dwExtraInfo;
        }
    }

    当我们了解了以上信息时,我们就基本了解了钩子函数的C#实现了,然后注意几个问题就好:


        1.钩子对资源占用很多,不用时应及时取消掉,这个需要使用UnhookWindowsHookEx函数


        2.处于礼貌,钩子应返回下一个钩子的处理结果,而不是单一地将当前钩子的处理结果返回(使用CallNextHookEx调用下一个钩子,由于钩子是先设置后生效,所以应该如此来保证钩子链的正常传递)


        3.钩子函数参数中的hInstance是只当前钩子的回调函数在哪儿,一定要给出正确地址


        4.因为使用了委托,应该保证委托的内存地址(对方法的引用)不会垃圾回收,否则在钩子执行时会出现异常


    展开全文
  • C#实现检测U盘的插拔

    2017-04-09 17:23:56
    C# Winform中WndProc 函数作用: 主要用在拦截并处理系统消息和自定义消息 比如: windows程序会产生很多消息,比如你单击鼠标,移动窗口都会产生消息。这个函数就是默认的消息处理函数。你可以重载这个函数来...

    C# Winform中WndProc 函数作用:

    主要用在拦截并处理系统消息和自定义消息

    比如:
    windows程序会产生很多消息,比如你单击鼠标,移动窗口都会产生消息。这个函数就是默认的消息处理函数。你可以重载这个函数来制定自己的消息处理流程.

    在Winform程序中,可以重写WndProc函数,来捕捉所有发生的窗口消息。

    这样,我们就可以"篡改"传入的消息,而人为的让窗口改变行为。

    我们用C#实现检测U盘插拔的功能,是用重写C# WndProc函数来做到的。 

    简单测试代码:

     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.IO;  //添加IO命名空间
    
    namespace CheckUdisk
    {
        public partial class Form1 : Form
        {
    
            //定义常量
            public const int WM_DEVICECHANGE = 0x219;
            public const int DBT_DEVICEARRIVAL = 0x8000;
            public const int DBT_CONFIGCHANGECANCELED = 0x0019;
            public const int DBT_CONFIGCHANGED = 0x0018;
            public const int DBT_CUSTOMEVENT = 0x8006;           
            public const int DBT_DEVICEQUERYREMOVE = 0x8001;
            public const int DBT_DEVICEQUERYREMOVEFAILED = 0x8002;
            public const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
            public const int DBT_DEVICEREMOVEPENDING = 0x8003;
            public const int DBT_DEVICETYPESPECIFIC = 0x8005;
            public const int DBT_DEVNODES_CHANGED = 0x0007;
            public const int DBT_QUERYCHANGECONFIG = 0x0017;
            public const int DBT_USERDEFINED = 0xFFFF;
    
    
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
    
            }
    
            protected override void WndProc(ref Message m)
            {
    
                try
                {
                    if (m.Msg == WM_DEVICECHANGE)
                    {
                        switch (m.WParam.ToInt32())
                        {
                            case WM_DEVICECHANGE:
                                break;
                            case DBT_DEVICEARRIVAL:
                                DriveInfo[] s = DriveInfo.GetDrives();
                                foreach (DriveInfo drive in s)
                                {
                                    if (drive.DriveType == DriveType.Removable)
                                    {
                                        this.richTextBox1.AppendText("U盘已插入,盘符是" + drive.Name.ToString() + "\r\n");
                                        break;
                                    }
                                }
                                break;
                            case DBT_CONFIGCHANGECANCELED:
                                MessageBox.Show("2");
                                break;
                            case DBT_CONFIGCHANGED:
                                MessageBox.Show("3");
                                break;
                            case DBT_CUSTOMEVENT:
                                MessageBox.Show("4");
                                break;
                            case DBT_DEVICEQUERYREMOVE:
                                MessageBox.Show("5");
                                break;
                            case DBT_DEVICEQUERYREMOVEFAILED:
                                MessageBox.Show("6");
                                break;
                            case DBT_DEVICEREMOVECOMPLETE:
                                this.richTextBox1.AppendText("U盘已卸载");
                                break;
                            case DBT_DEVICEREMOVEPENDING:
                                MessageBox.Show("7");
                                break;
                            case DBT_DEVICETYPESPECIFIC:
                                MessageBox.Show("8");
                                break;
                            case DBT_DEVNODES_CHANGED:
                                MessageBox.Show("9");
                                break;
                            case DBT_QUERYCHANGECONFIG:
                                MessageBox.Show("10");
                                break;
                            case DBT_USERDEFINED:
                                MessageBox.Show("11");
                                break;
                            default:
                                break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
    
                base.WndProc(ref m);
            }
        }
    }
    


    当我们插拔U盘时,Form1窗体会出现下面响应



    展开全文
  • C#基础教程-c#实例教程,适合初学者。 第一章 C#语言基础 本章介绍C#语言的基础知识,希望具有C语言的读者能够基本掌握C#语言,并以此为基础,能够进一步学习用C#语言编写window应用程序和Web应用程序。当然仅靠一...
  • c# dataGridView排序

    2019-06-27 02:02:19
    一、对阿拉伯数字进行自定义排序: 简单有效方法:  1.该列的sortmode属性为auto...(一般默认)  2.比如首列序号,添加该列数据的时候直接添加int即可。切忌不要用string。 object[] newRow = new ... in...
  • 以下内容参考本篇博文,看了许多的介绍,这篇个人感觉诗写得比较详细的,本篇参考这个并结合C#的实现过程进行整理 l Canny边缘检测算法可以分为以下5个步骤: 1)使用高斯滤波器,以平滑图像,滤除噪声。 2)计算图像...
  • C++函数指针与C#委托

    2018-01-02 11:54:39
    一、C++函数指针详解 1. 定义 每一个函数都占用一段内存单元,它们有一个起始地址,指向函数入口地址的指针称为函数指针。 2. 语法 指向函数的指针变量的一般定义形式为: 数据类型 (*指针变量名)(参数表); 3....
  • 对数据库的操作总体可以分为两类:查询(select)和更新(insert,delete,update)。为什么这样来分呢?仔细看看两类的区别,select只是从数据库中将数据拿出来使用,而其余三者都会对数据库的物理数据进行修改。...
  • 在Windows环境下编程必须熟练掌握Windows消息响应机制。  今天在练习Win32编程时碰到一个关于GetMessage函数的问题。这个问题之前一直没有引起过我的注意,但是今天  在网上搜索发现这个问题很多程序员都跟我一样...
  • 使用在HTML中嵌入C#代码 打开ASPX页面Default.aspx,首先在 之间添加标题“使用%HTML中嵌入C#“,再在其中的 标签中间输入以下代码: title>使用%HTML中嵌入C#title> … div>  int i;  for (i = 0; ...
  • 入门线程小例子C#支持通过多线程并行地执行代码,一个线程有它独立的执行路径,能够与其它的线程同时地运行。一个C#程序开始于一个单线程,这个单线程是被CLR和操作系统(也称为“主线程”)自动创建的,并具有多...
  • 我看同步阻塞 “你知道什么是同步阻塞吗”,当然知道了。“那你怎么看它呢”,这个。。。 在同步阻塞的世界里,代码执行到哪里,数据就跟到哪里。如果数据很慢跟不上来,代码就停在那里等待数据的到来,然后再带着...
  • 观察如下三组测试性能数据,每级首次执行时间比二次执行大概多用了4秒样子,虽说第...SQL中除了一个自定义时间截断函数外,就是表字段本身了,会不会是处理10多万条数据,占用了大量处理时间,也就是延长了总体响应...
  • c#中richTextBox的用法

    2017-03-02 17:11:01
    C# RichTextBox的用法 RichTextBox是一种可用于显示、输入和操作格式文本,除了可以实现TextBox的所有功能,还能提供富文本的显示功能。 控件除具有TextBox 控件的所有功能外,还能设定文字颜色、字体和段落...
  • C# 关闭串口卡死

    2017-10-28 08:41:35
    C#编写的wince串口通信程序基本大功告成了,与之前用API函数和线程来做串口通信不同,这次直接使用SerialPort控件来做,原本以为使用控件做会简单和方便许多,没成想,还遇到了很多麻烦。  通信协议解析判断、...
  • C#延时程序

    2017-01-07 18:18:37
    C#窗口程序中,如果在主线程里调用Sleep,在Sleep完成之前, 界面呈现出假死状态,不能响应任何操作! 下边实现的是非独占性延时函数,延时过时中界面仍可响应消息:public static void Delay(int milliSecond) { int ...
  • c#的一些技巧

    2006-08-20 11:20:00
    c#的一些技巧 1.怎样定制VC#DataGrid列标题? DataGridTableStyle dgts = new DataGridTableStyle(); dgts.M
  • C#多线程编程

    2019-06-20 16:02:12
    线程:线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。 多线程:多线程是指程序中包含多个执行流,即在一个程序中可以同时...
1 2 3 4 5 ... 20
收藏数 10,211
精华内容 4,084