定时关机知识点:API的基本概念和使用方法。日期/时间DateTimePicker控件的使用方法。系统托盘的编程。计时器Timer控件的使用方法。本章编写说明:和其他章节比较,本章对操作的步骤有较为详细的说明,这是考虑到不同层次读者的需要。在其他章节有对操作步骤不太理解的地方,可参阅本章的内容。1.1实例简介在许多应用程序中都涉及到重启、或关机等功能,利用API完成一个关机功能的程序以及对系统托盘的编程操作对许多应用程序是必需有的功能。本章将通过一个定时关机程序,详细描述实现过程。1.2必备知识下面通过小例了解和掌握本章的知识点:1.2.1一个简单的API函数调用1.API基础API(ApplicationProgrammingInterface)是应用编程接口,是程序与处理器接口的命令集。Win32API可以直接控制MicrosoftWindows的核心(Win32代表为32位处理器开发的),是微软留给程序开发者直接控制Windows的接口。Win32API函数存放在动态链接库DLL文件中,常用的API在下面3个库中得以运行,分别是KERNEL32、USER32和GDI32。表1-1API函数功能和动态链接库的关系Win32动态链接库文件名功能主要功能举例KERNEL32.DLL主要用于产生与操作系统之间的关联程序加载、上下文选择,文件输入输出,内存管理USER32.DLL允许管理全部的用户接口窗口、菜单、对话框、图标等GDI32.DLL图形输出库“画”出窗口、菜单以及对话框等.NET组件就是封装了相关的API,有些功能还没有封装,就需要程序开发者直接调用API;需要了解一些API调用的基本知识,下面通过一个实例了解如何用.NET平台上的C#语言来调用Win32平台上的DLL文件。2.通过API直接调用MessageBox(1)创建一个名为“通过API直接调用MssageBox”的Windows应用程序项目(2)添加一个Button按钮,采用默认属性值,其中Name属性值为button1(3)需要引用外来库,所以必须导入一个Namespace:usingSystem.Runtime.InteropServices;(4)添加代码来声明一个API(在属性和字段定义的位置)[DllImport(User32.dll)]publicstaticexternintMessageBox(inth,stringm,stringc,inttype);此处DllImport属性被用来从不可控代码中调用一方法。”User32.DLL”则设定了类库名。DllImport属性指定DLL的位置,这个DLL中包括调用的外部方法。Static修饰符则声明一个静态元素,而这个元素属于类型本身而不是上面指定的对象。extern则表示这个方法将在工程外部执行,使用DllImport导入的方法必须使用extern修饰符。MessageBox则是函数名,拥有4个参数,其返回值为数字。(5)添加button1的Click事件的处理方法,并添加代码如下:privatevoidbutton1_Click(objectsender,System.EventArgse){MessageBox(0,显示的信息,API直接调用MessageBox,0);}MessageBox4个参数的含义如下:第一个:弹出的MessageBox的父窗口是谁。本例中没有,所以是0,也就是“空指针”。第二个:MessageBox的内容。本例中是“显示的信息”。第三个:MessageBox的标题。本例中是“API直接调用MessageBox”第四个:MessageBox上的按钮是什么,是0,只有一个确定(6)运行程序,单击“button1”结果如图1-1所示图1-11.2.2关机相关的API函数和导入方法本小节涉及到了API函数的细节,需要一些相关的知识才能完全理解这些函数的作用。本小节的内容适合查阅使用。1.自定义的结构体TokenPrivilegeLuid为了匹配API函数在调用时相互之间的传值的要求,需要自定义一个向非托管函数之间相互传递值的结构体。只有类具有固定的成员布局,就可以将类的成员传递给非托管的DLL函数。使用如下方法可以实现:[StructLayout(LayoutKind.Sequential,Pack=1)]StructLayout可以使数据依照程序设计者的意愿来排列或打包,LayoutKind.Sequential表示按照结构体定义的顺序排列数据Pack=1表示结构体按一个字节对齐。结构体的名称为TokenPrivilegeLuid,其含义为“令牌权限Luid”,Luid的含义为locallyuniqueidentifier,是Windows系统保证局部唯一的标志,就是指在系统的每一次运行期间保证是唯一的值。结构体的定义如下:[StructLayout(LayoutKind.Sequential,Pack=1)]internalstructTokenPrivilegeLuid{//确定权限数组元素的个数publicintPrivilegesCount;//这里定义存放权限的LuidpubliclongPrivilegesLuid;//权限属性publicintPrivilegesAttributes;}2.获取当前进程句柄的函数函数名:GetCurrentProcess()参数:无返回类型:System.IntPtr,这里给出了在.C#.NET平台下对应的类型返回类型说明:为指针或句柄的特定类型,这里表示句柄类型对应类型后的函数为:IntPtrGetCurrentProcess()功能:获取当前进程的句柄3.打开当前进程访问令牌的函数函数名:OpenProcessToken(HANDLEProcessHandle,DWORDDesiredAccess,PHANDLETokenHandle)参数1的含义:ProcessHandle是要修改访问权限的进程句柄参数1的类型:HANDLE,在C#.NET平台下对应的类型为System.IntPtr参数2的含义:DesiredAccess是要进行的操作类型,操作类型必须使用系统定义好的数值参数2的类型:DWORD,在C#.NET平台下对应的类型为System.Int32,也就是int类型参数3的含义:TokenHandle是返回的访问令牌句柄(指针)参数3的类型:PHANDLE,在C#.NET平台下对应的类型为System.IntPtr返回类型:bool对应类型后的函数为:boolOpenProcessToken(IntPtrProcessHandle,intDesiredAccess,IntPtrTokenHandle)功能:获取进程访问令牌的句柄4.获取系统特定的权限值的函数函数名:LookupPrivilegeValue(LPCTSTRlpSystemName,LPCTSTRlpName,PLUIDlpLuid)参数1的含义:lpSystemName是系统的名称,如果是本地系统只要指明为null参数1的类型:LPCTSTR,在C#.NET平台下对应的类型为string参数2的含义:lpName是权限的名称,具体名称已由系统定义,例如关机的权限名称是“SeShutdownPrivilege”参数2的类型:LPCTSTR,在C#.NET平台下对应的类型为string参数3的含义:lpLuid是返回LUID的指针参数3的类型:PLUID,在C#.NET平台下对应的类型为reflong返回类型:bool对应类型后的函数为:boolLookupPrivilegeValue(stringlpSystemName,stringDesiredAccess,reflongTokenHandle)功能:获获取系统特定的权限值5.调整访问令牌权限的函数函数名:AdjustTokenPrivileges(HANDLETokenHandle,BOOLDisableAllPrivileges,PTOKEN_PRIVILEGESNewState,DWORDBufferLength,PTOKEN_PRIVILEGESPreviousState,PDWORDReturnLength)参数1的含义:TokenHandle是访问令牌的句柄参数1的类型:HANDLE,在.NET平台下对应的类型为System.IntPtr参数2的含义:DisableAllPrivileges是进行权限修改还是清除所有令牌权限,true为清除,false为修改权限参数2的类型:BOOL,在.NET平台下对应的类型为bool参数3的含义:NewState指明要修改的令牌权限参数3的类型:PTOKEN_PRIVILEGES是一个结构体,在C#.NET平台下没有对应的类型,这里就要用到自定义的结构TokenPrivilegeLuid,具体为:refTokenPrivilegeLuid参数4的含义:BufferLength是先前状态结构(PreviousState)的长度,如果PreviousState为空,其值为0参数4的类型:DWORD,在C#.NET平台下对应的类型为System.Int32,也就是int类型参数5的含义:PreviousState是一个指向TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息,可空参数5的类型:PTOKEN_PRIVILEGES,在C#.NET平台下对应的类型为System.IntPtr参数6的含义:ReturnLength为实际PreviousState结构返回的大小值的指针,如果PreviousState为空,这个参数也可以为空。参数6的类型:PDWORD,在C#.NET平台下对应的类型为System.IntPtr返回类型:bool对应类型后的函数为:boolAdjustTokenPrivileges(IntPtrTokenHandle,boolDesiredAccess,refTokenPrivilegeLuidNewState,intBufferLength,IntPtrPreviousState,IntPtrReturnLength)功能:调整访问令牌权限6.关闭计算机的函数函数名:ExitWindowsEx(UINTuFlags,DWORDdwReason)参数1的含义:uFlags指定关机的类型,具体值代表的关闭原因已被系统定义好,例如0x00000000代表注销计算机,0x00000001代表关闭计算机,0x00000002代表重启计算机参数1的类型:UINT,在.NET平台下对应的类型为System.UInt16,若为int类型,其值必须为正数参数2的含义:dwReason是关闭原因,具体值代表的关闭原因已由系统已被系统定义好。例如0值代表无原因关闭系统参数2的类型:DWORD,在.NET平台下对应的类型为System.Int32,也就是int类型返回类型:bool对应类型后的函数为:boolExitWindowsEx(intuFlags,intdwReason)功能:注销当前用户,关闭系统;或者关闭并重启7.导入API函数的代码这里给出了在C#.NET平台下完整的导入API函数的代码//定义向非托管函数之间相互传递值的结构体,[StructLayout(LayoutKind.Sequential,Pack=1)]internalstructTokenPrivilegeLuid{//确定权限数组元素的个数publicintPrivilegesCount;//这里定义存放权限的LuidpubliclongPrivilegesLuid;//权限属性publicintPrivilegesAttributes;}//导入获取当前进程句柄的函数。[DllImport(kernel32.dll,ExactSpelling=true)]internalstaticexternIntPtrGetCurrentP