??xml version="1.0" encoding="utf-8" standalone="yes"?>久久免费香蕉视频,一本久久知道综合久久,久久久久国产一区二区三区http://www.shnenglu.com/shaker/category/2564.htmlshaker's Blog<br>生当作h李ͼM为鬼雄,至今思项,不肯q江东?/description>zh-cnWed, 28 Dec 2011 11:59:44 GMTWed, 28 Dec 2011 11:59:44 GMT60Relocate SVNhttp://www.shnenglu.com/shaker/archive/2011/12/28/162978.htmlshaker(太子)shaker(太子)Wed, 28 Dec 2011 05:59:00 GMThttp://www.shnenglu.com/shaker/archive/2011/12/28/162978.htmlhttp://www.shnenglu.com/shaker/comments/162978.htmlhttp://www.shnenglu.com/shaker/archive/2011/12/28/162978.html#Feedback0http://www.shnenglu.com/shaker/comments/commentRss/162978.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/162978.html在工E的根目录里Z个svn目录Q然后在svn目录里创Z个版本库Q用svnZ下?br />q样q个工程可以被svn理了?br />但是如果整个工程被移动过了,那么svn׃出错Q于是写了个程序来重定位,从此可以把工E放在u盘带着到处跑了?br />
  1 #include "stdafx.h"
  2 
  3 using namespace std;
  4 
  5 void AlertError(DWORD err)
  6 {
  7     LPVOID lpMsgBuf;
  8     LPVOID lpDisplayBuf;
  9     
 10     FormatMessage(
 11         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
 12         FORMAT_MESSAGE_FROM_SYSTEM |
 13         FORMAT_MESSAGE_IGNORE_INSERTS,
 14         NULL,
 15         err,
 16         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 17         (LPTSTR) &lpMsgBuf,
 18         0, NULL );
 19     
 20     lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
 21         strlen((char*)lpMsgBuf) + 40); 
 22     sprintf((char*)lpDisplayBuf,"错误? %d\n信 ? %s", err,lpMsgBuf);
 23     MessageBox(NULL, (LPCTSTR)lpDisplayBuf, "错误", MB_OK|MB_ICONERROR); 
 24     
 25     LocalFree(lpMsgBuf);
 26     LocalFree(lpDisplayBuf);
 27 }
 28 
 29 void GetCommandOutput(char* CmdLine, string& strOutput, bool IncludeErr=false)
 30 {
 31     strOutput="";
 32     SECURITY_ATTRIBUTES  sa;
 33     HANDLE hRead, hWrite;
 34     memset(&sa,0,sizeof(sa));
 35     sa.nLength=sizeof(sa);
 36     sa.bInheritHandle=TRUE;
 37     sa.lpSecurityDescriptor=NULL;
 38     if(CreatePipe(&hRead,&hWrite,&sa,0))
 39     {
 40         STARTUPINFO si;
 41         memset(&si,0,sizeof(si));
 42         si.cb=sizeof(si);
 43         si.hStdOutput=hWrite;
 44         si.hStdInput=GetStdHandle(STD_INPUT_HANDLE);
 45         si.hStdError=(IncludeErr?hWrite:GetStdHandle(STD_ERROR_HANDLE));
 46         si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
 47         si.wShowWindow=SW_HIDE;
 48         
 49         PROCESS_INFORMATION pi;
 50         if(CreateProcess(NULL,CmdLine,&sa,&sa,TRUE,CREATE_NO_WINDOW|NORMAL_PRIORITY_CLASS,
 51             NULL,NULL,&si,&pi))
 52         {
 53             char szBuf[512];
 54             DWORD dwReaded;
 55             if(WaitForSingleObject(pi.hProcess,INFINITE)!=WAIT_TIMEOUT)
 56             {
 57                 CloseHandle(hWrite);
 58                 while(ReadFile(hRead,szBuf,511,&dwReaded,NULL))
 59                 {
 60                     szBuf[dwReaded]='\0';
 61                     strOutput+=szBuf;
 62                 }
 63             }
 64             else
 65             {
 66                 cout<<"WaitForSingleObject错误;"<<endl;
 67                 CloseHandle(hWrite);
 68                 AlertError(GetLastError());
 69             }
 70             CloseHandle(pi.hThread);
 71             CloseHandle(pi.hProcess);
 72         }
 73         else
 74         {
 75             cout<<"建立q程错误;"<<endl;
 76             AlertError(GetLastError());
 77         }
 78         CloseHandle(hRead);
 79     }
 80     else
 81     {
 82         cout<<"建立道错误;"<<endl;
 83         AlertError(GetLastError());
 84     }
 85 }
 86 
 87 int APIENTRY WinMain(HINSTANCE hInstance,
 88                      HINSTANCE hPrevInstance,
 89                      LPSTR     lpCmdLine,
 90                      int       nCmdShow)
 91 {
 92     char szBuf[MAX_PATH+20];
 93     wchar_t wcBuf[MAX_PATH];
 94     int i, j;
 95     string msg;
 96     
 97     GetCommandOutput("svn info --xml",msg);
 98     TiXmlDocument *doc = new TiXmlDocument();
 99     doc->Parse(msg.c_str());
100     TiXmlNode *node;
101     node=doc->FirstChild("info");
102     if(node==NULL)
103     {
104         MessageBox(NULL,"当前目录没有被SVN","l果",MB_OK|MB_ICONINFORMATION);
105         return 0;
106     }
107     node=node->FirstChild("entry");
108     if(node==NULL)
109     {
110         MessageBox(NULL,"当前目录没有被SVN","l果",MB_OK|MB_ICONINFORMATION);
111         return 0;
112     }
113     node=node->FirstChild("wc-info");
114     if(node==NULL)
115     {
116         MessageBox(NULL,"当前目录没有被SVN","l果",MB_OK|MB_ICONINFORMATION);
117         return 0;
118     }
119     node=node->FirstChild("wcroot-abspath");
120     if(node==NULL)
121     {
122         MessageBox(NULL,"当前目录没有被SVN","l果",MB_OK|MB_ICONINFORMATION);
123         return 0;
124     }
125     strcpy(szBuf,node->ToElement()->GetText());
126     delete node;
127     delete doc;
128 
129     j=strlen(szBuf);
130     i=MultiByteToWideChar(CP_UTF8,0,szBuf,j,wcBuf,MAX_PATH);
131     wcBuf[i]=static_cast<wchar_t>(0);
132     strcpy(szBuf,"svn relocate file:///");
133     j=strlen(szBuf);
134     i=WideCharToMultiByte(CP_ACP,0,wcBuf,i,&szBuf[j],MAX_PATH,NULL,NULL);
135     szBuf[i+j]=static_cast<char>(0);
136     SetCurrentDirectory(&szBuf[j]);
137 
138     i+=j;
139     if((szBuf[i-1]=='\\')||(szBuf[i-1]=='/'))
140         strcat(szBuf,"svn/");
141     else
142         strcat(szBuf,"/svn/");
143     for(i=0,j=strlen(szBuf);i<j;i++)
144     {
145         if(szBuf[i]=='\\')
146             szBuf[i]='/';
147     }
148 
149     GetCommandOutput(szBuf,msg,true);
150     MessageBox(NULL,msg.c_str(),"l果",MB_OK);
151 
152     return 0;
153 }


shaker(太子) 2011-12-28 13:59 发表评论
]]>
又是一个APIHOOKhttp://www.shnenglu.com/shaker/archive/2011/07/31/152142.htmlshaker(太子)shaker(太子)Sun, 31 Jul 2011 07:21:00 GMThttp://www.shnenglu.com/shaker/archive/2011/07/31/152142.htmlhttp://www.shnenglu.com/shaker/comments/152142.htmlhttp://www.shnenglu.com/shaker/archive/2011/07/31/152142.html#Feedback0http://www.shnenglu.com/shaker/comments/commentRss/152142.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/152142.html阅读全文

shaker(太子) 2011-07-31 15:21 发表评论
]]>
[zt] Windows APC机制http://www.shnenglu.com/shaker/archive/2011/05/05/145733.htmlshaker(太子)shaker(太子)Thu, 05 May 2011 04:09:00 GMThttp://www.shnenglu.com/shaker/archive/2011/05/05/145733.htmlhttp://www.shnenglu.com/shaker/comments/145733.htmlhttp://www.shnenglu.com/shaker/archive/2011/05/05/145733.html#Feedback0http://www.shnenglu.com/shaker/comments/commentRss/145733.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/145733.html异步q程调用(APCs) 是NT异步处理体系l构中的一个基部分Q理解了它,对于了解NT怎样操作和执行几个核心的pȝ操作很有帮助?/p>

1) APCs允许用户E序和系l元件在一个进E的地址I间内某个线E的上下文中执行代码?br>2) I/O理器用APCs来完成一个线E发L异步的I/O操作。例如:当一个设备驱动调用IoCompleteRequest来通知I/O理器,它已l结束处理一个异步I/OhӞI/O理器排队一个apc到发赯求的U程。然后线E在一个较低IRQLU别Q来执行APC. APC的作用是从系l空间拷贝I/O操作l果和状态信息到U程虚拟内存I间的一个缓冲中?br>3) 使用APC可以得到或者设|一个线E的上下文和挂vU程的执行?/p>

上面是网上找来的Q下面是MSDN上的说明Q?/p>


An asynchronous procedure call (APC) is a function that executes asynchronously in the context of a particular thread. When an APC is queued to a thread, the system issues a software interrupt. The next time the thread is scheduled, it will run the APC function. An APC generated by the system is called a kernel-mode APC. An APC generated by an application is called a user-mode APC. A thread must be in an alertable state to run a user-mode APC.

Each thread has its own APC queue. An application queues an APC to a thread by calling the QueueUserAPC function. The calling thread specifies the address of an APC function in the call to QueueUserAPC. The queuing of an APC is a request for the thread to call the APC function.

q先看一下那些重要结构:

kd> dt KTHREAD
nt!KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 MutantListHead : _LIST_ENTRY
+0x018 InitialStack : Ptr32 Void
+0x01c StackLimit : Ptr32 Void
+0x020 KernelStack : Ptr32 Void
+0x024 ThreadLock : Uint4B
+0x028 ApcState : _KAPC_STATE
+0x028 ApcStateFill : [23] UChar
+0x03f ApcQueueable : UChar
+0x040 NextProcessor : UChar
+0x041 DeferredProcessor : UChar
+0x042 AdjustReason : UChar
+0x043 AdjustIncrement : Char
+0x044 ApcQueueLock : Uint4B
+0x048 ContextSwitches : Uint4B
+0x04c State : UChar
+0x04d NpxState : UChar
+0x04e WaitIrql : UChar
+0x04f WaitMode : Char
+0x050 WaitStatus : Int4B
+0x054 WaitBlockList : Ptr32 _KWAIT_BLOCK
+0x054 GateObject : Ptr32 _KGATE
+0x058 Alertable : UChar
+0x059 WaitNext : UChar
+0x05a WaitReason : UChar
+0x05b Priority : Char
+0x05c EnableStackSwap : UChar
+0x05d SwapBusy : UChar
+0x05e Alerted : [2] UChar
+0x060 WaitListEntry : _LIST_ENTRY
+0x060 SwapListEntry : _SINGLE_LIST_ENTRY
+0x068 Queue : Ptr32 _KQUEUE
+0x06c WaitTime : Uint4B
+0x070 KernelApcDisable : Int2B
+0x072 SpecialApcDisable : Int2B
+0x070 CombinedApcDisable : Uint4B
+0x074 Teb : Ptr32 Void
+0x078 Timer : _KTIMER
+0x078 TimerFill : [40] UChar
+0x0a0 AutoAlignment : Pos 0, 1 Bit
+0x0a0 DisableBoost : Pos 1, 1 Bit
+0x0a0 ReservedFlags : Pos 2, 30 Bits
+0x0a0 ThreadFlags : Int4B
+0x0a8 WaitBlock : [4] _KWAIT_BLOCK
+0x0a8 WaitBlockFill0 : [23] UChar
+0x0bf SystemAffinityActive : UChar
+0x0a8 WaitBlockFill1 : [47] UChar
+0x0d7 PreviousMode : Char
+0x0a8 WaitBlockFill2 : [71] UChar
+0x0ef ResourceIndex : UChar
+0x0a8 WaitBlockFill3 : [95] UChar
+0x107 LargeStack : UChar
+0x108 QueueListEntry : _LIST_ENTRY
+0x110 TrapFrame : Ptr32 _KTRAP_FRAME
+0x114 CallbackStack : Ptr32 Void
+0x118 ServiceTable : Ptr32 Void
+0x11c ApcStateIndex : UChar
+0x11d IdealProcessor : UChar
+0x11e Preempted : UChar
+0x11f ProcessReadyQueue : UChar
+0x120 KernelStackResident : UChar
+0x121 BasePriority : Char
+0x122 PriorityDecrement : Char
+0x123 Saturation : Char
+0x124 UserAffinity : Uint4B
+0x128 Process : Ptr32 _KPROCESS
+0x12c Affinity : Uint4B
+0x130 ApcStatePointer : [2] Ptr32 _KAPC_STATE
+0x138 SavedApcState : _KAPC_STATE
+0x138 SavedApcStateFill : [23] UChar
+0x14f FreezeCount : Char
+0x150 SuspendCount : Char
+0x151 UserIdealProcessor : UChar
+0x152 CalloutActive : UChar
+0x153 Iopl : UChar
+0x154 Win32Thread : Ptr32 Void
+0x158 StackBase : Ptr32 Void
+0x15c SuspendApc : _KAPC
+0x15c SuspendApcFill0 : [1] UChar
…………

…………

上面U色部分是APC机制用到的几个字D!Q?/p>

kd> dt _KAPC_STATE
nt!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRYQ每个指针指向_KAPCl构
+0x010 Process : Ptr32 _KPROCESS
+0x014 KernelApcInProgress : UChar
+0x015 KernelApcPending : UChar
+0x016 UserApcPending : UChar

昄Q这里的 ApcListHead 是 APC 队列头。不q这是个大小?2 的数l,说明实际
?每个U程)有两?APC 队列。这是因?APC 函数分ؓ用户 APC 和内?APC 两种Q各?br>各的队列。所谓用?APCQ是指相应的 APC 函数位于用户I间、在用户I间执行Q而内?br>APCQ则相应?APC 函数为内核函数?/p>

SavedApcState也是个_KAPC_STATEl构Q当当前E暂?#8220;挂靠(Attach)”到另一个进E的地址I间的时侯,ApcState拷贝到SavedApcState暂时存放Q?/p>

当然Q还要有状态信息说明本U程当前是处?#8220;原始环境”q是“挂靠环境”Q这是 ApcStateIndex 的作用,代码中ؓ ApcStateIndex的值定义了一U枚丄型:

typedef enum _KAPC_ENVIRONMENT {
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT;

实际可用?ApcStateIndex 的只?OriginalApcEnvironment?AttachedApcEnvironment?/p>

KAPC_STATE 指针数组 ApcStatePointer[2]Q就用ApcStateIndex 的当前gZ标,而数l中的指针则Ҏ情况可以分别指向两个APC_STATE 数据l构中的一个?/p>

kd> dt _KAPC ;APC对象
nt!_KAPC
+0x000 Type : UChar
+0x001 SpareByte0 : UChar
+0x002 Size : UChar
+0x003 SpareByte1 : UChar
+0x004 SpareLong0 : Uint4B
+0x008 Thread : Ptr32 _KTHREAD
+0x00c ApcListEntry : _LIST_ENTRY
+0x014 KernelRoutine : Ptr32 void
+0x018 RundownRoutine : Ptr32 void
+0x01c NormalRoutine : Ptr32 void
+0x020 NormalContext : Ptr32 Void
+0x024 SystemArgument1 : Ptr32 Void
+0x028 SystemArgument2 : Ptr32 Void
+0x02c ApcStateIndex : Char
+0x02d ApcMode : Char
+0x02e Inserted : UChar

KernelRoutine、RundownRoutine、NormalRoutine。其中只?NormalRoutine才指?执行)APC 函数的请求者所提供的函敎ͼ其余两个都是辅助性的Q?/p>

NTKERNELAPI
VOID
KeInitializeApc (
__out PRKAPC Apc,
__in PRKTHREAD Thread,
__in KAPC_ENVIRONMENT Environment,
__in PKKERNEL_ROUTINE KernelRoutine,
__in_opt PKRUNDOWN_ROUTINE RundownRoutine,
__in_opt PKNORMAL_ROUTINE NormalRoutine,
__in_opt KPROCESSOR_MODE ProcessorMode,
__in_opt PVOID NormalContext
);

q个函数主要用来初始化ApcQ_KAPCQ这个结构的Q如果Environment==CurrentApcEnvironment,Apc->ApcStateIndexqKTHREAD中的ApcStateIndex军_Q但Environment不能大于KTHREAD中的ApcStateIndexQ?/p>

最后,APC h的模式ProcessorModeQ但是有个例外,那就是:如果指针NormalRoutine ?0Q那么实际的模式变成?KernelMode。这是因为在q种情况下没有用L间APC函数可以执行Q?唯一得到执行的是KernelRoutineQ?/p>

最后,KeInitializeApc 讄Inserted域ؓFALSE。然而初始化APC对象Qƈ没有把它存放到相应的APC队列中?/p>


NTKERNELAPI
BOOLEAN
KeInsertQueueApc (
__inout PRKAPC Apc,
__in_opt PVOID SystemArgument1,
__in_opt PVOID SystemArgument2,
__in KPRIORITY Increment
);

据APCh的具体情况,有时候要插在队列的前_一般则挂在队列的尾部?/p>

_KiServiceExit:

cli ; disable interrupts
DISPATCH_USER_APC ebp, ReturnCurrentEax

;
; Exit from SystemService
;

EXIT_ALL NoRestoreSegs, NoRestoreVolatile ;q个宏以后再?/p>

DISPATCH_USER_APC macro TFrame, ReturnCurrentEax
local a, b, c
c:
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)

test byte ptr [TFrame]+TsEflags+2, EFLAGS_V86_MASK/010000h ; is previous mode v86?
jnz short b ; if nz, yes, go check for APC
test byte ptr [TFrame]+TsSegCs,MODE_MASK ; is previous mode user mode?
jz a ; No, previousmode=Kernel, jump out
b: mov ebx, PCR[PcPrcbData+PbCurrentThread]; get addr of current thread
mov byte ptr [ebx]+ThAlerted, 0 ; clear kernel mode alerted
cmp byte ptr [ebx]+ThApcState.AsUserApcPending, 0
je a ; if eq, no user APC pending

mov ebx, TFrame
ifnb <ReturnCurrentEax>;条g宏汇~,如果ReturnCurrentEax参数不ؓI,则编译!

;DISPATCH_USER_APC ebp, ReturnCurrentEaxQ显然这里是~译的!
mov [ebx].TsEax, eax ; Store return code in trap frame
mov dword ptr [ebx]+TsSegFs, KGDT_R3_TEB OR RPL_MASK
mov dword ptr [ebx]+TsSegDs, KGDT_R3_DATA OR RPL_MASK
mov dword ptr [ebx]+TsSegEs, KGDT_R3_DATA OR RPL_MASK
mov dword ptr [ebx]+TsSegGs, 0
endif

;
; Save previous IRQL and set new priority level
;
RaiseIrql APC_LEVEL
push eax ; Save OldIrql

sti ; Allow higher priority ints

;
; call the APC delivery routine.
;
; ebx - Trap frame
; 0 - Null exception frame
; 1 - Previous mode
;
; call APC deliver routine
;

stdCall _KiDeliverApc, <1, 0, ebx> ;1是UserMode

pop ecx ; (ecx) = OldIrql
LowerIrql ecx

ifnb <ReturnCurrentEax> ;同上分析
mov eax, [ebx].TsEax ; Restore eax, just in case
endif

cli
jmp b ; 注意q个循环Q!

ALIGN 4
a:
endm

q段代码主要查:

卛_q回的是否用L间?br>是否有用户APCh正在{待执行

条gW合才用KiDeliverApc真正投递APC。注意代码jmp bQ好像在q回用户I间前KiDeliverApc会被循环调用直到没有user APCQ其实不是的QKiDeliverApc每处理完一个User APC把UserApcPending清零Q所以User APCs在返回用L间时q是只能投递一ơ!KiDeliverApc中用while处理完所有Kernel Mode APCsQ但User Mode APC却只处理一个!事实上User APC的投递是很特D的Q以后会讲到Q而且每次只能投递一ơ!


前面讲过QKTHREAD 中有两个 KAPC_STATE 数据l构Q一个是 ApcStateQ另一个是SavedApcStateQ二者都有APC 队列Q但是要投递的只是ApcState 中的队列?/p>

KiDeliverApc (
IN KPROCESSOR_MODE PreviousMode,//写成DeliverMode不是更好
IN PKEXCEPTION_FRAME ExceptionFrame,//q个参数几乎是0
IN PKTRAP_FRAME TrapFrame
)

q个函数里面q比较复杂,代码不帖了?/p>

参数PreviousMode表示需?#8220;投?#8221;哪一U?APCQ可以是UserModeQ也可以是KernelMode。不q,KernelMode 实表示只要求执行内?APCQ而UserMode 却表C在执行内核 APC 之外再执行用户APC?/p>

The Windows operating system uses three kinds of APCs:


User APCs run strictly in user mode and only when the current thread is in an alertable wait state. The operating system uses user APCs to implement mechanisms such as overlapped I/O and the QueueUserApc Win32 routine. Qrun IRQL = PASSIVE_LEVELQ?br>Normal kernel APCs run in kernel mode at IRQL = PASSIVE_LEVEL. A normal kernel APC preempts all user-mode code, including user APCs. Normal kernel APCs are generally used by file systems and file-system filter drivers.
Special kernel APCs run in kernel mode at IRQL = APC_LEVEL. A special kernel APC preempts user-mode code and kernel-mode code that executes at IRQL = PASSIVE_LEVEL, including both user APCs and normal kernel APCs. The operating system uses special kernel APCs to handle operations such as I/O request completion.
从代码的角度看是q样的:

User APCs
_KAPC.ApcMode==UserMode,_KAPC.KernelRoutine!=NULL,_KAPC.NormolRoutine!=NULL

Normal kernel APCs

_KAPC.ApcMode==KernelMode,_KAPC.KernelRoutine!=NULL,_KAPC.NormolRoutine!=NULL

Special kernel APCs

_KAPC.ApcMode==KernelMode,_KAPC.KernelRoutine!=NULL,_KAPC.NormolRoutine==NULL

有一点它们的_KAPC.KernelRoutine肯定不ؓI。ƈ且,如果NormolRoutine也不为空Q那么KernelRoutine都在NormolRoutine被调用前被调用!Q?/p>

上文中讲到投递User Mode APCs是很Ҏ的,道理很简单,因ؓUser Mode APC是ring3下的回调函数Q显然ring0中的KiDeliverAPCQ)不能像Kernel Mode APC那样直接callQ必要先回到ring3环境下。当然不能像普通情况那栯回(否则回到ring3pȝ调用的地方了Q,只有一个办法,那就是修改TrapFrame Q欺骗系l返?#8220;APC回调函数”QKiInitializeUserApc是q么做的Q?/p>

该函数首先把TrapFrame转化为ContextFrameQ然后移动用hESP指针Q分配大UsizeofQCONTEXTQ?sizeofQKAPC_RECORDQ个字节:

UserStack-> ……

KAPC_RECORD

……

……

CONTEXT

TopOfStack-> ……

KAPC_RECORD是KiInitializeUserApc的最后四个参?/p>

nt!_KAPC_RECORD

+0x000 NormalRoutine : Ptr32 void

+0x004 NormalContext : Ptr32 Void

+0x008 SystemArgument1 : Ptr32 Void

+0x00c SystemArgument2 : Ptr32 Void

CONTEXTl构主要来存放被修改前的TrapFrameQ之所以用CONTEXTl构是跟APC函数q回有关Q?/p>

TrapFrame->HardwareEsp = UserStack;

TrapFrame->Eip = (ULONG)KeUserApcDispatcher;

TrapFrame->ErrCode = 0;

从上面的代码看到实修改了TrapFrameQƈ且返回到的是ring3下的KeUserApcDispatcherQ刚才说的_KAPC_RECORD其实也是它的参数Q真正我们的User APC回调函数是由KeUserApcDispatcher调用的!

.func KiUserApcDispatcher@16

.globl _KiUserApcDispatcher@16

_KiUserApcDispatcher@16:

/* Setup SEH stack */

lea eax, [esp+CONTEXT_ALIGNED_SIZE+16]

mov ecx, fs:[TEB_EXCEPTION_LIST]

mov edx, offset _KiUserApcExceptionHandler

mov [eax], ecx

mov [eax+4], edx

/* Enable SEH */

mov fs:[TEB_EXCEPTION_LIST], eax

/* Put the Context in EDI */

pop eax

lea edi, [esp+12]

/* Call the APC Routine */

call eax

/* Restore exception list */

mov ecx, [edi+CONTEXT_ALIGNED_SIZE]

mov fs:[TEB_EXCEPTION_LIST], ecx

/* Switch back to the context */

push 1 ; TestAlert

push edi ;edi->CONTEXTl构

call _ZwContinue@8

;;不会q回到这里的

上面的代码ƈ不难理解Q我们的User APC回调函数q回后,立即调用了ZwContinueQ这是个ntdll中的导出函数Q这个函数又通过pȝ调用q入kernel中的NtContinueQ?/p>

NTSTATUS

; NtContinue (

; IN PCONTEXT ContextRecord,

; IN BOOLEAN TestAlert

; )

;

; Routine Description:

;

; This routine is called as a system service to continue execution after

; an exception has occurred. Its function is to transfer information from

; the specified context record into the trap frame that was built when the

; system service was executed, and then exit the system as if an exception

; had occurred.

;

; WARNING - Do not call this routine directly, always call it as

; ZwContinue!!! This is required because it needs the

; trapframe built by KiSystemService.

;

; Arguments:

;

; KTrapFrame (ebp+0: after setup) -> base of KTrapFrame

;

; ContextRecord (ebp+8: after setup) = Supplies a pointer to a context rec.

;

; TestAlert (esp+12: after setup) = Supplies a boolean value that specifies

; whether alert should be tested for the previous processor mode.

;

; Return Value:

;

; Normally there is no return from this routine. However, if the specified

; context record is misaligned or is not accessible, then the appropriate

; status code is returned.

;

;--

NcTrapFrame equ [ebp + 0]

NcContextRecord equ [ebp + 8]

NcTestAlert equ [ebp + 12]

align dword

cPublicProc _NtContinue ,2

push ebp ;ebp->TrapFrame

;

; Restore old trap frame address since this service exits directly rather

; than returning.

;

mov ebx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address

mov edx, [ebp].TsEdx ; restore old trap frame address

mov [ebx].ThTrapFrame, edx ;

;

; Call KiContinue to load ContextRecord into TrapFrame. On x86 TrapFrame

; is an atomic entity, so we don't need to allocate any other space here.

;

; KiContinue(NcContextRecord, 0, NcTrapFrame)

;

mov ebp,esp

mov eax, NcTrapFrame

mov ecx, NcContextRecord

stdCall _KiContinue, <ecx, 0, eax>

or eax,eax ; return value 0?

jnz short Nc20 ; KiContinue failed, go report error

;

; Check to determine if alert should be tested for the previous processor mode.

;

cmp byte ptr NcTestAlert,0 ; Check test alert flag

je short Nc10 ; if z, don't test alert, go Nc10

mov al,byte ptr [ebx]+ThPreviousMode ; No need to xor eax, eax.

stdCall _KeTestAlertThread, <eax> ; test alert for current thread

;如果User APCs不ؓI,它会讄UserApcPending,

;跟Alertable无关

Nc10: pop ebp ; (ebp) -> TrapFrame

mov esp,ebp ; (esp) = (ebp) -> trapframe

jmp _KiServiceExit2 ; common exit

Nc20: pop ebp ; (ebp) -> TrapFrame

mov esp,ebp ; (esp) = (ebp) -> trapframe

jmp _KiServiceExit ; common exit

stdENDP _NtContinue

NtContinue把CONTEXTl构转化成TrapFrameQ回复原来的陷阱帧)Q然后就从KiServiceExit2处退出系l调用!

;++

;

; _KiServiceExit2 - same as _KiServiceExit BUT the full trap_frame

; context is restored

;

;--

public _KiServiceExit2

_KiServiceExit2:

cli ; disable interrupts

DISPATCH_USER_APC ebp

;

; Exit from SystemService

;

EXIT_ALL ; RestoreAll

KiServiceExit2跟KiServiceExit差不多,只是宏参数的不同!同样如果q有User APC又会q入上文描述的情形,直到没有User APCQ至此才会返回真正发起原始系l调用的地方Q?/p>


本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/better0332/archive/2009/06/29/4306683.aspx



shaker(太子) 2011-05-05 12:09 发表评论
]]>
[zt]谈谈对APC的一点理?/title><link>http://www.shnenglu.com/shaker/archive/2011/05/05/145731.html</link><dc:creator>shaker(太子)</dc:creator><author>shaker(太子)</author><pubDate>Thu, 05 May 2011 03:46:00 GMT</pubDate><guid>http://www.shnenglu.com/shaker/archive/2011/05/05/145731.html</guid><wfw:comment>http://www.shnenglu.com/shaker/comments/145731.html</wfw:comment><comments>http://www.shnenglu.com/shaker/archive/2011/05/05/145731.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/shaker/comments/commentRss/145731.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/shaker/services/trackbacks/145731.html</trackback:ping><description><![CDATA[<span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; FONT: 16px Arial,Microsoft Yahei,Simsun,sans-serif; WHITE-SPACE: normal; ORPHANS: 2; COLOR: rgb(83,100,130); WORD-SPACING: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" class=Apple-style-span><span style="LINE-HEIGHT: 18px; FONT-FAMILY: 'Lucida Grande', 'Trebuchet MS', Verdana, Helvetica, Arial, sans-serif; COLOR: rgb(51,51,51); FONT-SIZE: 13px" class=Apple-style-span>异步q程调用(APCs) 是NT异步处理体系l构中的一个基部分Q理解了它,对于了解NT怎样操作和执行几个核心的pȝ操作很有帮助?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">1) APCs允许用户E序和系l元件在一个进E的地址I间内某个线E的上下文中执行代码?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">2) I/O理器用APCs来完成一个线E发L异步的I/O操作。例如:当一个设备驱动调用IoCompleteRequest来通知I/O理器,它已l结束处理一个异步I/OhӞI/O理器排队一个apc到发赯求的U程。然后线E在一个较低IRQLU别Q来执行APC. APC的作用是从系l空间拷贝I/O操作l果和状态信息到U程虚拟内存I间的一个缓冲中?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">3) 使用APC可以得到或者设|一个线E的上下文和挂vU程的执行?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">管APCs在nt体系l构下被q泛使用Q但是关于怎样使用它的文却非常的~Z。本我们详l介l下ntpȝ是怎样处理APCs的,q且记录导出的nt函数Q方便设备驱动开发者在他们的程序中使用APCs。我也会展示一个非常可靠的NT的APC调度子程序KiDeliverApc的实玎ͼ来帮助你更好的掌握APC调度的内q?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">APC对象<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">在NT中,有两U类型的APCsQ用h式和内核模式。用户APCsq行在用h式下目标U程当前上下文中Qƈ且需要从目标U程得到许可来运行。特别是Q用h式的APCs需要目标线E处在alertable{待状态才能被成功的调度执行。通过调用下面L一个函敎ͼ都可以让U程q入q种状态。这些函数是QKeWaitForSingleObject, KeWaitForMultipleObjects, KeWaitForMutexObject, KeDelayExecutionThread?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">对于用户模式下,可以调用函数SleepEx, SignalObjectAndWait, WaitForSingleObjectEx, WaitForMultipleObjectsEx,MsgWaitForMultipleObjectsEx 都可以目标U程处于alertable{待状态,从而让用户模式APCs执行,原因是这些函数最l都是调用了内核中的KeWaitForSingleObject, KeWaitForMultipleObjects, KeWaitForMutexObject, KeDelayExecutionThread{函数。另外通过调用一个未公开的alert-test服务KeTestAlertThreadQ用LE可以用户模式APCs执行?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">当一个用h式APC被投递到一个线E,调用上面的等待函敎ͼ如果q回{待状态STATUS_USER_APCQ在q回用户模式Ӟ内核转去控制APC例程Q当APC例程完成后,再l线E的执行.<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">和用h式APCs比较Q内核模式APCs执行在内核模式下。可以被划分为常规的和特D的两类。当APCs被投递到一个特D的U程Q特D的内核模式APCs不需要从U程得到许可来运行。然而,常规的内核模式APCs在他们成功执行前Q需要有特定的环境。此外,Ҏ的内核APC被尽可能快地执行Q既只要APC_LEVELU上有可调度的活动。在很多情况下,Ҏ的内核APC甚至能唤醒阻塞的U程。普通的内核APC仅在所有特DAPC都被执行完,q且目标U程仍在q行Q同时该U程中也没有其它内核模式APC正执行时才执行。用h式APC在所有内核模式APC执行完后才执行,q且仅在目标U程有alertable属性时才执行?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">每一个等待执行的APC都存在于一个线E执行体Q由内核理的队列中。系l中的每一个线E都包含两个APC队列Q一个是为用h式APCs,另一个是为内核模式APCs的?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">NT通过一个成为KAPC的内核控制对象来描述一个AQEQ尽DQO中没有明的文化AQEs,但是在NTDDK.H中却非常清楚的定义了QPQ对象。从下面的KAPC对象的定义看Q有些是不需要说明的。像Type和Size。Type表示了这是一个APC内核对象。在nt中,每一个内核对象或者执行体对象都有Type和Sizeq两个域。由此处理函数可以确定当前处理的对象。Size表示一个字寚w的结构体的大。也是指明了对象占的内存空间大。Spare0看v来有些晦涩难懂,但是它是没用什么Q何深q的意义Q仅仅是Z内存补齐。其他的域将在下面的幅中介l?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">//-------------------------------------------------------------------------------------------------------<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">几个函数声明和结构定义:<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">typedef struct _KAPC {<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">CSHORT Type;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">CSHORT Size;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">ULONG Spare0;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">struct _KTHREAD *Thread;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">LIST_ENTRY ApcListEntry;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">PKKERNEL_ROUTINE KernelRoutine;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">PKRUNDOWN_ROUTINE RundownRoutine;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">PKNORMAL_ROUTINE NormalRoutine;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">PVOID NormalContext;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">//<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">// N.B. The following two members MUST be together.<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">//<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">PVOID SystemArgument1;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">PVOID SystemArgument2;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">CCHAR ApcStateIndex;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">KPROCESSOR_MODE ApcMode;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">BOOLEAN Inserted;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">} KAPC, *PKAPC, *RESTRICTED_POINTER PRKAPC;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">//------<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">APC环境<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">一个线E在它执行的L时刻Q假讑ֽ前的IRQL是在PassiveU,它可能需要时在其他的进E上下文中执行代码,Z完成q个操作Q线E调用系l功能函数KeAttachProcessQ在从这个调用返回时Q线E执行在另一个进E的地址I间。先前所有在U程自己的进E上下文中等待执行的APCs,׃q时其所属进E的地址I间不是当前可用的,因此他们不能被投递执行。然而,新的插入到这个线E的APCs可以执行在这个新的进E空间。甚臛_U程最后从新的q程中分LQ新的插入到q个U程的APCsq可以在q个U程所属的q程上下文中执行?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">Z辑ֈ控制APC传送的q个E度QNT中每个线E维护了两个APC环境或者说是状态。每一个APC环境包含了用h式的APC队列和内核模式的APC队列Q一个指向当前进E对象的指针和三个控制变量,用于指出Q是否有未决的内核模式APCs(KernelApcPending),是否有常规内核模式APC在进行中(KernelApcInProgress)Q是否有未决的用h式的APC(UserApcPending). q些APC的环境保存在U程对象的ApcStatePointer域中。这个域是由2个元素组成的数组。即Q?0x138 ApcStatePointer : [2] Ptr32 _KAPC_STATE<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">typedef struct _KAPC_STATE {<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">LIST_ENTRY ApcListHead[MaximumMode];<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">struct _KPROCESS *Process;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">BOOLEAN KernelApcInProgress;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">BOOLEAN KernelApcPending;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">BOOLEAN UserApcPending;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">} KAPC_STATE, *PKAPC_STATE, *PRKAPC_STATE;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">lkd> dt _kthread<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">ntdll!_KTHREAD<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x000 Header : _DISPATCHER_HEADER<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x010 MutantListHead : _LIST_ENTRY<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x018 InitialStack : Ptr32 Void<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x01c StackLimit : Ptr32 Void<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x020 Teb : Ptr32 Void<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x024 TlsArray : Ptr32 Void<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x028 KernelStack : Ptr32 Void<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x02c DebugActive : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x02d State : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x02e Alerted : [2] UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x030 Iopl : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x031 NpxState : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x032 Saturation : Char<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x033 Priority : Char<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x034 ApcState : _KAPC_STATE<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x04c ContextSwitches : Uint4B<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x050 IdleSwapBlock : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x051 Spare0 : [3] UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x054 WaitStatus : Int4B<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x058 WaitIrql : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x059 WaitMode : Char<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x05a WaitNext : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x05b WaitReason : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x05c WaitBlockList : Ptr32 _KWAIT_BLOCK<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x060 WaitListEntry : _LIST_ENTRY<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x060 SwapListEntry : _SINGLE_LIST_ENTRY<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x068 WaitTime : Uint4B<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x06c BasePriority : Char<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x06d DecrementCount : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x06e PriorityDecrement : Char<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x06f Quantum : Char<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x070 WaitBlock : [4] _KWAIT_BLOCK<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x0d0 LegoData : Ptr32 Void<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x0d4 KernelApcDisable : Uint4B<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x0d8 UserAffinity : Uint4B<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x0dc SystemAffinityActive : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x0dd PowerState : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x0de NpxIrql : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x0df InitialNode : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x0e0 ServiceTable : Ptr32 Void<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x0e4 Queue : Ptr32 _KQUEUE<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x0e8 ApcQueueLock : Uint4B<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x0f0 Timer : _KTIMER<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x118 QueueListEntry : _LIST_ENTRY<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x120 SoftAffinity : Uint4B<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x124 Affinity : Uint4B<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x128 Preempted : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x129 ProcessReadyQueue : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x12a KernelStackResident : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x12b NextProcessor : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x12c CallbackStack : Ptr32 Void<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x130 Win32Thread : Ptr32 Void<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x134 TrapFrame : Ptr32 _KTRAP_FRAME<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x138 ApcStatePointer : [2] Ptr32 _KAPC_STATE<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x140 PreviousMode : Char<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x141 EnableStackSwap : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x142 LargeStack : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x143 ResourceIndex : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x144 KernelTime : Uint4B<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x148 UserTime : Uint4B<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x14c SavedApcState : _KAPC_STATE<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x164 Alertable : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x165 ApcStateIndex : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x166 ApcQueueable : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x167 AutoAlignment : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x168 StackBase : Ptr32 Void<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x16c SuspendApc : _KAPC<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x19c SuspendSemaphore : _KSEMAPHORE<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x1b0 ThreadListEntry : _LIST_ENTRY<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x1b8 FreezeCount : Char<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x1b9 SuspendCount : Char<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x1ba IdealProcessor : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x1bb DisableBoost : UChar<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">主APC环境是位于线E对象的ApcState 域,卻I<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">+0x034 ApcState : _KAPC_STATE<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">U程中等待在当前q程上下文中执行的APC保存在ApcState的队列中。无ZӞNT的APCz֏?dispatcher)和其他系l元件查询一个线E未决的APCs? 他们都会查主APC环境Q如果这里有M未决的APCs,׃马上被投递,或者修改它的控制变量稍后投递?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">W二个APC环境是位于线E对象的SavedApcState域,当线E时挂接到其他q程Ӟ它是用来备䆾主APC环境的?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">当一个线E调用KeAttachProcessQ在另外的进E上下文中执行后l的代码ӞApcState域的内容p拯到SavedApcState域。然后ApcState域被清空Q它的APC队列重新初始化,控制变量讄?Q当前进E域讄为新的进E。这些步骤成功的保先前在线E所属的q程上下文地址I间中等待的APCsQ当U程q行在其它不同的q程上下文时Q这些APCs不被传送执行。随后,ApcStatePointer域数l内容被更新来反映新的状态,数组中第一个元素指向SavedApcState域,W二个元素指向ApcState域,表明U程所属进E上下文的APC环境位于SavedApcState域。线E的新的q程上下文的APC环境位于ApcState域。最后,当前q程上下文切换到新的q程上下文?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">对于一个APC对象Q决定当前APC环境的是ApcStateIndex域。ApcStateIndex域的g为ApcStatePointer域数l的索引来得到目标APC环境指针。随后,目标APC环境指针用来在相应的队列中存放apc对象.<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">当线E从新的q程中脱L(KeDetachProcess), M在新的进E地址I间中等待执行的未决的内核APCs被派发执行。随后SavedApcState 域的内容被拷贝回ApcState域。SavedApcState 域的内容被清I,U程的ApcStateIndex域被设ؓOriginalApcEnvironmentQApcStatePointer域更斎ͼ当前q程上下文切换到U程所属进E?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">使用APCs<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">讑֤驱动E序使用两个主要函数来利用APCs, W一个是KeInitializeApcQ用来初始化APC对象。这个函数接受一个驱动分配的APC对象Q一个目标线E对象指针,APC环境索引Q指出APC对象存放于哪个APC环境Q,APC的kernel,rundown和normal例程指针QAPCcdQ用h式或者内核模式)和一个上下文参数?函数声明如下Q?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">NTKERNELAPI<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">VOID<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">KeInitializeApc (<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PRKAPC Apc,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PKTHREAD Thread,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN KAPC_ENVIRONMENT Environment,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PKKERNEL_ROUTINE KernelRoutine,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PKNORMAL_ROUTINE NormalRoutine OPTIONAL,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN KPROCESSOR_MODE ApcMode,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PVOID NormalContext<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">);<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">typedef enum _KAPC_ENVIRONMENT {<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">OriginalApcEnvironment,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">AttachedApcEnvironment,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">CurrentApcEnvironment<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">} KAPC_ENVIRONMENT;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">KeInitializeApc 首先讄APC对象的Type和Size域一个适当的|然后查参数Environment的|如果是CurrentApcEnvironmentQ那么ApcStateIndex域设|ؓ目标U程的ApcStateIndex域。否则,ApcStateIndex域设|ؓ参数Environment的倹{随后,函数直接用参数设|APC对象ThreadQRundownRoutineQKernelRoutine域的倹{ؓ了正地定APC的类型,KeInitializeApc 查参数NormalRoutine的|如果是NULLQApcMode域的D|ؓKernelModeQNormalContext域设|ؓNULL。如果NormalRoutine的g是NULLQ这时候它一定指向一个有效的例程Q就用相应的参数来设|ApcMode域和NormalContext域。最后,KeInitializeApc 讄Inserted域ؓFALSE.然而初始化APC对象Qƈ没有把它存放到相应的APC队列中?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">从这个解释看Q你可以了解到APCs对象如果~少有效的NormalRoutineQ就会被当作内核模式APCs.其是它们会被认为是Ҏ的内核模式APCs.<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">实际上,I/O理器就是用q类的APC来完成异步I/O操作。相反地QAPC对象定义了有效的NormalRoutineQƈ且ApcMode域是KernelModeQ就会被当作常规的内核模式APCs,否则׃被当作是用户模式APCs. NTDDK.H中KernelRoutine, RundownRoutine, and NormalRoutine 的定义如下:<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">typedef<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">VOID<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">(*PKKERNEL_ROUTINE) (<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN struct _KAPC *Apc,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN OUT PKNORMAL_ROUTINE *NormalRoutine,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN OUT PVOID *NormalContext,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN OUT PVOID *SystemArgument1,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN OUT PVOID *SystemArgument2<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">);<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">typedef<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">VOID<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">(*PKRUNDOWN_ROUTINE) (<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN struct _KAPC *Apc<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">);<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">typedef<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">VOID<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">(*PKNORMAL_ROUTINE) (<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PVOID NormalContext,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PVOID SystemArgument1,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PVOID SystemArgument2<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">);<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">//------------------<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">通常Q无论是什么类型,每个APC对象必须要包含一个有效的KernelRoutine 函数指针。当q个APC被NT的APC dispatcher传送执行时Q这个例E首先被执行。用h式的APCs必须包含一个有效的NormalRoutine 函数指针Q这个函数必d用户内存区域。同LQ常规内核模式APCs也必d含一个有效的NormalRoutineQ但是它像KernelRoutine一栯行在内核模式。作为可选择的,Lcd的APC都可以定义一个有效的RundownRoutineQ这个例E必d内核内存区域Qƈ且仅仅当pȝ需要释放APC队列的内ҎQ才被调用。例如线E退出时Q在q种情况下,KernelRoutine和NormalRoutine都不执行Q只有RundownRoutine执行。没有这个例E的APC对象会被删除?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">CQ投递APCsC个线E的动作Q仅仅是操作pȝ调用KiDeliverApc完成的。执行APC实际上就是调用APC内的例程?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">一旦APC对象完成初始化后Q设备驱动调用KeInsertQueueApc来将APC对象存放到目标线E的相应的APC队列中。这个函数接受一个由KeInitializeApc完成初始化的APC对象指针Q两个系l参数和一个优先增量。跟传递给KeInitializeApc函数的参数context 一Pq两个系l参数只是在APC的例E执行时Q简单的传递给APC的例E?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">NTKERNELAPI<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">BOOLEAN<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">KeInsertQueueApc (<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PRKAPC Apc,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PVOID SystemArgument1,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PVOID SystemArgument2,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN KPRIORITY Increment<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">);<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">//-----------------<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">在KeInsertQueueApc APC对象存放到目标线E相应的APC队列之前Q它首先查目标线E是否是APC queueable。如果不是,函数立即q回FALSE.如果是,函数直接用参数设|SystemArgument1域和SystemArgument2 域,随后Q函数调用KiInsertQueueApc来将APC对象存放到相应的APC队列?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">KiInsertQueueApc 仅仅接受一个APC对象和一个优先增量。这个函数首先得到线EAPC队列的spinlockq且持有它,防止其他U程修改当前U程的APCl构。随后,查APC对象的Inserted 域。如果是TRUE,表明q个APC对象已经存放到APC队列中了Q函数立卌回FALSE.如果APC对象的Inserted 域是FALSE.函数通过ApcStateIndex域来定目标APC环境Q然后把APC对象存放到相应的APC队列中,卛_APC对象中的ApcListEntry 域链入到APC环境的ApcListHead域中。链入的位置由APC的类型决定。常规的内核模式APC,用户模式APC都是存放到相应的APC队列的末端。相反的Q如果队列中已经存放了一些APC对象Q特D的内核模式APC存放到队列中W一个常规内核模式APC对象的前面。如果是内核定义的一个当U程退出时使用的用户APC,它也会被攑֜相应的队列的前面。然后,U程的主APC环境中的UserApcPending域杯讄为TRUE。这时KiInsertQueueApc 讄APC对象的Inserted 域ؓTRUEQ表明这个APC对象已经存放到APC队列中了。接下来Q检查这个APC对象是否被排队到U程的当前进E上下文APC环境中,如果不是Q函数立卌回TRUE。如果这是一个内核模式APCQ线E主APC环境中的KernelApcPending域设|ؓTRUE?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">在WIN32 SDK文中是q样描述APCs的: 当一个APC被成功的存放到它的队列后Q发Z个Y中断QAPC会在线E被调度q行的下一个时间片执行。然而这不是完全正确的。这样一个Y中断Q仅仅是当一个内核模式的APCQ无论是常规的内核模式APCq是Ҏ的内核模式APCQ针对于调用U程Ӟ才会发出。随后函数返回TRUE?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">1Q如果APC不是针对于调用线E,目标U程在Passive权限{处在{待状态;<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">2Q这是一个常规内核模式APC<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">3Q这个线E不再界区<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">4Q没有其他的常规内核模式APC仍然在进行中<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">那么q个U程被唤醒,q回状态是STATUS_KERNEL_APC。但是等待状态没有aborted?如果q是一个用h式APCQKiInsertQueueApc查判断目标线E是否是alertable{待状态,q且WaitMode域等于UserMode。如果是Q主APC环境的UserApcPending 域设|ؓTRUE。等待状态返回STATUS_USER_APCQ最后,函数释放spinlockQ返回TRUEQ表CAPC对象已经被成功放入队列?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">早期作ؓAPC理函数的补充,讑֤驱动开发者可以用未公开的系l服务NtQueueApcThread来直接将一个用h式的APC投递到某个U程?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">q个函数内部实际上是调用了KeInitializeApc 和KeInsertQueueApc 来完成这个Q务?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">NTSYSAPI<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">NTSTATUS<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">NTAPI<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">NtQueueApcThread (<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN HANDLE Thread,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PKNORMAL_ROUTINE NormalRoutine,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PVOID NormalContext,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PVOID SystemArgument1,<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">IN PVOID SystemArgument2<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">);<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">NT的APCz֏?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">NT查是否线E有未决的APCs. 然后APCz֏器子E序KiDeliverApc在这个线E上下文执行来开始将未决的APC执行。注意,q个行ؓ中断了线E的正常执行程Q首先将控制权给APCz֏器,随后当KiDeliverApc完成后,l箋U程的执行?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">例如Q当一个线E被调度q行Ӟ最后一步,上下文切换函?SwapContext 用来查是否新的线E有未决的内核APCs.如果是,SwapContext要么Q?Q请求一个APCU别的Y中断来开始APC执行Q由于新U程q行在低的IRQLQPassiveU别?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">或者(2Q返回TRUEQ表C新的线E有未决的内核APCs?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">I竟是执?1)q是(2)取决于新U程所处的IRQLU别. 如果它的权限U别高于PassiveU?SwapContext 执行(1),如果它是在PassiveU?则选择执行(2).<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">SwapContext的返回g仅是特定pȝ函数可用?q些pȝ函数调用SwapContext来强制切换线E上下文到另一个线E? 然后,当这些系l函数经q一D|间再l箋?他们通常查SwapContext 的返回?如果是TRUE,他们׃调用APCz֏器来投递内核APCs到当前的U程. 例如:<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">pȝ函数KiSwapThread被等待服务用来放弃处理器Q直到等待结束。这个函数内部调用SwapContext。当{待l束Ql从调用SwapContext处执行时Q就会检查SwapContext的返回倹{如果是TRUEQKiSwapThread会降低IRQLU别到APCU,然后调用KiDeliverApc来在当前U程执行内核APCs.<br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">对于用户APCs, 内核调用APCz֏器仅仅是当线E回到用h式,q且U程的主APC环境的UserApcPending域ؓTRUE时。例如:当系l服务派发器KiSystemService完成一个系l服务请求正打算回到用户模式Ӟ它会查是否有未决的用户APCs。在执行上,KiDeliverApc调用用户APC的KernelRoutine. 随后QKiInitializeUserApc函数被调用,用来讄U程的陷阱。所以从内核模式退出时Q线E开始在用户模式下执?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">。KiInitializeUserApc的函数的作用是拷贝当前线E先前的执行状态(当进入内核模式时Q这个状态保存在U程内核栈创建的陷阱帧里Q,从内核栈到线E的用户模式栈,初始化用h式APC。APCz֏器子E序KiUserApcDispatcher在Ntdll.dll内。最后,加蝲陷阱帧的EIP寄存器和Ntdll.dll中KiUserApcDispatcher的地址。当陷阱帧最后释放时Q内核将控制转交lKiUserApcDispatcherQ这个函数调用APC的NormalRoutine例程QNormalRoutine函数地址以及参数都在栈中Q当例程完成Ӟ它调用NtContinue来让U程利用在栈中先前的上下文l执行,仿佛什么事情也没有发生q?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">当内核调用KiDeliverApc来执行一个用h式APCӞU程中的PreviousMode域被设ؓUserMode. TrapFrame域指向线E的陷阱帧。当内核调用KiDeliverApc来执行内核APCsӞU程中的PreviousMode域被设ؓKernelMode. TrapFrame域指向NULL?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">注意Q无Z时只要KernelRoutine被调用,传递给它的指针是一个局部的APC属性的副本Q由于APC对象已经q了队列,所以可以安全的在KernelRoutine中释放APC内存。此外,q个例程在它的参数被传递给其他例程之前Q有一个最后的Z来修改这些参数?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px"><br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">l论Q?br style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 0px">APC提供了一个非常有用的机制Q允许在特定的线E上下文中异步的执行代码。作Z个设备驱动开发者,你可以依赖APCs在某个特定的U程上下文中执行一个例E,而不需要线E的许可和干涉。对于用户应用程序,用户模式APCs可以用来有效地实C些回调通知机制?/span></span><img src ="http://www.shnenglu.com/shaker/aggbug/145731.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/shaker/" target="_blank">shaker(太子)</a> 2011-05-05 11:46 <a href="http://www.shnenglu.com/shaker/archive/2011/05/05/145731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NtProtectVirtualMemoryhttp://www.shnenglu.com/shaker/archive/2011/03/23/142548.htmlshaker(太子)shaker(太子)Wed, 23 Mar 2011 02:01:00 GMThttp://www.shnenglu.com/shaker/archive/2011/03/23/142548.htmlhttp://www.shnenglu.com/shaker/comments/142548.htmlhttp://www.shnenglu.com/shaker/archive/2011/03/23/142548.html#Feedback1http://www.shnenglu.com/shaker/comments/commentRss/142548.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/142548.html 

  1 NTSTATUS
  2 NTAPI
  3 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
  4                        IN OUT PVOID *UnsafeBaseAddress,
  5                        IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
  6                        IN ULONG NewAccessProtection,
  7                        OUT PULONG UnsafeOldAccessProtection)
  8 {
  9     PEPROCESS Process;
 10     ULONG OldAccessProtection;
 11     ULONG Protection;
 12     PEPROCESS CurrentProcess = PsGetCurrentProcess();
 13     PVOID BaseAddress = NULL;
 14     SIZE_T NumberOfBytesToProtect = 0;
 15     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
 16     NTSTATUS Status;
 17     BOOLEAN Attached = FALSE;
 18     KAPC_STATE ApcState;
 19     PAGED_CODE();
 20 
 21     //
 22     // Check for valid protection flags
 23     //
 24     Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
 25     if (Protection != PAGE_NOACCESS &&
 26         Protection != PAGE_READONLY &&
 27         Protection != PAGE_READWRITE &&
 28         Protection != PAGE_WRITECOPY &&
 29         Protection != PAGE_EXECUTE &&
 30         Protection != PAGE_EXECUTE_READ &&
 31         Protection != PAGE_EXECUTE_READWRITE &&
 32         Protection != PAGE_EXECUTE_WRITECOPY)
 33     {
 34         //
 35         // Fail
 36         //
 37         return STATUS_INVALID_PAGE_PROTECTION;
 38     }
 39 
 40     //
 41     // Check if we came from user mode
 42     //
 43     if (PreviousMode != KernelMode)
 44     {
 45         //
 46         // Enter SEH for probing
 47         //
 48         _SEH2_TRY
 49         {
 50             //
 51             // Validate all outputs
 52             //
 53             ProbeForWritePointer(UnsafeBaseAddress);
 54             ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
 55             ProbeForWriteUlong(UnsafeOldAccessProtection);
 56 
 57             //
 58             // Capture them
 59             //
 60             BaseAddress = *UnsafeBaseAddress;
 61             NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
 62         }
 63         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
 64         {
 65             //
 66             // Get exception code
 67             //
 68             _SEH2_YIELD(return _SEH2_GetExceptionCode());
 69         }
 70         _SEH2_END;
 71     }
 72     else
 73     {
 74         //
 75         // Capture directly
 76         //
 77         BaseAddress = *UnsafeBaseAddress;
 78         NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
 79     }
 80 
 81     //
 82     // Catch illegal base address
 83     //
 84     if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
 85 
 86     //
 87     // Catch illegal region size
 88     //
 89     if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
 90     {
 91         //
 92         // Fail
 93         //
 94         return STATUS_INVALID_PARAMETER_3;
 95     }
 96 
 97     //
 98     // 0 is also illegal
 99     //
100     if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
101 
102     //
103     // Get a reference to the process
104     //
105     Status = ObReferenceObjectByHandle(ProcessHandle,
106                                        PROCESS_VM_OPERATION,
107                                        PsProcessType,
108                                        PreviousMode,
109                                        (PVOID*)(&Process),
110                                        NULL);
111     if (!NT_SUCCESS(Status)) return Status;
112 
113     //
114     // Check if we should attach
115     //
116     if (CurrentProcess != Process)
117     {
118         //
119         // Do it
120         //
121         KeStackAttachProcess(&Process->Pcb, &ApcState);
122         Attached = TRUE;
123     }
124 
125     //
126     // Do the actual work
127     //
128     Status = MiProtectVirtualMemory(Process,
129                                     &BaseAddress,
130                                     &NumberOfBytesToProtect,
131                                     NewAccessProtection,
132                                     &OldAccessProtection);
133 
134     //
135     // Detach if needed
136     //
137     if (Attached) KeUnstackDetachProcess(&ApcState);
138 
139     //
140     // Release reference
141     //
142     ObDereferenceObject(Process);
143 
144     //
145     // Enter SEH to return data
146     //
147     _SEH2_TRY
148     {
149         //
150         // Return data to user
151         //
152         *UnsafeOldAccessProtection = OldAccessProtection;
153         *UnsafeBaseAddress = BaseAddress;
154         *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
155     }
156     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
157     {
158     }
159     _SEH2_END;
160 
161     //
162     // Return status
163     //
164     return Status;
165 }


shaker(太子) 2011-03-23 10:01 发表评论
]]>
【{帖】Windows|络体系l构ȝhttp://www.shnenglu.com/shaker/archive/2011/02/22/140439.htmlshaker(太子)shaker(太子)Tue, 22 Feb 2011 06:48:00 GMThttp://www.shnenglu.com/shaker/archive/2011/02/22/140439.htmlhttp://www.shnenglu.com/shaker/comments/140439.htmlhttp://www.shnenglu.com/shaker/archive/2011/02/22/140439.html#Feedback0http://www.shnenglu.com/shaker/comments/commentRss/140439.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/140439.html?? Windows|络体系l构ȝ
?? jbwang
?? 2009-11-23,11:22:25
?? http://bbs.pediy.com/showthread.php?t=101794

做了一些东西自׃看了一些书Q最qȝ了一下,想给大家分n一下,高手可以飞过了。如果有什么问题可以给弟指正一下,多谢Q?img alt="" src="http://www.shnenglu.com/images/cppblog_com/shaker/022211_0647_Windows1.gif">

在介lWindows|络体系架构之前Q我首先介绍一下Windows中的两个重要~程规范——TDIQNDIS.Q然后再介绍|络体系的架构?br>TDIQTransport Driver InterfaceQ传输驱动程序接口。\Windows\System32\Drivers\Tdi.sys
在实现网lAPI驱动E序Ӟ׃牉|到很多不同协议,会用C同协议驱动提供的接口Q得开发的工作复杂化。所以Microsoft在网lAPI驱动E序和协议驱动之间又增加了一层TDI。TDI接口只是一U?网l请求格式化成IRPQ以及申Ll地址和数据通信"的做法规范化。遵从TDI标准的传输协议向他们的客P如Socket emulatorQNetbios emulator{)导出了TDI接口Q有利于上下层之间的通信Q?br>?nbsp; 一斚wQ对于TDI上层的网lAPI驱动E序׃需要用所有协议驱动程序所提供的接口,直接使用TDI提供的统一接口?br>?nbsp; 另一斚wQ对于下层协议驱动程序(也称为TDI Transport Provider传输器)直接由TDI接口来调用,发出h?br> 


在Windows VISTA版本之后QTDI׃再用了Q取而代之的是Windows filter platform和Winsock kernel?br>
NDISQNetwork Driver Interface SpecificationQ网l驱动程序接口规范,在操作系l中的位|?br>\Windows\System32\Drivers\NDIS.sys
当一个协议驱动程序想要按照其协议的格式解析网上读写的数据Ӟ而这些数据必通过|络适配器才能取得,期望协议驱动E序能够理解市场上的每一Ƅl适配器的l微区别是不可能的。所以在1989q_由Microsoft?Com联合开发的了NDISQ得协议驱动程序可以以一U与讑֤无关的方式来跟网l适配器驱动程序进行通信。遵从NDIS的网l适配器驱动程序称为NDIS minport driver?/span>

 

 
NDIS规范实现了与TDI标准cM的功能,都是复杂的下层调用规范化、标准化Q大大提高了Windows操作pȝ的可扩展性和兼容性。也表现在两个方面:
?nbsp; 对于下层Q让|络适配器制造商很easy的开发自q讑֤驱动E序Q也是Ndis miniport driver。这些miniport driver直接利用NDIS提供的接口发送指令,NDIS对这些格式化的指令进行解析,做进一步处理。(q些处理到了HAL了)
?nbsp; 对于上层Q多个协议驱动程序与下层minport driver之间的通信Q也都是通过l一的NDIS接口QNidsAllocatePacketQNdisSend{函数来收发数据?br>
废话两句QTDI和NDIS两大接口规范Q有力的提升了Windows操作pȝ对不同设备厂商的支持Q降低了讑֤厂商对设备驱动程序开发的隑ֺQ也增加了对于不同网l协议的支持Q给用户更强大的|络功能支持。这U设计我们也可以在Windows存储理中看刎ͼ从中我们g可以了解C些,Windows操作pȝ在商业上取得成功的原因。Linux操作pȝ中没有这L驱动层次l构?br>
讑֤刉商开发的Ndis miniport driver直接调用NDIS库中的接口函敎ͼ因此不需要考虑重入的问题,是一个请求尚未结束的时候,新的h又进来了。NDIS库对hq行了序列化Q但是这U序列化也妨了多处理器的扩展性。所以NDIS5中提供了非序列化的操作项。下面,我来介绍一下,Deserialized和Serialized minport driver的区别:
Deserialized NDIS miniport driver自己序列化对MinportXxx函数的操作,排队和管理多个ƈ发请求的d都由驱动E序自己来完成。而Serialized NDIS miniport driver以上的工作都是依赖于NDIS库来完成的。从性能角度看,Deserialized NDIS miniport driver的性能是Serialized NDIS miniport driver性能?倍多Q所以到NDIS6.0之后的所有Miniport driver都是deserialize的?br>
 
以上是我参考MSDN以及自己的一些理解画出来的windows|络架构图,下面我就从上C来简单介l一下其中的各个层?/span>

 


1.  |络应用E,Network applicantionQ用h的应用E序调用Windows操作pȝ提供的网lAPIQ网lAPI包括Q?br>a)  Windows套接字(winsockQ?br>b)  q程q程调用RPC
c)  Web讉KAPI
d)  命名道和邮件槽
e)  其他|络API
q些API既可以在用户模式下实玎ͼ也可以同时在用户模式和内核模式下实现。从本质上说q些API是下层提供接口的另一层封装而已?br>2.  TDI ClientsQ传输驱动程序接口客P是内核模式的讑֤驱动E序Q用于实现网lAPI的内栔R分。将|络API的请求{换成IRPQ通过TDI标准格式化后Q发送给下层的协议驱动(也就是TDI传输器)。从sockets emulator的架构图看到QTDI Clients的实现可以有用户态的部分Q也有内核态的部分。AFD辅助功能驱动E序通过向协议驱动程序发送TDI IRP来执行网l套接字操作Q比如发送和接受消息。AFD没有不是定使用哪一个协议驱动,而是上层通知其要使用的协议名Uͼ然后AFDL开相应协议的设备对象?/span>

 

 
3.  TDI Transport Providers、TDI传输器、NDIS协议驱动E序、协议驱动程序,所有这些其实就是指的同一个东西,我在后面q其ؓ协议驱动E序。这个部分就是我们对某个协议的具体实现部分。做q网l协议开发的朋友一定知道,协议其实是双发协商好的一套通信的规则。以IP协议ZQ实际上是对网l数据的一U处理方式,Ҏ|络数据包的解析l构Q做出相应的处理。Windows的tcpip.sys实C多个协议Qip、tcp、udp、arp、icmp、igmpQ它Z层的TDI Clients提供?个设备对象,用于讉K使用q些协议QTDI Clients打开q些讑֤对象Q向其发送IRPh来实现自q操作。通过DDK的DeviceTree我们可以得到q些讑֤对象
a)  \Device\Rawip
b)  \Device\Tcp
c)  \Device\Udp
d)  \Device\IPMULTICAST
e)  \Device\Ip
协议驱动E序处理的数据是通过NDIS库中提供的接口来获取的,不需要发送IRP来取得。在DDK XP中提供了一个协议驱动程序了源程序NdisuioQDDK XP后的版本提供的是Ndisport。在DriverEntry中我们可以看刎ͼ驱动E序一开始就注册了一个NDIS_PROTOCOL_CHARACTERISTICSQ这个结构体中是一堆NdisXxxx函数。NDIS规范在这里就开始发挥它的作用了?br>协议驱动E序的另一个作用就是监听网l数据,自己开发一个网l协议通过Ndis API获得所有的|络数据Q但是不能够拦截|络数据Q因为其他协议驱动也可以通过Nids API获取数据。一个典型的应用是Winpcap了,使用NPF.SYS来捕Ll数据,q且做好充分~冲处理Q防止大数据量到来时出现数据包丢q情况。详情情节winpcap的开源代码?br>具体的协议驱动开发过E,我就不细qCQ大家可以参看Ndisuio和DDK docQ我推荐boywhp的一文《NDIS协议驱动开发》给大家?br>4.  NDISQNetwork Driver Interface SpecificationQ网l协议接口标准。从图中我们可以看到包裹在其中的两个驱动E序Q一个是NDIS intermediate driverQNDIS中间层驱动程序,另一个是NDIS minport driverQ小端口驱动E序。下面简单介l一下这两个驱动E序Q?br>a)  Ndis intermediate driverQNDIS中间层驱动程序,对于上层的protocol driver它充当minport driver的作用,对于下层的minport driver它充当一个protocol driver的作用,所以在驱动E序DriverEntry中就注册NDIS_PROTOCOL_CHARACTERISTICS和NDIS_MINIPORT_CHARACTERISTICSQ用protocol characteristics中NDIS API从miniport driver那里取得数据包,再用miniport characteristics的NDIS API向上层的protocol driver发送数据包。Nids intermediate driver最大的优势是所有miniport driver的数据包都要通过它这里倒手lprotocol driverQ所以网l防火墙q上了q块风水宝地。现在很多网l防火墙都用NDIS intermediate driver做数据包的过滤和拦截工作Q过滤的规则讄到MPSendPacketsQPTReceiveQPTReceiveRacketq三个函数。具体开发过E请大家参考DDK提供的PassThru源代码,www.ndis.comQ?nbsp;|上有很多相关的资料?br>NDIS 6.0之后Qfilter driver取代了Ndis intermediate driverQWDK中提供源码?br>b)  Ndis miniport driver一般是p备厂商提供的Q在DDK中也提供了miniport driver的一个例子e100bexQ支持Intel EtherExpressTM PRO/100+ Ethernet PCI adapter 和Intel EtherExpressTM PRO/100B PCI adapter两款|络适配器?br>5.  最后介l一下ȝQ计机ȝ有好几种QUSBȝ、ISAȝ、PCIȝ、虚拟ȝ{,一般都是以PCIȝ作ؓҎȝQ在Windowspȝ中其他的ȝ可以理解为PCIȝ上的一个设备。PCIȝ作ؓҎȝQ其传输速度较高Q可以达?33MB/SQ显卡和|卡很多都是用PCI插槽?br>PCI-ISA桥设备,也称为南桥,实现了ISAȝ与PCIȝ的桥接, 南桥q包括终端、IDE、USB、DMA{控制器讑֤。其中USB-HOST讑֤实现了USBȝ和PCIȝ的桥接。HOST/PCI桥称为北桥,是主处理器中心啊到基PCI局部ȝ。南桥和北桥l成了主板的芯片l,通过芯片的扩展实C多种ȝ与基PCI局部ȝ的桥接?br>ȝ驱动E序和PNP理器实Cx即用的功能,物理讑֤对象PDO是由ȝ驱动E序产生的?/span>



shaker(太子) 2011-02-22 14:48 发表评论
]]>
Pro OGRE 3D Programming 中文译版本0.2.0http://www.shnenglu.com/shaker/archive/2010/08/18/123851.htmlshaker(太子)shaker(太子)Wed, 18 Aug 2010 09:04:00 GMThttp://www.shnenglu.com/shaker/archive/2010/08/18/123851.htmlhttp://www.shnenglu.com/shaker/comments/123851.htmlhttp://www.shnenglu.com/shaker/archive/2010/08/18/123851.html#Feedback0http://www.shnenglu.com/shaker/comments/commentRss/123851.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/123851.html

shaker(太子) 2010-08-18 17:04 发表评论
]]>
Boost的状态机库教E?补充http://www.shnenglu.com/shaker/archive/2010/08/16/123601.htmlshaker(太子)shaker(太子)Mon, 16 Aug 2010 06:55:00 GMThttp://www.shnenglu.com/shaker/archive/2010/08/16/123601.htmlhttp://www.shnenglu.com/shaker/comments/123601.htmlhttp://www.shnenglu.com/shaker/archive/2010/08/16/123601.html#Feedback0http://www.shnenglu.com/shaker/comments/commentRss/123601.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/123601.html来自:http://www.shnenglu.com/shanoa/archive/2009/05/30/86143.html
接触了boost的状态机Q发C是想象中的那么好用,在一些地方还得用上mpl库里的东西,׃Ҏ板元~程不是很熟l,搞了好些天才弄明白q该ȝmpl::list的原理和用法?br>boost的状态机是属于静态链接的状态机Q也是_它的囄构是~译期间q定了的,在运行时不可以动态配|。所以,它的用途是有一定局限性的Q但在一般情况下Q它不仅很通用Q而且在你会用q熟l地情况下,q会很好用,用v来很舒服Q逻辑也很合理。下面就是一D代码,当然也是借鉴了别人的东西Q自׃改了一下,在MainState中添加了一个Transition做了试Q因为此前我q不知道一个状态如何包含多个TransitionQ呵呵,原来是用mpl::list来做。至于这个状态机的入门教E,|上随处可见的三部曲Q《boost 状态机入门教程》说得很清楚?br>

  1 #include <iostream>
  2 #include <ctime>
  3 
  4 #include <boost/statechart/transition.hpp>
  5 #include <boost/statechart/event.hpp>
  6 #include <boost/statechart/state_machine.hpp>
  7 #include <boost/statechart/simple_state.hpp>
  8 
  9 namespace sc = boost::statechart;
 10 
 11 
 12 
 13 class EvtStartStop : public sc::event<EvtStartStop>{};
 14 class EvtReset : public sc::event<EvtReset>{};
 15 class EvtGo : public sc::event<EvtGo>{};
 16 
 17 
 18 class MainState;
 19 class StopState;
 20 class RunState;
 21 class TwoState;
 22 
 23 class Machine : public sc::state_machine<Machine, MainState>
 24 {};
 25 
 26 
 27 
 28 
 29 
 30 
 31 class MainState : public sc::simple_state<MainState, Machine, StopState>
 32 {
 33 public:
 34     typedef sc::transition<EvtReset, MainState> reactReset;
 35     typedef sc::transition<EvtGo, TwoState> reactGo;
 36     typedef boost::mpl::list<reactReset, reactGo> reactions;
 37 
 38     MainState(void){
 39         std::cout<<"q入MainState"<<std::endl;
 40         mTime = 0;
 41     }
 42 
 43     ~MainState(void){
 44         std::cout<<"退出MainState"<<std::endl;
 45     }
 46 
 47     double mTime;
 48 };
 49 
 50 
 51 // 该状态属于无用状态,用于试mpl::list的多transition用法
 52 class TwoState : public sc::simple_state<TwoState, Machine>
 53 {
 54 public:
 55     typedef sc::transition<EvtGo, MainState> reactions;
 56 
 57     TwoState(void){
 58         std::cout<<"q入TwoState"<<std::endl;
 59     }
 60 
 61     ~TwoState(void){
 62         std::cout<<"退出TwoState"<<std::endl;
 63     }
 64 };
 65 
 66 
 67 class StopState : public sc::simple_state<StopState, MainState>
 68 {
 69 public:
 70     typedef sc::transition<EvtStartStop, RunState> reactions;
 71     StopState(void){
 72         std::cout<<"q入StopState"<<std::endl;
 73     }
 74 
 75     ~StopState(void){
 76         std::cout<<"退出StopState"<<std::endl;
 77     }
 78 };
 79 
 80 class RunState : public sc::simple_state<RunState, MainState>
 81 {
 82 public:
 83     typedef sc::transition<EvtStartStop, StopState> reactions;
 84     RunState(void){
 85         std::cout<<"q入RunState"<<std::endl;
 86         mStartTime = 0;
 87     }
 88 
 89     ~RunState(void){
 90         std::cout<<"退出RunState"<<std::endl;
 91         context<MainState>().mTime += std::difftime(std::time(0), mStartTime);
 92     }
 93 
 94     std::time_t mStartTime;
 95 };
 96 
 97 
 98 int _tmain(int argc, _TCHAR* argv[])
 99 {
100     Machine mc;
101     mc.initiate();
102 
103     mc.process_event(EvtStartStop());
104     mc.process_event(EvtStartStop());
105     mc.process_event(EvtReset());
106     mc.process_event(EvtGo());
107     mc.process_event(EvtGo());
108 
109     return 0;
110 }


shaker(太子) 2010-08-16 14:55 发表评论
]]>
Boost的状态机库教E?3)http://www.shnenglu.com/shaker/archive/2010/08/16/123599.htmlshaker(太子)shaker(太子)Mon, 16 Aug 2010 06:53:00 GMThttp://www.shnenglu.com/shaker/archive/2010/08/16/123599.htmlhttp://www.shnenglu.com/shaker/comments/123599.htmlhttp://www.shnenglu.com/shaker/archive/2010/08/16/123599.html#Feedback1http://www.shnenglu.com/shaker/comments/commentRss/123599.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/123599.html

1.2 增加动作


    此时我们只用一U动作:transitionsQ我们在下面的代码中插入了黑体的部分?/span>

 1#include <boost/statechart/transition.hpp>
 2
 3// 
 4
 5struct  Stopped;
 6struct  Active : sc::simple_state< Active, StopWatch, Stopped >
 7{
 8   typedef  sc::transition< EvReset, Active > reactions;
 9}
;
10
11struct  Running : sc::simple_state< Running, Active >
12{
13   typedef  sc::transition< EvStartStop, Stopped > reactions;
14}
;
15
16struct  Stopped : sc::simple_state< Stopped, Active >
17{
18   typedef  sc::transition< EvStartStop, Running > reactions;
19}
;
20
21//一个状态可以定义Q意数量的动作。这是Z么当多于一个时Q?br>22//我们不得不将它们攑ֈ一个mpl::list<> 里?/span>
23
24int  main()
25{
26  StopWatch myWatch;
27  myWatch.initiate();
28  myWatch.process_event( EvStartStop() );
29  myWatch.process_event( EvStartStop() );
30  myWatch.process_event( EvStartStop() );
31  myWatch.process_event( EvReset() );
32   return  0;
33}

34


    现在我们有了所有的状态,q在适当的位|增加了所有的q移动作Q同时我们也向StopWatch发送了一些事件。这个状态机会尽职尽责的按我们的希望q行状态迁U,但依然现在还没有其它的动作?/span>


1.3 State-local存储


    下一步我们将让这个Stop watch真正的记录时间了。根据stop watch所处不同的状态,我们需要不同的变量?/span>

Stopped状态:需要一个保存逝去旉的变量?/span>

l Running状态:需要一个保存逝去旉的变量,q需要一个保存上一ơ启动的旉点的变量?/span>

    无论状态机在什么状态下Q我们都必须观察逝去旉q个变量。此外,当我们向状态机发送EvReSet事gӞq个变量应该被置?。其它的变量只是状态机在Running状态时需要。无Z时我们进入Running状态时Q它应该被置为系l时钟的当前旉。当我们退出Running状态时Q我们仅仅从pȝ旉的当前时间减d始时_q入时记录的旉Q,结果加到逝去旉里就可以了?/span>

 1#include <ctime>
 2
 3// 
 4
 5struct  Stopped;
 6struct  Active : sc::simple_state< Active, StopWatch, Stopped >
 7{
 8   public :
 9     typedef  sc::transition< EvReset, Active > reactions;
10
11    Active() : elapsedTime_( 0.0 ) {}
12    double  ElapsedTime()  const  {  return  elapsedTime_; }
13    double  & ElapsedTime() {  return  elapsedTime_; }
14   private :
15     double  elapsedTime_ ;
16}
;
17
18struct  Running : sc::simple_state< Running, Active >
19{
20   public :
21     typedef  sc::transition< EvStartStop, Stopped > reactions;
22
23    Running() : startTime_( std::time( 0 ) ) {}
24    ~Running()
25    {
26       // 与派生类可以讉K它的基类怼Q?br>27       //context<>() 用来获得一个状态的直接或间接的上下文的讉K权?br>28       // q可以是直接或间接的外层状态或状态机本n
29       // (例如Q像q样: context< StopWatch >()).
30      context< Active >().ElapsedTime() +=
31        std::difftime( std::time( 0 ), startTime_ );
32    }

33   private :
34    std:: time_t  startTime_;
35}
;

    q个状态机现在可以量旉了,但是我们q不能看到结果?/span>

在这里,State-local storage的优势还没有完成昄出来。在FAQ目“State-local storage酷在哪里Q?#8221;中,会通过与一个没有用State-local storage的Stop Watch的比较来说明?/span>


1.4 在状态机外得到状态信?/span>


    Z取得量的时_我们需要一个从状态机外得到状态信息的机制。按我们现在的状态机设计Q可以有两种Ҏ。ؓ单v见,我们在这里用一个低效的方式Qstate_cast<>()(在StopWatch2.cpp中我们会用一个稍复杂一点的替代Ҏ)Q?span style="COLOR: #000080">译者注Q在StopWatch2.cpp中是向状态机发送一个取得逝去旉的事Ӟ从事件成员量中将逝去旉带回?/span> Q,从字面意思就可以看出Q它在语义上与dynamic_cast有点怼。例如,当我们调用myWatch.state_cast<const Stpped&>()Ӟ当状态机在Stopped状态时Q我们会得到一个Stopped状态类的引用。否则,会抛出std::bad_cast异常。我们可以利用这个功能来实现一个StopWatch的成员函敎ͼ让它的结果返回逝去的时间。然而,我们不是先问一下状态机在什么状态,然后再去用不同的Ҏ计算逝去旉Q而是计放到Stopped和Running状态中Q用一个接口来获得逝去逝去旉?/span>

 

  1. #include <iostream> 
  2. // ... 
  3. struct  IElapsedTime 
  4.    virtual   double  ElapsedTime()  const  = 0; 
  5. }; 
  6. struct  Active; 
  7. struct  StopWatch : sc::state_machine< StopWatch, Active > 
  8.    double  ElapsedTime()  const  
  9.   { 
  10.      return  state_cast<  const  IElapsedTime & >().ElapsedTime(); 
  11.   } 
  12. }; 
  13. // ... 
  14. struct  Running : IElapsedTime,  
  15.   sc::simple_state< Running, Active > 
  16.    public
  17.      typedef  sc::transition< EvStartStop, Stopped > reactions; 
  18.     Running() : startTime_( std::time( 0 ) ) {} 
  19.     ~Running() 
  20.     { 
  21.       context< Active >().ElapsedTime() = ElapsedTime(); 
  22.     }
  23.      virtual   double  ElapsedTime()  const  
  24.         { 
  25.            return  context< Active >().ElapsedTime() + 
  26.             std::difftime( std::time( 0 ), startTime_ ); 
  27.         } 
  28.        private
  29.         std:: time_t  startTime_; 
  30.     };  
  31.      
  32.      struct  Stopped : IElapsedTime,  
  33.       sc::simple_state< Stopped, Active > 
  34.     { 
  35.        typedef  sc::transition< EvStartStop, Running > reactions; 
  36.      
  37.        virtual   double  ElapsedTime()  const  
  38.       { 
  39.          return  context< Active >().ElapsedTime(); 
  40.       }  
  41.     }; 
  42.      
  43.      int  main() 
  44.     { 
  45.       StopWatch myWatch; 
  46.       myWatch.initiate(); 
  47.       std::cout << myWatch.ElapsedTime() <<  "\n" ;  
  48.       myWatch.process_event( EvStartStop() ); 
  49.       std::cout << myWatch.ElapsedTime() <<  "\n"
  50.       myWatch.process_event( EvStartStop() ); 
  51.       std::cout << myWatch.ElapsedTime() <<  "\n"
  52.       myWatch.process_event( EvStartStop() ); 
  53.       std::cout << myWatch.ElapsedTime() <<  "\n"
  54.       myWatch.process_event( EvReset() ); 
  55.       std::cout << myWatch.ElapsedTime() <<  "\n"
  56.        return  0; 
  57.     }

Z实看到被测量的旉Q你应该惛_法在main()中单步执行。StopWatch例子这个程序扩展ؓ一个交互式的终端程序了?/span>



shaker(太子) 2010-08-16 14:53 发表评论
]]>
Boost的状态机库教E?2)http://www.shnenglu.com/shaker/archive/2010/08/16/123598.htmlshaker(太子)shaker(太子)Mon, 16 Aug 2010 06:45:00 GMThttp://www.shnenglu.com/shaker/archive/2010/08/16/123598.htmlhttp://www.shnenglu.com/shaker/comments/123598.htmlhttp://www.shnenglu.com/shaker/archive/2010/08/16/123598.html#Feedback0http://www.shnenglu.com/shaker/comments/commentRss/123598.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/123598.html1 基础主题Q秒?/strong>
    下面我们要ؓ一个机械秒表徏模一个状态机。这样一个秒表通常会有两个按钮?br>     * Start/Stop
     * Reset
     同时有两U状态:
     * Stoped: 表针停留在上ơ停止时的位|:
        o 按下Reset按钮Q表针回退?的位|。秒表保持在Stoped状态不变?br>        o 按下Start/Stop按钮Q秒表{到Running状态?br>     * Running: 表针在移动,q持l显Cȝ旉Q?/span>
        o 按下Reset按钮Q表针回退?的位|,U表转到停止状态?br>        o 按下Start/Stop按钮Q{到Stoped状态?br>    下面是其UML图:



1.1 定义状态和事g
两个按钮可以建模Z个事件。进而,定义出必要的状态和初始状态。我们从下面的代码开始,以前的代码片D会陆箋加入其中Q?/span>

 

#include <boost/statechart/event.hpp>
#include 
<boost/statechart/state_machine.hpp>
#include 
<boost/statechart/simple_state.hpp>

namespace sc = boost::statechart;

struct EvStartStop : sc::event< EvStartStop > {};
struct EvReset : sc::event< EvReset > {};

struct Active;
struct StopWatch : sc::state_machine< StopWatch, Active > {};

struct Stopped;

// q里的simple_statecL板可以接?个参敎ͼ
// - W?个参数指定内部的初始状态,如果有一个这L状态的话?br>//   在这里,Active有一个内部状态(StopedQ, 所以将q个内部
//   初始状态传l它的基cR?br>// - W?个参数指定是否保留和保留什么类型历?br>
// Active是最外层的状态,因此要把它所属的状态机cMl它
struct Active : sc::simple_state<
  Active, StopWatch, Stopped 
> {};

// Stopped ?nbsp;Running 都把Active作ؓ它们的上下文Q这使他们嵌入到了Active状态中?/span>
struct Running : sc::simple_state< Running, Active > {};
struct Stopped : sc::simple_state< Stopped, Active > {};

// 因ؓ状态的上下文必L一个完整的cdQ不能单单是声明Q,
// 所以状态机必须要在“外层状?#8221;之间先定义?br>// 也就是说Q我们需要从状态机开始,然后是最外层的状态,然后是其内部的状态,如此反复?br>// 我们可以用广度或深度方式Q再或是以两都؜合的方式来进行定义?/span>

int main()
{
  StopWatch myWatch;
  myWatch.initiate();
  
return 0;
}

q个代码已经可以~译了,但不会发生Q何可察觉的事件?br>



shaker(太子) 2010-08-16 14:45 发表评论
]]>
Boost的状态机库教E?1) http://www.shnenglu.com/shaker/archive/2010/08/16/123597.htmlshaker(太子)shaker(太子)Mon, 16 Aug 2010 06:42:00 GMThttp://www.shnenglu.com/shaker/archive/2010/08/16/123597.htmlhttp://www.shnenglu.com/shaker/comments/123597.htmlhttp://www.shnenglu.com/shaker/archive/2010/08/16/123597.html#Feedback0http://www.shnenglu.com/shaker/comments/commentRss/123597.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/123597.html介绍
    Boost状态机库一个应用程序框Ӟ你可以用它将UML状态图快速的转换为可执行的c++代码Q而不需要Q何的代码生成器。它支持几乎所有的UML特征Q可以直接了当的转换Qƈ且{换后的c++代码像对状态机q行一ơ文本描qC样具体可L?br>
如何阅读q个教程
    q个教程是以U性阅ȝ方式q行的章节设计。如果你是第一ơ看q个教程的话Q你可以从头开始读Q到你觉得了解的东西对你手头的Q务来说已l够时停止。具体可以这P
    * 如果你的d是要实现一个小的、简单的Qƈ且有很少几个状态的状态机Q那么下面的“初主题Q秒?#8221;里所讲的差不多够你用的了?br>    * 如果你要做一个有很多状态的大型状态机Q你可以看一?#8220;中主题Q数码相?#8221;Q那里的讲解可能对你有帮助?br>    * 最后,如果你是一个要创徏异常复杂状态机的用P或者是一个想要评C个Boost状态机的设计师的话Q你p看一?#8220;高主题”部分。ƈ且,我还强烈你看一下Rationle里的Limitions部分?br>
Hello World!
    我们要从一个最单程序开始我们的W一步,状态图如下Q?br>
对于q个状态图Q我们的实现代码如下Q?
 1#include <boost/statechart/state_machine.hpp>
 2#include <boost/statechart/simple_state.hpp>
 3#include <iostream>
 4
 5namespace  sc = boost::statechart;
 6
 7// Z避免写publicQ下面声明的cd全部为struct?br> 8// 如果你不在乎的话可以把它们都Ҏclass?br> 9
10// 我们需要先声明一下初始状态,q是因ؓ我们要在定义状态机时用它
11// 但又不得不在状态机q后定义它?/span>
12
13struct  Greeting;
14
15// Boost.Statechart大量应用模板模式?br>16// zcdd自己做ؓ基类模板的第一个参数?br>17//
18// 状态机必须要知道当其初始化后进行的W一个状态?br>19// q就是ؓ什么Greeting要做为每二个模板参数?br>20// Q译者注Q也是说Greeting状态是Machine状态机初始化后q入的第一个状态)
21struct  Machine : sc::state_machine< Machine, Greeting > {};
22
23// 对于每一个状态,我们需要ؓ其指明:它属于哪一个状态机Q它位于状态图的哪个位|?br>24// 我们用simple_state<>的上下文参数可以完成这些指定了?br>25// 对于我们目前的这个简单的状态机来说Q上下文是状态机QMachine)
26// 所以,Machine必须要做为simple_state的第二个模块参数?br>27// Q关于上下文参数的详l解释在下一个例子中有)
28struct  Greeting : sc::simple_state< Greeting, Machine >
29{
30  // 一旦状态机q行一个状态的时候,它就要创Z个相应状态类的对象(cd例)
31  // 只要状态机保持在这个状态下Q这个对象就会一直存在?br>32  // 最后,当状态机dq个状态时Q对象被销毁?br>33  // 所以,一个状态的q入动作是q个状态类的构造器Q而它的退出动作则是它的析构类?nbsp;  Greeting() { std::cout <<  "Hello World!\n" ; }  // q入
34  ~Greeting() { std::cout <<  "Bye Bye World!\n" ; }  // 退?/span>
35}
;
36
37int  main()
38{
39  Machine myMachine;
40  // 构造完状态机后,它ƈ未开始运行。我们要通过调用它的initiate()来启动它?br>41  // 同时Q它也将触发它的初始状态(GreetingQ的构造?/span>
42  myMachine.initiate();
43  // 当我们离开main()函数ӞmyMachine被销毁,q将D它销毁它内部的所有活动的状态类?br>44  // Q译者注Qؓ什么会说所有?q是因ؓ一个状态机可以同时保持在多个状态中Q可以参?#8220;高主题”部分Q?/span>
45  return  0;
46}
q个E序会显C?#8220;Hello World!”?#8220; Bye Bye World! ”Q然后退出?/span>

shaker(太子) 2010-08-16 14:42 发表评论
]]>
[转]RGB与YUV转换http://www.shnenglu.com/shaker/archive/2010/03/26/110607.htmlshaker(太子)shaker(太子)Fri, 26 Mar 2010 12:27:00 GMThttp://www.shnenglu.com/shaker/archive/2010/03/26/110607.htmlhttp://www.shnenglu.com/shaker/comments/110607.htmlhttp://www.shnenglu.com/shaker/archive/2010/03/26/110607.html#Feedback0http://www.shnenglu.com/shaker/comments/commentRss/110607.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/110607.html1 前言

        自然界的颜色千变万化Qؓ了给颜色一个量化的衡量标准Q就需要徏立色彩空间模型来描述各种各样的颜Ԍ׃人对色彩的感知是一个复杂的生理和心理联合作?的过E,所以在不同的应用领域中Z更好更准的满各自的需求,出C各种各样的色彩空间模型来量化的描q颜艌Ӏ我们比较常接触到的包?RGB / CMYK / YIQ / YUV / HSI{等?/p>

        对于数字电子多媒体领域来_我们l常接触到的色彩I间的概念,主要是RGB , YUVq两U(实际上,q两U体pd含了许多U具体的颜色表达方式和模型,如sRGB, Adobe RGB, YUV422, YUV420 …Q? RGB是按三基色加光系l的原理来描q颜Ԍ而YUV则是按照 亮度Q色差的原理来描q颜艌Ӏ?/p>

        即只是RGB YUVq两大类色彩I间Q所涉及到的知识也是十分丰富复杂的,自知不具备够的相关专业知识Q所以本文主要针对工E领域的应用及算法进行讨论?/p>

2 YUV相关色彩I间模型

2.1 YUV ?YIQ YcrCb

        对于YUV模型Q实际上很多时候,我们是把它和YIQ / YCrCb模型混ؓ一谈的?/p>

        实际?YUV模型用于PAL制式的电视系l,Y表示亮度QUVqM单词的羃写?/p>

        YIQ模型与YUV模型cMQ用于NTSC制式的电视系l。YIQ颜色I间中的I和Q分量相当于将YUVI间中的UV分量做了一?3度的旋{?/p>

        YCbCr颜色I间是由YUV颜色I间z的一U颜色空_主要用于数字电视pȝ中。从RGB到YCbCr的{换中Q输入、输出都?位二q制格式?/p>

        三者与RGB的{换方E如下:

        RGB -> YUVQ?/p>


        实际上也是Q?/p>

Y=0.30R+0.59G+0.11B Q?U=0.493(BQY) Q?V=0.877(RQY)

        RGB -> YIQQ?/p>


        RGB -> YCrCbQ?/p>


        从公式中Q我们关键要理解的一ҎQUV / CbCr信号实际上就是蓝色差信号和红色差信号Q进而言之,实际上一定程度上间接的代表了蓝色和红色的强度Q理解这一点对于我们理解各U颜色变换处理的q程会有很大的帮助?/p>

        我们在数字电子多媒体领域所谈到的YUV格式Q实际上准确的说Q是以YcrCb色彩I间模型为基的具有多U存储格式的一c颜色模型的家族Q包?YUV444 / YUV422 / YUV420 / YUV420P{等Q。ƈ不是传统意义上用于PAL制模拟电视的YUV模型。这些YUV模型的区别主要在于UV数据的采h式和存储方式Q这里就不详q?/p>

        而在Camera Sensor中,最常用的YUV模型?YUV422格式Q因为它采用4个字节描qC个像素,能和RGB565模型比较好的兼容。有利于Camera Sensor和Camera controller的Yg接口设计?/p>

3 YUV2RGB快速算法分?/p>

        q里指的YUV实际是YcrCb?8  ) YUV2RGB的{换公式本w是很简单的Q但是牵涉到点q算Q所以,如果要实现快速算法,法l构本n没什么好研究的了Q主要是采用整型q算或者查表来加快计算速度?br>首先可以推导得到转换公式为:

        R = Y + 1.4075 *QV-128Q?br>        G = Y – 0.3455 *QU –128Q?– 0.7169 *QV –128Q?br>        B = Y + 1.779 *QU – 128Q?/p>

3.1 整型法

       要用整型q算代替点q算Q当然是要用UM的办法了Q我们可以很Ҏ得到下列法Q?/p>

        u = YUVdata[UPOS] - 128;
        v = YUVdata[VPOS] - 128;

        rdif = v + ((v * 103) >> 8);
        invgdif = ((u * 88) >> 8) +((v * 183) >> 8);
        bdif = u +( (u*198) >> 8);

        r = YUVdata[YPOS] + rdif;
        g = YUVdata[YPOS] - invgdif;
        b = YUVdata[YPOS] + bdif;

Z防止出现溢出Q还需要判错计的l果是否?-255范围内,做类g面的判断?/p>

        if (r>255)
            r=255;
        if (r<0)
            r=0;

        要从RGB24转换成RGB565数据q要做移位和或运:

        RGBdata[1] =( (r & 0xF8)  | ( g >> 5) );
        RGBdata[0] =( ((g & 0x1C) << 3) | ( b >> 3) );

3.2 部分查表?/p>

        查表法首先可以想到的是用查表替代上q整型算法中的乘法运?/p>

        rdif = fac_1_4075[u];
        invgdif = fac_m_0_3455[u] + fac_m_0_7169[v];
        bdif = fac_1_779[u];

        q里一共需??l数l,下标?开始到255Q表格共占用U?K的内存空间。uv可以不需要做?28的操作了。在事先计算对应的数l元素的值的时候计在内就好了?/p>

        对于每个像素Q部分查表法用查表替代了2ơ减法运和4ơ乘法运,4ơ移位运。但是,依然需要多ơ加法运和6ơ比较运和可能存在的赋值操作,相对W一U方法运速度提高q不明显?/p>

3.3 完全查表?/p>

        那么是否可以由YUV直接查表得到对应的RGB值呢Q乍一看似乎不太可能,以最复杂的G的运ؓ例,因ؓG与YUV三者都相关Q所以类?G=YUV2G[Y][U][V]q样的算法,一个三l下标尺寔R?56的数l就需要占??4ơ方U?6兆空_l对是没法接受的。所以目前多数都 是采用部分查表法?/p>

        但是Q如果我们仔l分析就可以发现Q对于G我们实际上完全没有必要采用三l数l,因ؓY只与UVq算的结果相养I与UV的个体无养I所以我们可以采用二ơ查表的ҎG的运简化ؓ对两个二l数l的查表操作Q如下:

        G = yig2g_table[ y ][ uv2ig_table[ u ][ v ] ]Q?/p>

        而RB本n只和YU或YV相关Q所以这h们一共需??*8的二l表|需要占???6ơ方?56K内存。基本可以接受。但是对于手L嵌入式运用来_q是略有些大了?/p>

        q一步分析,我们可以看到Q因为在手机{嵌入式q用上我们最l是要把数据转换成RGB565格式送到LCD屏上昄的,所以,对于RGB三分量来_我们 Ҏ不需?bitq么高的_ֺQؓ了简单和q算的统一赯Q对每个分量我们其实只需要高6bit的数据就_了,所以我们可以进一步把表格改ؓ4?6*6的二l表|q样一共只需要占?6K内存Q在计算表格元素值的时候还可以把最l的溢出判断也事先做完。最后的法如下Q?/p>

        y = (YUVdata[Y1POS] >> 2);
        u = (YUVdata[UPOS] >> 2);
        v = (YUVdata[VPOS] >> 2);

        r = yv2r_table[ y ][ v ];
        g = yig2g_table[ y ][ uv2ig_table[ u ][ v ] ];
        b = yu2b_table[ y ][ u ];
 
        RGBdata[1] =( (r & 0xF8)  | ( g >> 5) );
        RGBdata[0] =( ((g & 0x1C) << 3) | ( b >> 3) );

        q样相对部分查表法,我们增加?ơ移位运,而进一步减了4ơ加法运和6ơ比较赋值操作?/p>

        在计表格元素数值的时候,要考虑舍入和偏Uȝ因数使得计算的中间结果满xl下标非负的要求Q需要一定的技巧?/p>

        采用完全查表法,相对于第一U算法,最l运速度可以有比较明昄提高Q具体性能能提高多,要看所在^台的CPUq算速度和内存存取速度的相Ҏ例。内 存存取速度快Q用查表法带来的性能改善明显。在我的PC上测试的l果性能大约能提?5%。而在某ARMq_上测试只提高了约15%?/p>

3.4 q一步的思?/p>

        实际上,上述法Q?/p>

        RGBdata[1] =( (r & 0xF8)  | ( g >> 5) );
        RGBdata[0] =( ((g & 0x1C) << 3) | ( b >> 3) );

        中的 (r & 0xF8) ?( b >> 3) {运也完全可以在表g事先计算出来。另外,YU / YV的取值实际上不可能覆盖满6*6的范_中间有些Ҏ永远取不到的无输入,RB的运也可以考虑?*5的表根{这些都可能q一步提高运的速度Q减 表格的寸?/p>

        另外Q在嵌入式运用中Q如果可能尽量将表格攑֜高速内存如SRAM中应该比攑֜SDRAM中更加能发挥查表法的优势?/p>

4 RGB2YUV ?

        目前觉得q个是没法将3l表格的查表q算化简?l表格的查表q算了。只能用部分查表法替代其中的乘法q算?/p>

        另外Q多数情况下Q我们需要的q是YUV2RGB的{换,因ؓ从Sensor得到的数据通常我们会用YUV数据Q此外JPG和MPEG实际上也是基于YUV格式~码的,所以要昄解码后的数据需要的也是YUV2RGB的运?8 Q运气运气?/p>

 

本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/ALENTAM/archive/2008/03/13/2178020.aspx



shaker(太子) 2010-03-26 20:27 发表评论
]]>
一个简易的采集pȝhttp://www.shnenglu.com/shaker/archive/2009/11/08/100426.htmlshaker(太子)shaker(太子)Sun, 08 Nov 2009 15:14:00 GMThttp://www.shnenglu.com/shaker/archive/2009/11/08/100426.htmlhttp://www.shnenglu.com/shaker/comments/100426.htmlhttp://www.shnenglu.com/shaker/archive/2009/11/08/100426.html#Feedback0http://www.shnenglu.com/shaker/comments/commentRss/100426.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/100426.html  阅读全文

shaker(太子) 2009-11-08 23:14 发表评论
]]>
IE8DVS2008出错问题的解x?/title><link>http://www.shnenglu.com/shaker/archive/2009/05/04/81842.html</link><dc:creator>shaker(太子)</dc:creator><author>shaker(太子)</author><pubDate>Mon, 04 May 2009 09:29:00 GMT</pubDate><guid>http://www.shnenglu.com/shaker/archive/2009/05/04/81842.html</guid><wfw:comment>http://www.shnenglu.com/shaker/comments/81842.html</wfw:comment><comments>http://www.shnenglu.com/shaker/archive/2009/05/04/81842.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/shaker/comments/commentRss/81842.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/shaker/services/trackbacks/81842.html</trackback:ping><description><![CDATA[     摘要:   <a href='http://www.shnenglu.com/shaker/archive/2009/05/04/81842.html'>阅读全文</a><img src ="http://www.shnenglu.com/shaker/aggbug/81842.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/shaker/" target="_blank">shaker(太子)</a> 2009-05-04 17:29 <a href="http://www.shnenglu.com/shaker/archive/2009/05/04/81842.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows下无法安装VS2005SP1的解军_?/title><link>http://www.shnenglu.com/shaker/archive/2008/08/01/57781.html</link><dc:creator>shaker(太子)</dc:creator><author>shaker(太子)</author><pubDate>Fri, 01 Aug 2008 10:19:00 GMT</pubDate><guid>http://www.shnenglu.com/shaker/archive/2008/08/01/57781.html</guid><wfw:comment>http://www.shnenglu.com/shaker/comments/57781.html</wfw:comment><comments>http://www.shnenglu.com/shaker/archive/2008/08/01/57781.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.shnenglu.com/shaker/comments/commentRss/57781.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/shaker/services/trackbacks/57781.html</trackback:ping><description><![CDATA[     摘要: PolicyScope  <a href='http://www.shnenglu.com/shaker/archive/2008/08/01/57781.html'>阅读全文</a><img src ="http://www.shnenglu.com/shaker/aggbug/57781.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/shaker/" target="_blank">shaker(太子)</a> 2008-08-01 18:19 <a href="http://www.shnenglu.com/shaker/archive/2008/08/01/57781.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BCGSoft announces that BCGControlBar Pro for MFC version 10.0 is available!http://www.shnenglu.com/shaker/archive/2008/06/09/52636.htmlshaker(太子)shaker(太子)Mon, 09 Jun 2008 04:45:00 GMThttp://www.shnenglu.com/shaker/archive/2008/06/09/52636.htmlhttp://www.shnenglu.com/shaker/comments/52636.htmlhttp://www.shnenglu.com/shaker/archive/2008/06/09/52636.html#Feedback1http://www.shnenglu.com/shaker/comments/commentRss/52636.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/52636.html阅读全文

shaker(太子) 2008-06-09 12:45 发表评论
]]>
BCGControlBar Library Professional Edition v9.56http://www.shnenglu.com/shaker/archive/2008/02/13/42683.htmlshaker(太子)shaker(太子)Wed, 13 Feb 2008 03:15:00 GMThttp://www.shnenglu.com/shaker/archive/2008/02/13/42683.htmlhttp://www.shnenglu.com/shaker/comments/42683.htmlhttp://www.shnenglu.com/shaker/archive/2008/02/13/42683.html#Feedback10http://www.shnenglu.com/shaker/comments/commentRss/42683.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/42683.html阅读全文

shaker(太子) 2008-02-13 11:15 发表评论
]]>
[ZT]C++ Boost Thread ~程指南 http://www.shnenglu.com/shaker/archive/2007/10/06/33583.htmlshaker(太子)shaker(太子)Fri, 05 Oct 2007 16:05:00 GMThttp://www.shnenglu.com/shaker/archive/2007/10/06/33583.htmlhttp://www.shnenglu.com/shaker/comments/33583.htmlhttp://www.shnenglu.com/shaker/archive/2007/10/06/33583.html#Feedback1http://www.shnenglu.com/shaker/comments/commentRss/33583.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/33583.html阅读全文

shaker(太子) 2007-10-06 00:05 发表评论
]]>
C++Builder2007 安装办法http://www.shnenglu.com/shaker/archive/2007/10/05/33564.htmlshaker(太子)shaker(太子)Fri, 05 Oct 2007 15:06:00 GMThttp://www.shnenglu.com/shaker/archive/2007/10/05/33564.htmlhttp://www.shnenglu.com/shaker/comments/33564.htmlhttp://www.shnenglu.com/shaker/archive/2007/10/05/33564.html#Feedback3http://www.shnenglu.com/shaker/comments/commentRss/33564.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/33564.html转自:http://www.ccrun.com/article.asp?i=1022&d=j0q784  阅读全文

shaker(太子) 2007-10-05 23:06 发表评论
]]>
[图书]使用wxWidgetsq行跨^台程序开?/title><link>http://www.shnenglu.com/shaker/archive/2007/09/27/33009.html</link><dc:creator>shaker(太子)</dc:creator><author>shaker(太子)</author><pubDate>Thu, 27 Sep 2007 05:08:00 GMT</pubDate><guid>http://www.shnenglu.com/shaker/archive/2007/09/27/33009.html</guid><wfw:comment>http://www.shnenglu.com/shaker/comments/33009.html</wfw:comment><comments>http://www.shnenglu.com/shaker/archive/2007/09/27/33009.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/shaker/comments/commentRss/33009.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/shaker/services/trackbacks/33009.html</trackback:ping><description><![CDATA[     摘要: PDF文g  <a href='http://www.shnenglu.com/shaker/archive/2007/09/27/33009.html'>阅读全文</a><img src ="http://www.shnenglu.com/shaker/aggbug/33009.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/shaker/" target="_blank">shaker(太子)</a> 2007-09-27 13:08 <a href="http://www.shnenglu.com/shaker/archive/2007/09/27/33009.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>发一个mir2的内挂代?/title><link>http://www.shnenglu.com/shaker/archive/2007/08/28/31075.html</link><dc:creator>shaker(太子)</dc:creator><author>shaker(太子)</author><pubDate>Tue, 28 Aug 2007 15:59:00 GMT</pubDate><guid>http://www.shnenglu.com/shaker/archive/2007/08/28/31075.html</guid><wfw:comment>http://www.shnenglu.com/shaker/comments/31075.html</wfw:comment><comments>http://www.shnenglu.com/shaker/archive/2007/08/28/31075.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.shnenglu.com/shaker/comments/commentRss/31075.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/shaker/services/trackbacks/31075.html</trackback:ping><description><![CDATA[     摘要: q时的游?反正攑֜盘里面烂掉 不如发出?看看有没人需?nbsp; <a href='http://www.shnenglu.com/shaker/archive/2007/08/28/31075.html'>阅读全文</a><img src ="http://www.shnenglu.com/shaker/aggbug/31075.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/shaker/" target="_blank">shaker(太子)</a> 2007-08-28 23:59 <a href="http://www.shnenglu.com/shaker/archive/2007/08/28/31075.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用boost::signalsq接cL员函?/title><link>http://www.shnenglu.com/shaker/archive/2007/08/28/31073.html</link><dc:creator>shaker(太子)</dc:creator><author>shaker(太子)</author><pubDate>Tue, 28 Aug 2007 15:40:00 GMT</pubDate><guid>http://www.shnenglu.com/shaker/archive/2007/08/28/31073.html</guid><wfw:comment>http://www.shnenglu.com/shaker/comments/31073.html</wfw:comment><comments>http://www.shnenglu.com/shaker/archive/2007/08/28/31073.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/shaker/comments/commentRss/31073.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/shaker/services/trackbacks/31073.html</trackback:ping><description><![CDATA[使用boost::signalsq接cL员函?参照boost的手?a >http://www.boost.org/doc/html/signals/tutorial.html#id1627926</a> 的写法似乎在VS2005中编译不q?不知道是新的版本更新了还是手册的bug<br>应该使用cM以下的方?<br> <div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.shnenglu.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">    m_Game.sig_HpChange.connect(boost::bind(</span><span style="COLOR: #000000">&</span><span style="COLOR: #000000">CMainFrame::OnHpChange,boost::</span><span style="COLOR: #0000ff">ref</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">),_1,_2));<br><img src="http://www.shnenglu.com/Images/OutliningIndicators/None.gif" align=top></span></div><img src ="http://www.shnenglu.com/shaker/aggbug/31073.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/shaker/" target="_blank">shaker(太子)</a> 2007-08-28 23:40 <a href="http://www.shnenglu.com/shaker/archive/2007/08/28/31073.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>write a simple os with asm&chttp://www.shnenglu.com/shaker/archive/2007/05/20/24411.htmlshaker(太子)shaker(太子)Sat, 19 May 2007 16:26:00 GMThttp://www.shnenglu.com/shaker/archive/2007/05/20/24411.htmlhttp://www.shnenglu.com/shaker/comments/24411.htmlhttp://www.shnenglu.com/shaker/archive/2007/05/20/24411.html#Feedback23http://www.shnenglu.com/shaker/comments/commentRss/24411.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/24411.html阅读全文

shaker(太子) 2007-05-20 00:26 发表评论
]]>
以前写的一个网l流包c?/title><link>http://www.shnenglu.com/shaker/archive/2007/04/23/22682.html</link><dc:creator>shaker(太子)</dc:creator><author>shaker(太子)</author><pubDate>Mon, 23 Apr 2007 15:44:00 GMT</pubDate><guid>http://www.shnenglu.com/shaker/archive/2007/04/23/22682.html</guid><wfw:comment>http://www.shnenglu.com/shaker/comments/22682.html</wfw:comment><comments>http://www.shnenglu.com/shaker/archive/2007/04/23/22682.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.shnenglu.com/shaker/comments/commentRss/22682.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/shaker/services/trackbacks/22682.html</trackback:ping><description><![CDATA[     摘要: CStreamPacket: 为无固定l构的网l数据包传送设计的.  <a href='http://www.shnenglu.com/shaker/archive/2007/04/23/22682.html'>阅读全文</a><img src ="http://www.shnenglu.com/shaker/aggbug/22682.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/shaker/" target="_blank">shaker(太子)</a> 2007-04-23 23:44 <a href="http://www.shnenglu.com/shaker/archive/2007/04/23/22682.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用boost::regex来做词法分析http://www.shnenglu.com/shaker/archive/2007/04/11/21653.htmlshaker(太子)shaker(太子)Wed, 11 Apr 2007 07:20:00 GMThttp://www.shnenglu.com/shaker/archive/2007/04/11/21653.htmlhttp://www.shnenglu.com/shaker/comments/21653.htmlhttp://www.shnenglu.com/shaker/archive/2007/04/11/21653.html#Feedback0http://www.shnenglu.com/shaker/comments/commentRss/21653.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/21653.html阅读全文

shaker(太子) 2007-04-11 15:20 发表评论
]]>
MSDN的Bug!http://www.shnenglu.com/shaker/archive/2007/02/28/19073.htmlshaker(太子)shaker(太子)Wed, 28 Feb 2007 14:59:00 GMThttp://www.shnenglu.com/shaker/archive/2007/02/28/19073.htmlhttp://www.shnenglu.com/shaker/comments/19073.htmlhttp://www.shnenglu.com/shaker/archive/2007/02/28/19073.html#Feedback3http://www.shnenglu.com/shaker/comments/commentRss/19073.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/19073.html
CListCtrl::SortItems

This method sorts list view items using an application-defined comparison function. The index of each item changes to reflect the new sequence.

BOOL SortItems( PFNLVCOMPARE pfnCompare, DWORD dwData);
Parameters
pfnCompare
Specifies the address of the application-defined comparison function. The comparison function is called during the sort operation each time the relative order of two list items needs to be compared. The comparison function must be either a static member of a class or a standalone function that is not a member of any class.
dwData
Specifies the application-defined value that is passed to the comparison function.
Return Value

Nonzero if it is successful; otherwise, it is zero. Remarks
The comparison function has the following form:
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2,   LPARAM lParamSort);

The comparison function must return a negative value if the first item should precede the second, a positive value if the first item should follow the second, or zero if the two items are equivalent.
The lParam1 and lParam2 parameters specify the item data for the two items being compared. The lParamSort parameter is the same as the dwData value.

Example


// Sort the item in reverse alphabetical order.
static int CALLBACK 
MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
   
// lParamSort contains a pointer to the list view control.
   CListCtrl* pListCtrl = (CListCtrl*) lParamSort;
   CString    strItem1 
= pListCtrl->GetItemText(lParam1, 0);
   CString    strItem2 
= pListCtrl->GetItemText(lParam2, 0);

   
return strcmp(strItem2, strItem1);
}

void snip_CListCtrl_SortItems()
{
   
// The pointer to my list view control.
   extern CListCtrl* pmyListCtrl;

   
// Sort the list view items using my callback procedure.
   pmyListCtrl->SortItems(MyCompareProc, (LPARAM) pmyListCtrl);
}


例子中的代码是不能按照预想的工作? 因ؓ回调函数接收?个参数是?strong>SetItemData传入的?而不是Item的Index!

shaker(太子) 2007-02-28 22:59 发表评论
]]>
更新了下TMD!几张截图http://www.shnenglu.com/shaker/archive/2007/02/28/19072.htmlshaker(太子)shaker(太子)Wed, 28 Feb 2007 14:45:00 GMThttp://www.shnenglu.com/shaker/archive/2007/02/28/19072.htmlhttp://www.shnenglu.com/shaker/comments/19072.htmlhttp://www.shnenglu.com/shaker/archive/2007/02/28/19072.html#Feedback5http://www.shnenglu.com/shaker/comments/commentRss/19072.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/19072.html阅读全文

shaker(太子) 2007-02-28 22:45 发表评论
]]>
[工具]VSS2005 下蝲http://www.shnenglu.com/shaker/archive/2006/12/31/17052.htmlshaker(太子)shaker(太子)Sat, 30 Dec 2006 17:22:00 GMThttp://www.shnenglu.com/shaker/archive/2006/12/31/17052.htmlhttp://www.shnenglu.com/shaker/comments/17052.htmlhttp://www.shnenglu.com/shaker/archive/2006/12/31/17052.html#Feedback9http://www.shnenglu.com/shaker/comments/commentRss/17052.htmlhttp://www.shnenglu.com/shaker/services/trackbacks/17052.htmlhttp://www.shangdu8.com/download/Microsoft.Visual.SourceSafe.2005_01.rar

shaker(太子) 2006-12-31 01:22 发表评论
]]>
VC2003~译boost::regex模块命o?/title><link>http://www.shnenglu.com/shaker/archive/2006/12/19/16622.html</link><dc:creator>shaker(太子)</dc:creator><author>shaker(太子)</author><pubDate>Tue, 19 Dec 2006 11:54:00 GMT</pubDate><guid>http://www.shnenglu.com/shaker/archive/2006/12/19/16622.html</guid><wfw:comment>http://www.shnenglu.com/shaker/comments/16622.html</wfw:comment><comments>http://www.shnenglu.com/shaker/archive/2006/12/19/16622.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/shaker/comments/commentRss/16622.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/shaker/services/trackbacks/16622.html</trackback:ping><description><![CDATA[bjam "-sTOOLS=vc-7_1" "-sVC71_ROOT=D:\Program Files\Microsoft Visual Studio .NET 2003\Vc7" --with-regex install<img src ="http://www.shnenglu.com/shaker/aggbug/16622.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/shaker/" target="_blank">shaker(太子)</a> 2006-12-19 19:54 <a href="http://www.shnenglu.com/shaker/archive/2006/12/19/16622.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZT]“Hello World!”的NU写?/title><link>http://www.shnenglu.com/shaker/archive/2006/12/19/16620.html</link><dc:creator>shaker(太子)</dc:creator><author>shaker(太子)</author><pubDate>Tue, 19 Dec 2006 11:16:00 GMT</pubDate><guid>http://www.shnenglu.com/shaker/archive/2006/12/19/16620.html</guid><wfw:comment>http://www.shnenglu.com/shaker/comments/16620.html</wfw:comment><comments>http://www.shnenglu.com/shaker/archive/2006/12/19/16620.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/shaker/comments/commentRss/16620.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/shaker/services/trackbacks/16620.html</trackback:ping><description><![CDATA[“Hello World!”的NU写?br /><a >http://www.ccrun.com/article.asp?i=999&d=v433i2</a><img src ="http://www.shnenglu.com/shaker/aggbug/16620.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/shaker/" target="_blank">shaker(太子)</a> 2006-12-19 19:16 <a href="http://www.shnenglu.com/shaker/archive/2006/12/19/16620.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.sohucn.com.cn" target="_blank">ݺɫɫݺݺۺϾþ</a>| <a href="http://www.dx37.cn" target="_blank">þAVij</a>| <a href="http://www.diy800.cn" target="_blank">þþƷ}Ů</a>| <a href="http://www.mfsdrj.com.cn" target="_blank">þþþAVƬ </a>| <a href="http://www.areall.cn" target="_blank">þþþƷһ</a>| <a href="http://www.jcwmt.cn" target="_blank">þۺɫHEZYO</a>| <a href="http://www.cbhz.com.cn" target="_blank">aaþʦ2021Ʒ </a>| <a href="http://www.sxstreet.cn" target="_blank">þþþۺþ</a>| <a href="http://www.ampv.cn" target="_blank">Ʒһþ</a>| <a href="http://www.07sn.cn" target="_blank">ɫ͵͵888ŷƷþþ</a>| <a href="http://www.kxvd.cn" target="_blank">þù׾Ʒҹ</a>| <a href="http://www.gzsaikou.cn" target="_blank">99þùۺϾƷԭ</a>| <a href="http://www.yingkounews.com.cn" target="_blank">þ99Ʒþþþþ</a>| <a href="http://www.tmuseum.cn" target="_blank">Ҳȥþۺ</a>| <a href="http://www.5656936.cn" target="_blank">պƷþһ</a>| <a href="http://www.sanghuan.cn" target="_blank">99þùۺϾƷŮͬͼƬ</a>| <a href="http://www.lianliankan123.cn" target="_blank">þþWWWѾƷ</a>| <a href="http://www.gyvgllqc.cn" target="_blank">þ99Ļþ</a>| <a href="http://www.suppin.cn" target="_blank">þ㽶ۺɫһۺɫ88</a>| <a href="http://www.ysbzxx.com.cn" target="_blank">޾ƷþëƬ</a>| <a href="http://www.yikafei.cn" target="_blank">޹Ʒþþþվ</a>| <a href="http://www.mk606.cn" target="_blank">޾Ʒרþþ</a>| <a href="http://www.2mo04k.cn" target="_blank">ĻþþƷ</a>| <a href="http://www.diylady.cn" target="_blank">þ99Ʒþþþþö̬ͼ</a>| <a href="http://www.1dis.cn" target="_blank">þAVۺϺɫ</a>| <a href="http://www.csrencaiwd.cn" target="_blank">þľƷĻ</a>| <a href="http://www.seo9968.cn" target="_blank">Ʒһþ</a>| <a href="http://www.jl893.com.cn" target="_blank">Ʒݾþþþ</a>| <a href="http://www.bamboomart.cn" target="_blank">Ʒ޾þþþþ</a>| <a href="http://www.51hitjj.cn" target="_blank">Ʒٸavþ</a>| <a href="http://www.fmzz6688.cn" target="_blank">97þùۺϾƷŮ</a>| <a href="http://www.ks-design.cn" target="_blank">þþwww˳</a>| <a href="http://www.onlinehotel.com.cn" target="_blank">Ʒŷþþþ޹</a>| <a href="http://www.aamih.cn" target="_blank">ƷþþӰ</a>| <a href="http://www.talkvo.cn" target="_blank">Ʒ99þѹۿ</a>| <a href="http://www.idccyy.cn" target="_blank">þĻȫ</a>| <a href="http://www.u18718.cn" target="_blank">ۺϳ˾þôƬ91</a>| <a href="http://www.jinziwan.com.cn" target="_blank">ܻƺ۵վþmimiɫ</a>| <a href="http://www.mianfeiwg.cn" target="_blank">þþþþþ97</a>| <a href="http://www.qvnb.cn" target="_blank">ȫɫƴɫƬѾþþ</a>| <a href="http://www.idigest.com.cn" target="_blank">Ʒþþþþþapp</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>