1.系统启动顺序
(1)自检----BIOS CUP MEMORY HD......
(2)初始化启动------按BIOS的启动顺序进行准备
(3)BOOT加载----NTLDR
(4)启动文件系统
(5)读取boot.ini
(6)检测和配置硬件
(7)Windows内核加载---登录
2.硬盘锁
(1)锁芯----MBR引导程序及加密后的分区表
(2)锁体---Windows下修改硬盘MBR
(I)GetPrivateProfileStringA
(II)CreateFileA
(III)SetFilePointer
(IV)ReadFile
(V)WriteFile
3.打造自己的按键精灵
(1)如何在窗口不激活的状态,获取鼠标位置
(I)SetWindowsHookEx 全局键盘钩子
(II)RegisterRawInputDevices 注册一个原始输入设备
(III)RegisterHotkey注册一个系统热键(快捷键)
(2)如何保存命令序列
数组--有大小的。
list ---链表
(3)如何执行命令序列
keybd_event
mouse_event
4.学习多线程编程技术的意义
(1)多线程简化了应用程序的用户界面的设计
(2)多线程比进行更轻量级,因此更容易创建和撤销
(3)多线程使得该应用程序更容易扩展。
5.进程的概念
进程是指在系统中正在运行的一个应用程序,在传统的操作系统中,是资源的分配单位又是高度运行的单位。在现代操作系统中,进程是资源的分配单位,一个进程通常定义为程序的一个实例。在32位Windows中,进程占据4GB(2的32次方)的虚拟地址空间。
6.进程的组成部分
进程由两个部分组成:
(a)操作系统用来管理进程的内核对象。内核对象也是系统用来存放关于进程的统计信息的地方。
(b)地址空间。它包含所有可执行模块或DLL模块的代码和数据。它还含动态内存分配的空间。如线程堆栈和堆分配空间。
7.进程是不活泼的
进程从来不执行任何东西,它只是线程的容器。若要使进程完成某项操作,它必须拥有一个在它的环境中运行的线程,此线程负责执行包含在进程的地址空间中的代码。
8.进程中的线程
(1)单个进程可能包含若干个线程,这些线程都“同时”执行进程地址空间中的代码。
(2)每个进程至少拥有一个线程,来执行进程的地址空间中的代码。当创建一个进程时,操作系统会自动创建这个进程的第一个线程,称为主线程。伺候,该线程可以创建其他的线程。
9.线程的概念
线程是指进程中执行运行的最小单位,即处理机调度的基本单位。和进程相比,进程是一项任务,线程是独立子任务,可由不同处理器分别来完成,提高了进程运行速度。
10.线程的组成部分
线程由两部分组成:
(I)线程的内核对象,操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。
(II)内存堆栈,它用于维护线程在执行代码时需要的所有参数和局部变量。
11.线程的创建
Create Thread
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
(1)lpThreadAttributes指向SECURITY ATTRIBUTES型态的结构的指针,这个参数通常设置为NULL
(2)dwStackSize设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小。
(3)lpStartAddress指向线程函数的指针,函数名称没有限制,但是必须以下列形式声明:
DWORD WINAPI ThreadProc(LPVOID lpParam),格式不正确将无法调用成功
(4)lpParameter向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL
(5)dwCreationFlags线程标志,可取值如下
(I)CREATE_SUSPENDED(0x00000004):创建一个挂起的线程
(II)0:表示创建后立即激活。
(6)lpThreadId :保存新线程的id。
注意:
(1)如果你不需要线程ID,lpTreadId参数可以被设为NULL,但这只在WindowsNT中才行得通,Windows95中就不行。如果你要写一个程序可以在Windows95中运行,不要把lpThreadId参数设为NULL。
(2)调用CreateThread时,系统会创建一个线程内核对象,这个线程内核对象不是线程本身,而是一个较小的数据结构,操作系统用这个结构来管理线程,可以把线程内核对象想象为一个由线程统计信息构成的小型数据结构。
12.线程的终止
线程可以通过以下3中方式来终止运行
(1)线程函数返回(这是强烈推荐的)
(2)线程可以调用ExitThread函数“杀死”自己(要避免使用这种方法)
(3)同一个进程或另一个进程中的线程调用TerminateThread函数(要避免使用这种方法)
详解:
(1)线程函数返回
设计线程函数时,应该让线程自己返回,这样可以确保以下正确的应用程序清理工作都得以执行。
(I)线程函数中创建的所有C++对象都通过其解析函数被正确销毁。
(II)操作系统正确释放线程栈使用的内存。
(III)操作系统把线程的退出代码(在线程的内核对象中维护)设为线程函数的返回值
(IV)系统递减少线程的内核对象的使用计数。
(2)ExitThread函数
该函数将终止线程的运行,并导致操作系统清理该线程使用的所有操作系统资源。但是,你的C/C++资源(如C++类对象)不会被销毁。有鉴于些,更好的做法是直接从线程函数返回,不要自己调用ExitThread函数。
(3)TerminateThread函数
不同于ExitThread总是“杀死”主调线程,TerminateThread能“杀死”任何线程。TerminateThread函数是异步的,所以函数返回时,并不保证线程已经终止了。
13.线程的挂起和恢复
<1>ResumeThread函数
DWORD ResumeThread(
HANDLE hThread
);
(I)hThread 指定的要重新运行的线程的句柄
如果ResumeThread函数成功,它将返回线程的前一个挂起计数,否则,它将返回0xFFFFFF.
(II)一个线程可以被多次挂起。如果一个线程被挂起三次,那么比需调用ResumeThread三次,才能使起线程运行。
(2)SuspendThread函数
DWORD SuspendThread(
HANDLE hThread
);
(I)hThread 指定的要挂起的线程句柄
(II)如果SuspendThread函数成功,它将返回线程的前一个挂起技术,否则,它将返回0xFFFFFF.
(III)在实际开发中,应用程序调用SuspendThread时必需小心,因为试图挂起一个线程时,我们不知道线程在做什么,比如说如果线程正在分配堆栈中的内存,线程将锁定堆,当其他线程要访问堆的时候,它们的执行将中止,直到第一个线程恢复。
14.线程睡眠
Sleep函数
使得线程在一段时间内自己不需要调度
void Sleep(
DWORD dwMilliseconds
);
(1)dwMilliseconds 毫秒时间
15.编写ThreadDemo程序
(1)修改背景颜色
在MyRegisterClass函数里,修改代码如下:
vcex.hbrBackground=(HBRUSH)(CTLCOLOR_BTN+1);
(2)禁用最大化按钮
在InitInstance函数里修改CreateWindow函数
hWind=CreateWindow(szWindowClass,szTitle,WS_OVERLAPPEDWINDOW&~WS_MAXIMINZEBOX,CW_USEDEFAULT,0,CW_USEDEFAULT,0,NULL,NULL,hInstance,NULL);
另一种方式:
LONG lStyle=GetWindowLong(hWnd,GWL_STYLE);
lStyle&=~WS_MAXIMIZEBOx;
SetWindowLong(hWnd,GWL_StYLE,lStyle);
16.线程同步
(1)当所有线程能够运行而不需要相互通信的时候,操作系统进入最佳运行状态,但很少有线程能够独立运行。
(2)线程间的通讯在很多情况下我们使用全局变量或者一些get函数获得某些共享资源,在这种情况下这些全局变量或者共享资源都是跨线程的,因此不具有线程安全性。而局部变量是具有线程安全的。
17.线程同步
(1)什么事线程同步:线程同步可以理解为线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。
(2)同步对象分类:
(a)用户模式下的同步对象:例如关键段等
优点:速度快
缺点:不能跨进程,容易引起死锁
(b)内核模式下的同步对象:例如互斥量,信号量,事件等
优点:跨进程,安全性高
缺点:速度比用户模式下的同步对象要慢得多
(3)等待函数:DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);
(a)hHandle 对象句柄。可以指定一系列的对象,如Event(事件),Mutex(互斥),Semaphore(信号)、Thread(线程)等
(b)dwMilliseconds 定时时间间隔,单位为milliseconds(毫秒)。如果指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。如果dwMilliseconds为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回,如果dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回
(4)等待函数运用模式
//等待函数 DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds); DWORD dw=WaitForSingleObject(hProcess,50000) { case WAIT_OBJECT_0; break; case WAIT_TIMEOUT: break; case WAIT_FAILED: break; }
18.Mutex互斥对象
(1)用于确保一个线程独占对一个资源的访问
(2)包含一个使用计数器,线程ID,以及一个递归计数器。
(3)线程的id用来标记当前占用这个互斥量的是系统中的哪个线程。
(4)递归计数器标示这个线程占用该互斥量的次数。
(5)互斥量是使用最为频繁的内核对象之一。
19.Mutex互斥对象规则
(1)如果线程ID=0,那么该互斥量不被任何线程占用,处于触发状态。
(2)如果线程ID!=0,那么有一个线程占用了该互斥量,处于未触发状态。
20.CreateMutex函数
(1)CreateMutex函数功能是建立互斥体对象
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,//指向安全属性的指针
BOOL bInitialOwner;//初始化互斥对象的所有者
LPCTSTR lpName//指向互斥对象名的指针
);
(2)lpMutexAttributes指定一个SECURITY_ATTRIBUTES结构,或传递零值。
(3)bInitialOwner ,通常为false,那么线程ID和递归计数器都被设置为0
(4)lpName String ,指定互斥体对象的名字。如已经存在拥有这个名字的一个事件,则打开现有的已命名互斥体。这个名字可能不与现有的事件、信号机、可等待计时器或文件映射相符。
21.ReleaseMutex函数
(1)释放互斥对象
BOOL WIANPI ReleaseMutex(
HANDLE hMutex
);
(2)hMutex:互斥对象的句柄
(3)这个函数会将对象的递归计数器减1.如果线程成功的等待了互斥量对象不止一次,那么线程必须调用release相同的次数才能使对象的递归计数器变为0。当递归计数器为0时,函数还将线程ID设置为0,使互斥量处于触发状态。
22.Mutex互斥对象流程:
(1)假设有一个全局的文件指针,同时又有多个线程需要对该文件指针进行读写。但是为了保证资源的完整性,我们在同一时刻,只允许一个线程进行读写操作。
为了获得对被保护的资源的访问权,线程要调用一个等待函数并转入前面创建的互斥量句柄,在内部,等待函数检查线程ID是否为0(也就是触发状态),如果为0,那么函数会把互斥量句柄中的线程id设为当前调用线程的id,并把递归计数器设为1,此时互斥量就处于非触发状态。对互斥量的线程id赋值和递归计数器增加都是原子操作的。所谓原子操作就是指操作系统保证在完成原子操作之前,不进行线程的切换。然后当前线程继续运行。
(2)假设某一个线程获成功的得到了互斥量,线程就知道自己独占了对受保护的资源的访问。那么任何视图通过等待互斥量来获得对资源的访问权的线程将进入等待状态。很重要的一点是当线程进入等待状态后,是不耗费CPU时钟频率的。当独占线程对资源操作完成后,必须要调用releaseMutex函数来将互斥量的递归计数器减1,如果递归计数器的值为0的话,那么还会将线程id设置为0.这样互斥量又处于触发状态。那任何线性又可以竞争上岗了。
23.临界区(Critical Section)
Win32之中最容易使用的一个同步机制就是critical section。某些共享资源具有互斥性,也就是他要求被互斥地使用,在任何时刻都只能有一个线程使用它。在程序中最常见的是全局变量的互斥使用和缓冲区的互斥使用。临界区的出现形式是一小段代码,这一小段代码包含互斥资源。临界区也有一定的局限性:它只能用于同步单个进程中的线程。在这里大家得注意一点就是:“在任何的关于同步机制中,不论是在哪一个操作系统下,我们都不要长时间锁定一份资源”。如果你一直让资源被锁定,你就会阻止其他线程的执行,并把整个程序带到一个完全停止的状态,我们很难定义所谓“长时间”是多长,但是最可靠的建议就是,不要在一个critical section中调用Sleep()或任何Wait...()等待之类的API函数。
24.InitializeCriticalSection函数
(1)初始化一个临界区
(2)VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection//critical section
);
(2)lpCriticalSection 临界资源对象指针
25.DeleteCriticalSection函数
(1)通过一个临界区对象来发释放所有的资源,使得不在拥有这个对象
void DeleteCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
lpCriticalSection指向一个不再需要的CRITICAL_SECTION变量
26.EnterCriticalSection函数
(1)进入临界区
(2)void EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
lpCriticalSection 临界资源对象指针
27.事件
(1)事件内核对象包含
(a)一个使用计数器
(b)一个表示事件是否是自动重置还是手动重置的布尔值
(c)一个表示事件有没有被触发的布尔值
(d)当触发为true时,等待该事件的线程变为可调度状态。
(e)事件的触发表示一个操作已经完成。
(2)CreateEvent函数
HANDLE CreateEvent(
LPSECURITY_ATIRIBUTES lpEventAttributes,//安全属性
BOOL bManualReset.//复位方式
BOOL bInitialState,//初始状态
LPCTSTR lpName//对象名称
);
(3)ResetEvent函数
(a)设置事件对象为无信号
BOOL ResetEvent(
HANDLE hEvent
);
(b)hEvent 设置事件对象的句柄
28.Semaphore信号对象
(1)概述
(a)信号量用来对资源进行比较
(b)包含一个使用计数器,一个最大资源计数器,和一个当前资源计数器
(c)最大资源计数器用来控制最大的资源数。
(d)当前资源计数器表示当前可用的资源数量。
(2)规则
(a)如果当前资源>0,则信号量处于触发状态
(b)如果当前资源=0,则信号狼处于未触发状态
(c)系统绝对不会让当前资源计数变为负值
(d)当前资源计数绝对不会大于最大资源计数