CreateThread,AfxBeginThread,PsCreateSystemThread

来源:互联网 发布:淘宝手机端支持gif吗 编辑:程序博客网 时间:2024/05/17 22:58

先举一些使用的例子:

1):

  //在类中创建一个线程:

void CQboxMacDlg::OnOK()

{

   // --------------------------

    DWORD dwThreadId;
    m_hMyThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)MyThreadProc, this,0,&dwThreadId);

}

//线程函数,必须为外部函数或者类的静态成员函数。

UINT MyThreadProc( LPVOID lpParam )

{

        CQboxMacDlg *pDlg = (CQboxMacDlg *)lpParam;

        DWORD ret;

        ret = pDlg->DlgThread(lpParam);

        if(m_hMyThread)

         {

               CloseHandle(m_hMyThread);

               m_hMyThread = NULL;

          }

        return ret;

}

//这里通过一个外部函数转换一下,线程函数我还是想用类里的非静态成员函数。所以这里是真正的线程实现函数:

DWORD CQboxMacDlg::DlgThread(LPVOID lpParam)

{

   //------------

       return 0;

}

 

2):

// 内核中创建一个线程:

        HANDLE thread = NULL;

        BOOL status;

        status = PsCreateSystemThread(&thread,0L,NULL,NULL,NULL,(PKSTART_ROUTINE)CommonInterfaceTask,NULL);

// 类的静态成员函数:

static VOID  CommonInterfaceTask();

VOID CDVBSDemod::CommonInterfaceTask()

{

          //--------

          KeSetPriorityThread(KeGetCurrentThread( ),HIGH_PRIORITY);

          //----------

          ntStatus = PsTerminateSystemThread(STATUS_SUCCESS);

 

}

详细介绍他们:

(一)

Win32的进程处理简介

因为MFC没有提供类处理进程,所以直接使用了Win32 API函数。

     

  1. 进程的创建

    调用CreateProcess函数创建新的进程,运行指定的程序。CreateProcess的原型如下:

    BOOL CreateProcess(

    LPCTSTR lpApplicationName,

    LPTSTR lpCommandLine,

    LPSECURITY_ATTRIBUTES lpProcessAttributes,

    LPSECURITY_ATTRIBUTES lpThreadAttributes,

    BOOL bInheritHandles,

    DWORD dwCreationFlags,

    LPVOID lpEnvironment,

    LPCTSTR lpCurrentDirectory,

    LPSTARTUPINFO lpStartupInfo,

    LPPROCESS_INFORMATION lpProcessInformation

    );

    其中:

    lpApplicationName指向包含了要运行模块名字的字符串。

    lpCommandLine指向命令行字符串。

    lpProcessAttributes描述进程的安全性属性,NT下有用。

    lpThreadAttributes描述进程初始线程(主线程)的安全性属性,NT下有用。

    bInHeritHandles表示子进程(被创建的进程)是否可以继承父进程的句柄。可以继承的句柄有线程句柄、有名或无名管道、互斥对象、事件、信号量、映像文件、普通文件和通讯端口等;还有一些句柄不能被继承,如内存句柄、DLL实例句柄、GDI句柄、URER句柄等等。

    子进程继承的句柄由父进程通过命令行方式或者进程间通讯(IPC)方式由父进程传递给它。

    dwCreationFlags表示创建进程的优先级类别和进程的类型。创建进程的类型分控制台进程、调试进程等;优先级类别用来控制进程的优先级别,分Idle、Normal、High、Real_time四个类别。

    lpEnviroment指向环境变量块,环境变量可以被子进程继承。

    lpCurrentDirectory指向表示当前目录的字符串,当前目录可以继承。

    lpStartupInfo指向StartupInfo结构,控制进程的主窗口的出现方式。

    lpProcessInformation指向PROCESS_INFORMATION结构,用来存储返回的进程信息。

    从其参数可以看出创建一个新的进程需要指定什么信息。

    从上面的解释可以看出,一个进程包含了很多信息。若进程创建成功的话,返回一个进程信息结构类型的指针。进程信息结构如下:

    typedef struct _PROCESS_INFORMATION {

    HANDLE hProcess;

    HANDLE hThread;

    DWORD dwProcessId;

    DWORD dwThreadId;

    }PROCESS_INFORMATION;

    进程信息结构包括进程句柄,主线程句柄,进程ID,主线程ID。

  2. 进程的终止

进程在以下情况下终止:

     

  • 调用ExitProcess结束进程;
  • 进程的主线程返回,隐含地调用ExitProcess导致进程结束;
  • 进程的最后一个线程终止;
  • 调用TerminateProcess终止进程。
  • 当要结束一个GDI进程时,发送WM_QUIT消息给主窗口,当然也可以从它的任一线程调用ExitProcess。
  1.  

       

    1. Win32的线程

         

      1. 线程的创建

使用CreateThread函数创建线程,CreateThread的原型如下:

HANDLE CreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttributes,

DWORD dwStackSize,

LPTHREAD_START_ROUTINE lpStartAddress,

LPVOID lpParameter,

DWORD dwCreationFlags, // creation flags

LPDWORD lpThreadId

);

其中:

lpThreadAttributes表示创建线程的安全属性,NT下有用。

dwStackSize指定线程栈的尺寸,如果为0则与进程主线程栈相同。

lpStartAddress指定线程开始运行的地址。

lpParameter表示传递给线程的32位的参数。

dwCreateFlages表示是否创建后挂起线程(取值CREATE_SUSPEND),挂起后调用ResumeThread继续执行。

lpThreadId用来存放返回的线程ID。

     

  • 线程的优先级别

进程的每个优先级类包含了五个线程的优先级水平。在进程的优先级类确定之后,可以改变线程的优先级水平。用SetPriorityClass设置进程优先级类,用SetThreadPriority设置线程优先级水平。

Normal级的线程可以被除了Idle级以外的任意线程抢占。

  1.  
    1.  

         

      1. 线程的终止

以下情况终止一个线程:

     

  • 调用了ExitThread函数;
  • 线程函数返回:主线程返回导致ExitProcess被调用,其他线程返回导致ExitThread被调用;
  • 调用ExitProcess导致进程的所有线程终止;
  • 调用TerminateThread终止一个线程;
  • 调用TerminateProcess终止一个进程时,导致其所有线程的终止。

当用TerminateProcess或者TerminateThread终止进程或线程时,DLL的入口函数DllMain不会被执行(如果有DLL的话)。

  1.  
    1.  

         

      1. 线程局部存储

如果希望每个线程都可以有线程局部(Thread local)的静态存储数据,可以使用TLS线程局部存储技术。TLS为进程分配一个TLS索引,进程的每个线程通过这个索引存取自己的数据变量的拷贝。

TLS对DLL是非常有用的。当一个新的进程使用DLL时,在DLL入口函数DllMain中使用TlsAlloc分配TLS索引,TLS索引就作为进程私有的全局变量被保存;以后,当该进程的新的线程使用DLL时(Attahced to DLL),DllMain给它分配动态内存并且使用TlsSetValue把线程私有的数据按索引保存。DLL函数可以使用TlsGetValue按索引读取调用线程的私有数据。

TLS函数如下:

     

  • DWORD TlsAlloc()

在进程或DLL初始化时调用,并且把返回值(索引值)作为全局变量保存。

     

  • BOOL TlsSetValue(

DWORD dwTlsIndex, //TLS index to set value for

LPVOID lpTlsValue //value to be stored

);

其中:

dwTlsIndex是TlsAlloc分配的索引。

lpTlsValue是线程在TLS槽中存放的数据指针,指针指向线程要保存的数据。

线程首先分配动态内存并保存数据到此内存中,然后调用TlsSetValue保存内存指针到TLS槽。

     

  • LPVOID TlsGetValue(

DWORD dwTlsIndex // TLS index to retrieve value for

);

其中:

dwTlsIndex是TlsAlloc分配的索引。

当要存取保存的数据时,使用索引得到数据指针。

     

  • BOOL TlsFree(

DWORD dwTlsIndex // TLS index to free

);

其中:

dwTlsIndex是TlsAlloc分配的索引。

当每一个线程都不再使用局部存储数据时,线程释放它分配的动态内存。在TLS索引不再需要时,使用TlsFree释放索引。

  1.  

       

    1. 线程同步

      同步可以保证在一个时间内只有一个线程对某个资源(如操作系统资源等共享资源)有控制权。共享资源包括全局变量、公共数据成员或者句柄等。同步还可以使得有关联交互作用的代码按一定的顺序执行。

      Win32提供了一组对象用来实现多线程的同步。

      这些对象有两种状态:获得信号(Signaled)或者没有或则信号(Not signaled)。线程通过Win32 API提供的同步等待函数(Wait functions)来使用同步对象。一个同步对象在同步等待函数调用时被指定,调用同步函数地线程被阻塞(blocked),直到同步对象获得信号。被阻塞的线程不占用CPU时间。

         

      1. 同步对象

同步对象有:Critical_section(关键段),Event(事件),Mutex(互斥对象),Semaphores(信号量)。

下面,解释怎么使用这些同步对象。

     

  1. 关键段对象:

    首先,定义一个关键段对象cs:

    CRITICAL_SECTION cs;

    然后,初始化该对象。初始化时把对象设置为NOT_SINGALED,表示允许线程使用资源:

    InitializeCriticalSection(&cs);

    如果一段程序代码需要对某个资源进行同步保护,则这是一段关键段代码。在进入该关键段代码前调用EnterCriticalSection函数,这样,其他线程都不能执行该段代码,若它们试图执行就会被阻塞。

    完成关键段的执行之后,调用LeaveCriticalSection函数,其他的线程就可以继续执行该段代码。如果该函数不被调用,则其他线程将无限期的等待。

  2. 事件对象

    首先,调用CreateEvent函数创建一个事件对象,该函数返回一个事件句柄。然后,可以设置(SetEvent)或者复位(ResetEvent)一个事件对象,也可以发一个事件脉冲(PlusEvent),即设置一个事件对象,然后复位它。复位有两种形式:自动复位和人工复位。在创建事件对象时指定复位形式。。

    自动复位:当对象获得信号后,就释放下一个可用线程(优先级别最高的线程;如果优先级别相同,则等待队列中的第一个线程被释放)。

    人工复位:当对象获得信号后,就释放所有可利用线程。

    最后,使用CloseHandle销毁创建的事件对象。

  3. 互斥对象

    首先,调用CreateMutex创建互斥对象;然后,调用等待函数,可以的话利用关键资源;最后,调用RealseMutex释放互斥对象。

    互斥对象可以在进程间使用,但关键段对象只能用于同一进程的线程之间。

  4. 信号量对象

    在Win32中,信号量的数值变为0时给以信号。在有多个资源需要管理时可以使用信号量对象。

    首先,调用CreateSemaphore创建一个信号量;然后,调用等待函数,如果允许的话,则利用关键资源;最后,调用RealeaseSemaphore释放信号量对象。

  5. 此外,还有其他句柄可以用来同步线程:

文件句柄(FILE HANDLES)

命名管道句柄(NAMED PIPE HANDELS)

控制台输入缓冲区句柄(CONSOLE INPUT BUFFER HANDLES)

通讯设备句柄(COMMUNICTION DEVICE HANDLES)

进程句柄(PROCESS HANDLES)

线程句柄(THREAD HANDLES)

例如,当一个进程或线程结束时,进程或线程句柄获得信号,等待该进程或者线程结束的线程被释放。

  1.  
    1.  

         

      1. 等待函数

Win32提供了一组等待函数用来让一个线程阻塞自己的执行。等待函数分三类:

     

  1. 等待单个对象的(FOR SINGLE OBJECT):

    这类函数包括:

    SignalObjectAndWait

    WaitForSingleObject

    WaitForSingleObjectEx

    函数参数包括同步对象的句柄和等待时间等。

    在以下情况下等待函数返回:

    同步对象获得信号时返回;

    等待时间达到了返回:如果等待时间不限制(Infinite),则只有同步对象获得信号才返回;如果等待时间为0,则在测试了同步对象的状态之后马上返回。

  2. 等待多个对象的(FOR MULTIPLE OBJECTS)

    这类函数包括:

    WaitForMultipleObjects

    WaitForMultipleObjectsEx

    MsgWaitForMultipleObjects

    MsgWaitForMultipleObjectsEx

    函数参数包括同步对象的句柄,等待时间,是等待一个还是多个同步对象等等。

    在以下情况下等待函数返回:

    一个或全部同步对象获得信号时返回(在参数中指定是等待一个或多个同步对象);

    等待时间达到了返回:如果等待时间不限制(Infinite),则只有同步对象获得信号才返回;如果等待时间为0,则在测试了同步对象的状态之后马上返回。

  3. 可以发出提示的函数(ALTERABLE)

这类函数包括:

MsgWaitForMultipleObjectsEx

SignalObjectAndWait

WaitForMultipleObjectsEx

WaitForSingleObjectEx

这些函数主要用于重叠(Overlapped)的I/O(异步I/O)。

  1.  

       

    1. MFC的线程处理

      在Win32 API的基础之上,MFC提供了处理线程的类和函数。处理线程的类是CWinThread,函数是AfxBeginThread、AfxEndThread等。

      表5-6解释了CWinThread的成员变量和函数。

      CWinThread是MFC线程类,它的成员变量m_hThread和m_hThreadID是对应的Win32线程句柄和线程ID。

      MFC明确区分两种线程:用户界面线程(User interface thread)和工作者线程(Worker thread)。用户界面线程一般用于处理用户输入并对用户产生的事件和消息作出应答。工作者线程用于完成不要求用户输入的任务,如耗时计算。

      Win32 API并不区分线程类型,它只需要知道线程的开始地址以便它开始执行线程。MFC为用户界面线程特别地提供消息泵来处理用户界面的事件。CWinApp对象是用户界面线程对象的一个例子,CWinApp从类CWinThread派生并处理用户产生的事件和消息。

         

      1. 创建用户界面线程

通过以下步骤创建一个用户界面线程:

     

  • 从CWinThread派生一个有动态创建能力的类。使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏来支持动态创建。
  • 覆盖CWinThread的一些虚拟函数,可以覆盖的函数见表5-4关于CWinThread的部分。其中,函数InitInstance是必须覆盖的,ExitInstance通常是要覆盖的。
  • 使用AfxBeginThread创建MFC线程对象和Win32线程对象。如果创建线程时没有指定CREATE_SUSPENDED,则开始执行线程。
  • 如果创建线程是指定了CREATE_SUSPENDED,则在适当的地方调用函数ResumeThread开始执行线程。
  1.  
    1.  

         

      1. 创建工作者线程

        程序员不必从CWinThread派生新的线程类,只需要提供一个控制函数,由线程启动后执行该函数。

        然后,使用AfxBeginThread创建MFC线程对象和Win32线程对象。如果创建线程时没有指定CREATE_SUSPENDED(创建后挂起),则创建的新线程开始执行。

        如果创建线程是指定了CREATE_SUSPENDED,则在适当的地方调用函数ResumeThread开始执行线程。

        虽然程序员没有从CWinThread派生类,但是MFC给工作者线程提供了缺省的CWinThread对象。

      2. AfxBeginThread

用户界面线程和工作者线程都是由AfxBeginThread创建的。现在,考察该函数:MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,分别有如下的原型和过程:

     

  1. 用户界面线程的AfxBeginThread

    用户界面线程的AfxBeginThread的原型如下:

    CWinThread* AFXAPI AfxBeginThread(

    CRuntimeClass* pThreadClass,

    int nPriority,

    UINT nStackSize,

    DWORD dwCreateFlags,

    LPSECURITY_ATTRIBUTES lpSecurityAttrs)

    其中:

    参数1是从CWinThread派生的RUNTIME_CLASS类;

    参数2指定线程优先级,如果为0,则与创建该线程的线程相同;

    参数3指定线程的堆栈大小,如果为0,则与创建该线程的线程相同;

    参数4是一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。

    参数5表示线程的安全属性,NT下有用。

  2. 工作者线程的AfxBeginThread

    工作者线程的AfxBeginThread的原型如下:

    CWinThread* AFXAPI AfxBeginThread(

    AFX_THREADPROC pfnThreadProc,

    LPVOID pParam,

    int nPriority,

    UINT nStackSize,

    DWORD dwCreateFlags,

    LPSECURITY_ATTRIBUTES lpSecurityAttrs)

    其中:

    参数1指定控制函数的地址;

    参数2指定传递给控制函数的参数;

    参数3、4、5分别指定线程的优先级、堆栈大小、创建标识、安全属性,含义同用户界面线程。

  3. AfxBeginThread创建线程的流程

不论哪个AfxBeginThread,首先都是创建MFC线程对象,然后创建Win32线程对象。在创建MFC线程对象时,用户界面线程和工作者线程的创建分别调用了不同的构造函数。用户界面线程是从CWinThread派生的,所以,要先调用派生类的缺省构造函数,然后调用CWinThread的缺省构造函数。图8-1中两个构造函数所调用的CommonConstruct是MFC内部使用的成员函数。

  1.  
    1.  

         

      1. CreateThread和_AfxThreadEntry

        MFC使用CWinThread::CreateThread创建线程,不论对工作者线程或用户界面线程,都指定线程的入口函数是_AfxThreadEntry。_AfxThreadEntry调用AfxInitThread初始化线程。

        CreateThread和_AfxThreadEntry在线程的创建过程中使用同步手段交互等待、执行。CreateThread由创建线程执行,_AfxThreadEntry由被创建的线程执行,两者通过两个事件对象(hEvent和hEvent2)同步:

        在创建了新线程之后,创建线程将在hEvent事件上无限等待直到新线程给出创建结果;新线程在创建成功或者失败之后,触发事件hEvent让父线程运行,并且在hEven2上无限等待直到父线程退出CreateThread函数;父线程(创建线程)因为hEvent的置位结束等待,继续执行,退出CreateThread之前触发hEvent2事件;新线程(子线程)因为hEvent2的置位结束等待,开始执行控制函数(工作者线程)或者进入消息循环(用户界面线程)。

        MFC在线程创建中使用了如下数据结构:

        struct _AFX_THREAD_STARTUP

        {

        //传递给线程启动的参数(IN)

        _AFX_THREAD_STATE* pThreadState;//父线程的线程状态

        CWinThread* pThread; //新创建的MFC线程对象

        DWORD dwCreateFlags; //线程创建标识

        _PNH pfnNewHandler; //新线程的句柄

        HANDLE hEvent; //同步事件,线程创建成功或失败后置位

        HANDLE hEvent2; //同步事件,新线程恢复执行后置位

        //返回给创建线程的参数,在新线程恢复执行后赋值

        BOOL bError; //如果创建发生错误,TRUE

        };

        该结构作为线程开始函数的参数被传递给_beginthreadex函数来创建和启动线程。_beginthreadex函数是“C”的线程创建函数,具有如下原型:

        unsigned long _beginthreadex(

        void *security,

        unsigned stack_size,

        unsigned ( __stdcall *start_address )( void * ),

        void *arglist,

        unsigned initflag,

        unsigned *thrdaddr );

        图8-2描述了上述过程。图中表示,_AfxThreadEntry在启动线程时,将创建本线程的线程状态,并且继承父线程的模块状态。关于MFC状态,见第9章。

         

         

      2. 线程的结束

        从图8-2可以看出,AfxEndThread用来结束调用它的线程:它将清理本线程创建的MFC对象和释放线程局部存储分配的内存空间;调用CWinThread的虚拟函数Delete;调用“C”的结束线程函数_endthreadex释放分配给线程的资源,但是不关闭线程句柄。

        CWinThread::Delete的缺省实现是:如果本线程的成员函数m_bDelete为TRUE,则调用“C”运算符号delete销毁MFC线程对象自身(delete this),这将导致线程对象的析构函数被调用。若析构函数检测线程句柄非空则调用CloseHandle关闭它。

        通常,让m_bDelete为TRUE以便自动地销毁线程对象,释放内存空间(MFC内存对象在堆中分配)。但是,有时候,在线程结束之后(Win32线程已经不存在)保留MFC线程对象是有用的,当然程序员自己最后要记得销毁该线程对象。

      3. 实现线程的消息循环

在MFC中,消息循环是由线程完成的。一般地,可以使用MFC缺省的消息循环(即使用函数CWindThrad::Run),但是,有些时候需要程序员自己实现一个线程的消息循环,比如在用户界面线程进行一个长时间计算处理或者等待另一个线程时。一般有如下形式:

while ( bDoingBackgroundProcessing)

{

MSG msg;

while ( ::PeekMessage( &msg, NULL,0, 0, PM_NOREMOVE ) )

{

if ( !PumpMessage( ) )

{

bDoingBackgroundProcessing = FALSE;

::PostQuitMessage( );

break;

}

}

// let MFC do its idle processing

LONG lIdle = 0;

while ( AfxGetApp()->OnIdle(lIdle++ ) );

// Perform some background processing here

// using another call to OnIdle

}

该段代码的解释参见图5-3对线程的Run函数的图解。

程序员实现线程的消息循环有两个好处,一是顾及了MFC的Idle处理机制;二是在长时间的处理中可以响应用户产生的事件或者消息。

在同步对象上等待其他线程时,也可以使用同样的方式,只要把条件

bDoingBackgroundProcessing

换成如下形式:

WaitForSingObject(hHandleOfEvent,0) == WAIT_TIMEOUT

即可。

(二)

AfxBeginThread函数初探[转]

在进行多线程程序设计的时候,我们经常用到AfxBeginThread函数来启动一条线程
该函数使用起来非常的简单方便,其定义如下

CWinThread* AfxBeginThread(
   AFX_THREADPROC pfnThreadProc,//线程函数地址
   LPVOID pParam,//线程参数
   int nPriority = THREAD_PRIORITY_NORMAL,//线程优先级
   UINT nStackSize = 0,//线程堆栈大小,默认为1M
   DWORD dwCreateFlags = 0,//
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);

CWinThread* AfxBeginThread(
   CRuntimeClass* pThreadClass,
   int nPriority = THREAD_PRIORITY_NORMAL,
   UINT nStackSize = 0,
   DWORD dwCreateFlags = 0,
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);

参数说明:
pfnThreadProc:线程函数的地址,该参数不能设置为NULL,线程函数必须定义成全局函数或者类的静态成员函数
例如:
UINT myThreadFunc(LPVOID lparam)
或者
class A
{
public:
        static UINT __stdcall myThreadFunc(LPVOID lparam);
}
之所以要定义成类的静态成员函数,是因为类的静态成员函数不属于某个类对象,这样在调用函数
的时候就不用传递一个额外的this指针.

pThreadClass:指向从CWinThread派生的子类对象的RUNTIME_CLASS

pParam:要传递给线程函数的参数

nPriority:要启动的线程的优先级,默认优先级为THREAD_PRIORITY_NORMAL(普通优先级),关于线程
 优先级的详细说明请参考Platform SDK SetThreadPriority函数说明

nStackSize:新线程的堆栈大小,如果设置为0,则使用默认大小,在应用程序中一般情况下线程的默认堆栈大小
 为1M

dwCreateFlags:线程创建标志,该参数可以指定为下列标志
 CREATE_SUSPENDED:以挂起方式启动线程,如果你在线程启动之前想初始化一些CWinThread类中的一些成员变量
 比如:m_bAutoDelete或者你的派生类中的成员变量,当初始化完成之后,你可以使用CWinThread类的ResumeThread
 成员函数来恢复线程的运行
 如果把该标志设置为0,则表示立即启动线程
lpSecurityAttrs:指向安全描述符的指针,如果使用默认的安全级别只要讲该参数设置为NULL就可以了!

上面就是AfxBeginThread函数的简单说明,我们在使用的时候一般情况下只要指定前两个参数,其他
参数使用默认值就可以.嗯,的确,使用起来是很简单,只要这个函数一被调用,就创建了一个线程.
但是大家有没有想过,AfxBeginThread函数究竟是如何启动的线程呢?它的内部是如何实现的呢?

下面我们就来看一下AfxBeginThread函数的内部实现

//启动worker线程
CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
 int nPriority, UINT nStackSize, DWORD dwCreateFlags,
 LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
         pfnThreadProc;
         pParam;
         nPriority;
         nStackSize;
         dwCreateFlags;
         lpSecurityAttrs;

         return NULL;
#else
         ASSERT(pfnThreadProc != NULL);

         CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
         ASSERT_VALID(pThread);

         if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
                  lpSecurityAttrs))
         {
                  pThread->Delete();
                  return NULL;
         }
         VERIFY(pThread->SetThreadPriority(nPriority));
         if (!(dwCreateFlags & CREATE_SUSPENDED))
                  VERIFY(pThread->ResumeThread() != (DWORD)-1);

         return pThread;
#endif //!_MT)
}

//启动UI线程
CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass,
 int nPriority, UINT nStackSize, DWORD dwCreateFlags,
 LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
        pThreadClass;
        nPriority;
        nStackSize;
        dwCreateFlags;
        lpSecurityAttrs;

        return NULL;
#else
        ASSERT(pThreadClass != NULL);
        ASSERT(pThreadClass->IsDerivedFrom(RUNTIME_CLASS(CWinThread)));

        CWinThread* pThread = (CWinThread*)pThreadClass->CreateObject();
        if (pThread == NULL)
                AfxThrowMemoryException();
        ASSERT_VALID(pThread);

        pThread->m_pThreadParams = NULL;
        if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
                lpSecurityAttrs))
        {
                pThread->Delete();
                return NULL;
        }
        VERIFY(pThread->SetThreadPriority(nPriority));
        if (!(dwCreateFlags & CREATE_SUSPENDED))
                VERIFY(pThread->ResumeThread() != (DWORD)-1);

        return pThread;
#endif //!_MT
}

从上面的代码中可以看出AfxBeginThread所做的事情主要有以下几点:

1.在heap中配置一个新的CWinThread对象(worker线程)
代码如:CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
调用CRuntimeClass结构中的CreateObject函数创建CWinThread对象
CWinThread* pThread = (CWinThread*)pThreadClass->CreateObject();
CRuntimeClass以及MFC相关类的内部实现,详情请参考
《深入浅出MFC》侯捷著

2.调用CWinThread::CreateThread()并设定属性,使线程以挂起状态产生
pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,lpSecurityAttrs);

3.设定线程的优先权
pThread->SetThreadPriority(nPriority);

4.调用CWinThread::ResumeThread
pThread->ResumeThread();

(三)

NTSTATUS PsCreateSystemThread(
__out PHANDLE ThreadHandle, __out PHANDLE ThreadHandle,
__in隆DesiredAccess, __in ULONG DesiredAccess,
__in_opt POBJECT_ATTRIBUTES ObjectAttributes, __in_opt POBJECT_ATTRIBUTES ObjectAttributes,
__in_opt把手ProcessHandle, __in_opt HANDLE ProcessHandle,
__out_opt PCLIENT_ID ClientId, __out_opt PCLIENT_ID ClientId,
__in PKSTART_ROUTINE StartRoutine, __in PKSTART_ROUTINE StartRoutine,
__in_opt PVOID StartContext __in_opt PVOID StartContext
); );

参数 Parameters

ThreadHandle [输出] ThreadHandle [out]

一个变量将接收处理站。 Points to a variable that will receive the handle. 该驱动程序必须关闭手柄ZwClose一旦处理是没有用的再研究。 The driver must close the handle with ZwClose once the handle is no longer in use. 此句柄是一个内核处理Windows Vista和Windows的更高版本。 This handle is a kernel handle for Windows Vista and later versions of Windows. 在Windows的早期版本中,处理可能不是一个内核处理。 In earlier versions of Windows, the handle might not be a kernel handle.

DesiredAccess [在] DesiredAccess [in]

指定ACCESS_MASK值,表示创建的线程所要求的类型的访问。 Specifies the ACCESS_MASK value that represents the requested types of access to the created thread.

ObjectAttributes [中,可选] ObjectAttributes [in, optional]

一个结构,它指定对象的属性点。 Points to a structure that specifies the object's attributes. OBJ_PERMANENT,OBJ_EXCLUSIVE和OBJ_OPENIF不是一个线程对象的有效属性。 OBJ_PERMANENT, OBJ_EXCLUSIVE, and OBJ_OPENIF are not valid attributes for a thread object. 在Windows XP和更高版本的Windows,如果调用方没有在系统中运行的过程中,必须设置为ObjectAttributes OBJ_KERNEL_HANDLE属性。 On Windows XP and later versions of Windows, if the caller is not running in the system process context, it must set the OBJ_KERNEL_HANDLE attribute for ObjectAttributes . 98/Me的驱动程序的Microsoft Windows 2000和Windows必须调用PsCreateSystemThread只有从系统进程中。 Drivers for Microsoft Windows 2000 and Windows 98/Me must only call PsCreateSystemThread from the system process context. 对于Windows Vista和Windows的更高版本,将是一个内核处理处理。 For Windows Vista and later versions of Windows, the handle will be a kernel handle.

ProcessHandle [中,可选] ProcessHandle [in, optional]

在一个指定的地址空间的线程要运行过程公开处理。 Specifies an open handle for the process in whose address space the thread is to be run. 调用方的线程必须PROCESS_CREATE_THREAD获得这一进程。 The caller's thread must have PROCESS_CREATE_THREAD access to this process. 如果未提供此参数,该线程将被创建在最初的系统过程。 If this parameter is not supplied, the thread will be created in the initial system process. 此值应为NULL驱动程序创建线程。 This value should be NULL for a driver-created thread. 使用NtCurrentProcess,Ntddk.h界定,指明当前进程。 Use the NtCurrentProcess macro, defined in Ntddk.h , to specify the current process.

ClientId [输出,可选] ClientId [out, optional]

一个结构,它接收新线程客户端标识符点。 Points to a structure that receives the client identifier of the new thread. 此值应为NULL驱动程序创建线程。 This value should be NULL for a driver-created thread.

StartRoutine [在] StartRoutine [in]

是一个驱动程序的线程的入口点。 Is the entry point for a driver thread.

StartContext [中,可选] StartContext [in, optional]

提供一个单一的参数传递到线程时,它开始执行。 Supplies a single argument that is passed to the thread when it begins execution.

返回值 Return Value

PsCreateSystemThread返回创造了STATUS_SUCCESS如果线程。 PsCreateSystemThread returns STATUS_SUCCESS if the thread was created.

备注 Remarks

驱动程序创建设备专用线程调用这个例程,或者当他们初始化或当I / O请求来开始在这样一个司机的调度程序。 Drivers that create device-dedicated threads call this routine, either when they initialize or when I/O requests begin to come in to such a driver's Dispatch routines. 例如,一个驱动程序可能建立这样一个线程当它接收设备控制的异步请求。 For example, a driver might create such a thread when it receives an asynchronous device control request.

PsCreateSystemThread创建一个内核模式的线程开始一个单独的系统线程内执行。 PsCreateSystemThread creates a kernel-mode thread that begins a separate thread of execution within the system. 这样一个系统线程没有TEB或用户模式中,只有在内核模式下运行。 Such a system thread has no TEB or user-mode context and runs only in kernel mode.

如果输入ProcessHandle为NULL,则创建的线程是进程相关的系统。 If the input ProcessHandle is NULL, the created thread is associated with the system process. 这种线程继续运行,直到系统关闭或终止该线程通过调用PsTerminateSystemThread本身。 Such a thread continues running until either the system is shut down or the thread terminates itself by calling PsTerminateSystemThread .

在Windows XP和更高版本的Windows,驱动程序例程PsCreateSystemThread运行在一个进程的上下文参数以外,该系统的过程中必须设置属性的ObjectAttributes为OBJ_KERNEL_HANDLE。 On Windows XP and later versions of Windows, driver routines that run in a process context other than that of the system process must set the OBJ_KERNEL_HANDLE attribute for the ObjectAttributes parameter of PsCreateSystemThread . 这限制使用的模式所返回的句柄PsCreateSystemThread到内核进程运行研究。 This restricts the use of the handle returned by PsCreateSystemThread to processes running in kernel mode. 否则,线程句柄可以通过在其范围内的驱动程序正在运行的进程。 Otherwise, the thread handle can be accessed by the process in whose context the driver is running. 司机可以设置OBJ_KERNEL_HANDLE属性如下。 Drivers can set the OBJ_KERNEL_HANDLE attribute as follows.

复制 Copy
InitializeObjectAttributes(&ObjectAttributes,空,OBJ_KERNEL_HANDLE,空,空); InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);

98/Me的驱动程序用于Windows 2000和Windows必须调用PsCreateSystemThread只有从系统进程中。 Drivers for Windows 2000 and Windows 98/Me must call PsCreateSystemThread only from the system process context.

欲了解更多的有关信息StartContext参数,见ThreadStart For more information about the StartContext parameter, see ThreadStart .

需求 Requirements

版本 Version

现有的Windows在Windows 2000及更高版本。 Available in Windows 2000 and later versions of Windows.

标题 Header

Wdm.h(包括Wdm.h,Ntddk.h,或Ntifs.h) Wdm.h (include Wdm.h, Ntddk.h, or Ntifs.h)

图书馆 Library

载于Ntoskrnl.lib。 Contained in Ntoskrnl.lib.

的IRQL IRQL

PASSIVE_LEVEL PASSIVE_LEVEL
原创粉丝点击