Win32 调试接口设计与实现浅析

所谓调试器实际上是一个很宽泛的概念,凡是能够以某种形式监控其他程序执行过程的程序,都可以泛称为调试器。在Windows平台上,根据调试器的实现原理大概可以将之分为三类:内核态调试器、用户态调试器和伪代码调试器。
    内核态调试器直接工作在操作系统内核一级,在硬件与操作系统之间针对系统核心或驱动进行调试,常见的有SoftICE、WinDbg、WDEB386和i386KD等等;用户态调试器则通过操作系统提供的调试接口,在操作系统和用户态程序之间针对用户态程序进行调试,常见的有各种开发环境如VC/Delphi自带的调试器,OllyDbg等等;伪代码调试器则使用目标系统自定义的调试接口,调试由用户态程序支持的脚本语言或虚拟机代码,常见的如JVM/CLR的调试工具、VB的pcode调试器、Active Script调试器等等。
    因为伪代码调试器跟具体系统实现相关性太强,不具备原理层面的通用性,本系列文章尽量不涉及其内容,以后如果有机会可以再讨论一下JVM/CLR/Active Script提供的调试接口;用户态调试器应用最广泛,参考资料也较为完整,我会花较大精力和大家探讨;核心态调试器则跟操作系统结合较为紧密,加上我也不是太熟悉,只能尽力而为了,呵呵。欢迎大家提出批评指正意见和建议 :)
    此外强烈推荐John Robbins在MSDN的Bugslayer专栏,以及其所著的<Debugging Applications>一书(中文版《应用程序调试技术》),此书中对调试器从原理到应用都有很全面的讲解。

[1] 用户态调试器结构初探

    用户态调试器直接使用Win32 API提供的调试接口,遵循Win32的事件驱动的设计思想,其实现思路非常简单,基本框架伪代码如下:

    //启动要调试的进程或挂接调试器到已运行的进程上
    CreateProcess(..., DEBUG_PROCESS, ...) or DebugActiveProcess(dwProcessId)

    DEBUG_EVENT de;
    BOOL bContinue = TRUE;
    DWORD dwContinueStatus;

    while(bContinue)
    {
      bContinue = WaitForDebugEvent(&de, INFINITE);

      switch(de.dwDebugEventCode)
      {
      ...
      default:
        {
          dwContinueStatus = DBG_CONTINUE;
          break;
        }
      }

      ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
    }

    在调试器开始调试的时候,会启动被调试程序的新进程或者挂接(attach)到一个已运行进程上,此时Win32系统会启动调试接口的服务器端;然后调试器调用WaitForDebugEvent函数等待调试服务器端的调试事件被引发;调试器根据调试事件进行相应的处理;最后调用ContinueDebugEvent函数请求调试服务器继续执行被调试进程,以等待并处理下一个调试事件。

    首先我们大致看看调试接口的服务器端的实现思路:调试服务的服务器端接口实际上是存在于被调试进程的调试端口(Debug Port),此核心对象实现上跟Win32的完成端口类似,都是通过一个核心队列实现的LPC端口。启动调试服务器实际上就是挂接Win32的调试子系统到被调试进程,并在被调试进程内构造调试端口。调试器通过调试端口与Win32的调试子系统通讯;调试子系统响应系统操作所引发的调试事件,并通过调试端口将调试事件分发给核心态/用户态调试器。

    建立被调试程序的新进程时,需要在CreateProcess函数的dwCreationFlags参数设置DEBUG_ONLY_THIS_PROCESS或DEBUG_PROCESS标志位,以表示新建的进程需要被调试。CreateProcess函数的调用路径如下

    CreateProcessA/CreateProcessW (kernel32.dll)
    CreateProcessInternalW (kernel32.dll)
    NtCreateProcessEx (ntoskrnl.dll)
    PspCreateProcess (ntos/ps/create.c:969)

    CreateProcessInternalW函数根据传入的dwCreationFlags参数,决定是否要构造端口核心对象用于调试端口,并设置PEB的相应调试标志;PspCreateProcess会根据传入参数的调试选项和端口对象句柄,选择是否创建目标进程的调试端口;如果要创建则将传入的端口句柄转换成内核对象引用,保存在被调试程序进程的EPROCESS->DebugPort字段里。
    Win32 API提供的IsDebuggerPresent函数就是通过判断CreateProcessInternalW函数在PEB中设置的标志位来判断当前进程是否被调试的。IsDebuggerPresent函数伪代码如下:

    BOOL IsDebuggerPresent(void)
    {
      return NtCurrentTeb()->ProcessEnvironmentBlock->BeingDebugged;
    }

    TEB和PEB的结构可在http://www.ntinternals.net/上找到。

    不过此种方法很容易被调试器直接修改PEB内存结构所欺骗,故而有另外一种直接通过检查EPROCESS->DebugPort字段是否被使用,来判断此进程是否正在被调试的方法。以前水木上也有过几次讨论,如blowfish的《检测debugger的方法补遗》一文给出的代码。Windows XP/2003开始由Win32 API提供的 CheckRemoteDebuggerPresent 函数也是使用相同的思路,通过调用  NtQueryInformationProcess  函数查询调试端口实现的,伪代码如下:

    BOOL CheckRemoteDebuggerPresent(HANDLE hProcess, PBOOL pbDebuggerPresent)
    {
      enum PROCESS_INFO_CLASS { ProcessDebugPort = 7 };

      if(hProcess && pbDebuggerPresent)
      {
        HANDLE hPort;

        *pbDebuggerPresent = NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessDebugPort, &hPort, sizeof(hPort), NULL)) ? TRUE : FALSE;

        return *pbDebuggerPresent;
      }
      return FALSE;
    }

    与直接创建被调试程序的新进程不同,调试已启动进程的 DebugActiveProcess 函数首先连接到Win32 系统调试服务器的端口上,然后激活当前正在运行的被调试进程的调试端口。DebugActiveProcess的伪代码如下:

    BOOL DebugActiveProcess(DWORD dwProcessId)
    {
      if(DbgUiConnectToDbg())
      {
        HANDLE hProcess = ProcessIdToHandle(dwProcessId);

        if(hProcess)
        {
          DbgUiDebugActiveProcess(hProcess);
          NtClose(hProcess);
        }
      }
      return FALSE;
    }

    DbgUiConnectToDbg函数(ntos/dll/dlluistb.c:27)尝试连接核心提供的调试子系统端口(名为"/DbgUiApiPort"),如果成功连接会获得一个端口对象(保存在DbgUiApiPort NtCurrentTeb()->DbgSsReserved[1]),和一个调试状态转换的信号灯句柄(保存在DbgStateChangeSemaphore NtCurrentTeb()->DbgSsReserved[0])用于等待调试事件。伪代码如下:

    #define DbgStateChangeSemaphore (NtCurrentTeb()->DbgSsReserved[0])
    #define DbgUiApiPort (NtCurrentTeb()->DbgSsReserved[1])

    NTSTATUS DbgUiConnectToDbg( VOID )
    {
      NTSTATUS st = NtConnectPort(&DbgUiApiPort, L"//DbgUiApiPort", ..., &DbgStateChangeSemaphore);

      if(NT_SUCCESS(st))
      {
        NtRegisterThreadTerminatePort(DbgUiApiPort);
      }
      else
      {
        DbgUiApiPort = NULL;
      }
      return st;
    }

    如果连接调试子系统成功,则调用NtRegisterThreadTerminatePort函数(ntos/ps/psdelete.c:1202)将调试端口加入到当前线程控制块的终止端口列表(ETHREAD->TerminationPortList)中。在线程结束的之前,会激活此列表中的端口,给调试器一个清理的机会。

    DbgUiDebugActiveProcess函数完成具体的激活被调试进程的调试服务器的功能。伪代码如下:

    #define DbgUiApiPort (NtCurrentTeb()->DbgSsReserved[1])

    void DbgUiDebugActiveProcess(HANDLE hProcess)
    {
      return NtDebugActiveProcess(DbgUiApiPort) &&
             DbgUiIssueRemoteBreakin(hProcess) &&
             DbgUiStopDebugging(hProcess);
    }

    至于这几个函数的具体实现,等后面章节详细分析Win32调试子系统时再详细讲解,呵呵

    在被调试进程启动了调试支持后,调试器调用WaitForDebugEvent函数等待调试事件的发生。此函数实际上是对DbgUiWaitStateChange函数(ntos/dll/dlluistb.c:93)的一个简单包装,通过等待DbgUiConnectToDbg函数获得的调试事件信号灯来完成实际功能。如果成功获得调试事件,还会通过NtRequestWaitReplyPort函数(ntos/lpc/lpcsend.c:717)向调试服务器通报DbgUiWaitStateChangeApi消息。

    在处理完调试事件后,调试器调用的ContinueDebugEvent函数是DbgUiContinue函数的一个简单包装,也是使用NtRequestWaitReplyPort函数向调试服务器通报DbgUiContinueApi消息。

    在完成调试功能后,WinXP/2003还提供了DebugActiveProcessStop函数停止调试。伪代码如下:

    BOOL DebugActiveProcessStop(DWORD dwProcessId)
    {
      HANDLE hProcess = ProcessIdToHandle(dwProcessId);

      if(hProcess)
      {
        CloseAllProcessHandles(dwProcessId);
        DbgUiStopDebugging(hProcess);
        if(NtClose(hProcess))
          return TRUE;
      }
      return FALSE;
    }

    DbgUiStopDebugging函数(ntdll.dll)调用ZwRemoveProcessDebug函数(ntoskrnl.exe)关闭指定进程的调试端口,实现上是传入端口句柄和进程句柄,调用0xC7号系统服务完成最终功能。这个暂时就不深入讨论了,就此打住 :P

    在了解这些后,对用户态调试器的实现应该就有了一个框架性的了解:其结构就是一个基于事件的模型,然后通过向调试子系统请求调试事件并完成具体操作。

[2] 调试事件

    前面说到 Win32 下的用户态调试器实际上就是一个while循环,循环体内先等待一个调试事件,然后处理之,最后将控制权交还给调试服务器,就好像一个窗口消息循环一样。调试事件的核心实际上就是一个DEBUG_EVENT结构,在WinBase.h文件中定义如下:

    typedef struct _DEBUG_EVENT {
        DWORD dwDebugEventCode;
        DWORD dwProcessId;
        DWORD dwThreadId;
        union {
            EXCEPTION_DEBUG_INFO Exception;
            CREATE_THREAD_DEBUG_INFO CreateThread;
            CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
            EXIT_THREAD_DEBUG_INFO ExitThread;
            EXIT_PROCESS_DEBUG_INFO ExitProcess;
            LOAD_DLL_DEBUG_INFO LoadDll;
            UNLOAD_DLL_DEBUG_INFO UnloadDll;
            OUTPUT_DEBUG_STRING_INFO DebugString;
            RIP_INFO RipInfo;
        } u;
    } DEBUG_EVENT, *LPDEBUG_EVENT;

    dwDebugEventCode字段给出此调试事件的类型,dwProcessId和dwThreadId字段分别给出调试事件发生的进程和线程ID号。

    调试事件一般有以下几类:

    #define EXCEPTION_DEBUG_EVENT       1
    #define CREATE_THREAD_DEBUG_EVENT   2
    #define CREATE_PROCESS_DEBUG_EVENT  3
    #define EXIT_THREAD_DEBUG_EVENT     4
    #define EXIT_PROCESS_DEBUG_EVENT    5
    #define LOAD_DLL_DEBUG_EVENT        6
    #define UNLOAD_DLL_DEBUG_EVENT      7
    #define OUTPUT_DEBUG_STRING_EVENT   8
    #define RIP_EVENT                   9

    CREATE_PROCESS_DEBUG_EVENT事件在创建一个新的进程的第一个线程时被引发;相应的EXIT_PROCESS_DEBUG_EVENT事件在被调试的进程结束最后一个线程运行时被引发;每次新建/退出一个线程时会有CREATE_THREAD_DEBUG_EVENT/EXIT_THREAD_DEBUG_EVENT事件被引发;每次载入/卸载一个DLL时会有LOAD_DLL_DEBUG_EVENT/UNLOAD_DLL_DEBUG_EVENT事件被引发;被调试程序使用OutputDebugString函数输出一个调试字符串时调试器会接受到一个OUTPUT_DEBUG_STRING_EVENT事件;异常被引发时调试器会接受到一个第一时间的EXCEPTION_DEBUG_EVENT事件,如果调试器不处理此异常,则进入被调试程序的正常SEH调用链,如果被调试进程也不处理,则会再次引发此事件;RIP_EVENT则一般用于报告错误事件。
    一般来说程序的调试事件按照如下顺序被引发:

    CREATE_PROCESS_DEBUG_EVENT

    LOAD_DLL_DEBUG_EVENT x n                            // 静态载入的DLL

    CREATE_THREAD_DEBUG_EVENT & EXIT_THREAD_DEBUG_EVENT // 多线程程序中成对出现

    LOAD_DLL_DEBUG_EVENT & UNLOAD_DLL_DEBUG_EVENT       // 动态载入 DLL 时成对出现

    EXCEPTION_DEBUG_EVENT x n                           // 随机出现

    OUTPUT_DEBUG_STRING_EVENT x n                       // 程序写调试信息时出现

    EXIT_PROCESS_DEBUG_EVENT

    接下来我们详细分析每种调试事件被引发的原因和时机。具体的调试事件内容这里就不罗嗦了,有兴趣写调试器的朋友可以参考MSDN和<Debugging Applications>中相关内容。

    首先是建立进程的CREATE_PROCESS_DEBUG_EVENT事件和建立线程的CREATE_THREAD_DEBUG_EVENT事件。这两个事件都是由DbgkCreateThread函数(ntos/dbgk/dbgkproc.h:211)引发的。此函数首先检查当前线程是否是具有调试端口的活动线程;然后检查当前线程是否是进程的创建的第一个线程;如果不是第一个线程,或者调试器是挂接(attach)到一个活动进程上(判断依据是此进程是否占用过用户态的CPU时间),则向调试子系统的调试服务器引发CREATE_THREAD_DEBUG_EVENT事件;否则转而报告CREATE_PROCESS_DEBUG_EVENT事件。

    DbgkCreateThread函数伪代码如下:

    VOID DbgkCreateThread(PVOID StartAddress)
    {
      if(!PsGetCurrentProcess()->DebugPort || PsGetCurrentThread()->DeadThread)
      {
        return;
      }

      PsLockProcess(Process,KernelMode,PsLockWaitForever); // 锁定进程中所有线程

      if(PsGetCurrentProcess()->Pcb.UserTime &&
         PsGetCurrentProcess()->CreateProcessReported == FALSE)
      {
        PsGetCurrentProcess()->CreateProcessReported = TRUE;

        // 引发 CREATE_PROCESS_DEBUG_EVENT 事件
      }
      else
      {
        // 引发 CREATE_THREAD_DEBUG_EVENT 事件
      }

      PsUnlockProcess(PsGetCurrentProcess());
    }

    Win32在创建用户态线程的时候,大致流程如下:

    CreateThread (kernel32.dll)
    CreateRemoteThread (kernel32.dll)
    NtCreateThread (ntoskrnl.exe)
    PspCreateThread (ntos/ps/create.c:237)

    PspCreateThread函数在创建用户态线程时,使用PspUserThreadStartup函数(ntos/ps/create.c:1639)作为线程入口函数,因此线程被创建后直接进入此函数。PspUserThreadStartup函数对非僵死线程和没有结束的线程初始化其APC;然后调用DbgkCreateThread函数通知调试器采取相应动作;最后将进程的用户态CPU时间设置为1,以标示此进程已启动。对一种特殊线程,非僵死线程但线程启动时已经停止,则直接调用DbgkCreateThread然后立刻调用PspExitThread,以通知调试器采取相应动作。PspUserThreadStartup函数伪代码如下:

    VOID PspUserThreadStartup(IN PKSTART_ROUTINE StartRoutine, IN PVOID StartContext)
    {
      if(!PsGetCurrentThread()->DeadThread && !PsGetCurrentThread()->HasTerminated)
      {
        // 初始化线程 APC
      }
      else
      {
        if(!PsGetCurrentThread()->DeadThread)
        {
          DbgkCreateThread(StartContext);
        }
        PspExitThread(STATUS_THREAD_IS_TERMINATING);
      }

      DbgkCreateThread(StartContext);

      if(PsGetCurrentProcess()->Pcb.UserTime == 0)
      {
        PsGetCurrentProcess()->Pcb.UserTime = 1;
      }
    }

    与DbgkCreateThread函数对应的是DbgkExitThread函数(ntos/dbgk/dbgkproc.c:384)和DbgkExitProcess函数(ntos/dbgk/dbgkproc.c:439),分别向调试服务器引发EXIT_THREAD_DEBUG_EVENT和EXIT_PROCESS_DEBUG_EVENT事件。
    这两个函数由系统内核退出线程的PspExitThread函数(ntos/ps/psdelete.c:622)在合适的时候调用。PspExitThread函数检测当前进程PCB的线程列表是否只有当前线程一个线程,如果没有其他线程则调用DbgkExitProcess函数,否则调用DbgkExitThread函数。

    Win32 系统中载入和卸载DLL,实际的函数调用流程如下:

    LoadLibrary (kernel32.dll)
    LoadLibraryEx (kernel32.dll)
    BasepLoadLibraryAsDataFile (kernel32.dll)
    NtMapViewOfSection (ntos/mm/mapview.c:204)
    MmMapViewOfSection (ntos/mm/mapview.c:699)

    NtMapViewOfSection函数在调用MmMapViewOfSection函数(ntos/mm/mapview.c:699)完成实际的内存文件映射之后,会根据映射节的标记位以及目标进程是否是当前进程,判断是否要调用DbgkMapViewOfSection函数(ntos/dbgk/dbgkproc.c:495),通知调试服务器有新的映象文件被加载。与之对应MmUnmapViewOfSection函数(ntos/mm/umapview.c:88)也在判断标志位和目标进程是否是当前进程后,在函数末尾调用DbgkUnMapViewOfSection函数(ntos/dbgk/dbgkproc.c:567)通知调试服务器有映象文件被卸载。

    与前面的几种事件不同,OutputDebugString函数(kernel32.dll)实际上是通过异常实现的。而且有趣的是,这个函数是为数不多的W后缀Unicode版本实现上转而调用A后缀Ansi版本,完成实际功能的例子。OutputDebugStringA函数(kernel32.dll)实际上使用RaiseException函数引发了一个异常号为0x40010006的软件异常,并将字符串的指针和长度作为异常参数传递。

    DbgkForwardException函数(ntos/dbgk/dbgkport.c:96)作为实际引发EXCEPTION_DEBUG_EVENT调试事件的函数,在系统的异常分发KiDispatchException函数(ntos/ke/i386/exceptn.c:797)中被调用。KiDispatchException函数根据异常被引发时的状态,分别完成核心和用户态的异常处理工作。

    对核心态异常,首先给核心调试程序一个处理机会,然后试图分发到基于帧的SEH异常链去,没有被处理的话则再给核心调试程序一个机会,如果还是没被处理,就只能调用KeBugCheckEx函数(ntos/ke/bugcheck.c:157)蓝屏了,呵呵。
    对用户态异常,还是首先试图让核心调试器处理,如果不行才调用DbgkForwardException函数分发,没有被处理的话则多次尝试,如果还是没被处理,就停止线程并报告异常给用户。KiDispatchException函数伪代码如下:

    VOID KiDispatchException (IN PEXCEPTION_RECORD ExceptionRecord, IN PKEXCEPTION_FRAME ExceptionFrame,
                              IN PKTRAP_FRAME TrapFrame, IN KPROCESSOR_MODE PreviousMode, IN BOOLEAN FirstChance)
    {
      CONTEXT ContextFrame;

      KeContextFromKframes(TrapFrame, ExceptionFrame, &ContextFrame); // 从核心异常帧(Frame)构造异常上下文(Context)

      if (ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) // 处理调试断点 int 3
      {
        ContextFrame.Eip--;
      }

      if (PreviousMode == KernelMode)
      {
        if (FirstChance == TRUE)
        {
          if (KiDebugRoutine && KiDebugRoutine(..., FALSE) != FALSE) goto Handle1

          if(RtlDispatchException(ExceptionRecord, &ContextFrame) == TRUE) goto Handled1;
        }

        if (KiDebugRoutine && KiDebugRoutine(..., TRUE) != FALSE) goto Handle1

        KeBugCheckEx(...); // 核心错误,以可控方式崩溃 -_-b 说白了就是Deadth Blue Screen,呵呵
      }
      else // PreviousMode = UserMode
      {
        if (FirstChance == TRUE)
        {
          if (KiDebugRoutine && KiDebugRoutine(..., FALSE) != FALSE) goto Handle1

          if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) goto Handled2;

          // 将异常信息转换到用户模式,并尝试分发
        }

        if (DbgkForwardException(ExceptionRecord, TRUE, TRUE))
        {
            goto Handled2;
        }
        else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE))
        {
            goto Handled2;
        }
        else
        {
            ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode);
            KeBugCheckEx(...);
        }
      }

    Handled1:
      KeContextToKframes(TrapFrame, ExceptionFrame, &ContextFrame,
                         ContextFrame.ContextFlags, PreviousMode);

    Handled2:
    }

    DbgkForwardException函数分别针对DebugException和SecondChance参数的三种组合被调用。DebugException为True时向调试端口发送信息,否则向异常端口发送。

    至此,我们对几种常见的调试事件的引发机制就大概有了一个了解,下一节将介绍将这些调试事件和最终用户态调试器关联起来的Win32中调试子系统的实现思路。
作为Microsoft 32位平台的应用程序编程接口Win32 API是从事Windows应用程序开发所必备的。 首先对Win32 API函数做完整的概述;然后收录五大类函数: 窗口管理、图形设备接口、系统服务、国际特性以及网络服务; 在附录部分,讲解如何在Visual Basic和Delphi中对其调用。 本书是从事Windows应用程序开发的软件工程师的必备参考手册。 控件与消息函数 共91个函数 硬件与系统函数 共98个函数 设备场景函数 共73个函数 绘图函数 共105个函数 位图、图标和光栅运算函数 共39个函数 菜单函数 共37个函数 文本和字体函数 共41个函数 打印函数 共66个函数 文件处理函数 共118个函数 进程和线程函数 共40个函数 Windows消息函数 共11个函数 网络函数 共14个函数 目 录 第一章 Win32 API概论…………………………………………………………………………1 1.1 为什么使用Win32 API …………………………………………………………………1 1.2 Win32 API简介 …………………………………………………………………………1 1.3 综述………………………………………………………………………………………11 第二章 窗口管理函数(Windows Control Function) ……………………………………13 2.1 易用特性函数(Accessibility Features)…………………………………………13 2.2 按钮函数(Button)……………………………………………………………………20 2.3 插入标记(^)函数(Caret)…………………………………………………………21 2.4 组合框函数(Combo box) ……………………………………………………………24 2.5 通用对话框函数(Common Dialog Box) ……………………………………………25 2.6 标函数(Cursor)………………………………………………………………………36 2.7 对话框函数(Dialog Box)……………………………………………………………40 2.8 编辑控制函数(Edit Control)………………………………………………………54 2.9 图标函数(Icon)………………………………………………………………………54 2.10 键盘加速器函数(Keyboard Accelerator)……………………………………… 61 2.11 键盘输入函数(Keyboard InPut) …………………………………………………63 2.12 列表框函数(List box) ……………………………………………………………75 2.13 菜单函数(Menu) ……………………………………………………………………76 2.14 消息和消息队列函数(Message and Message Queue)……………………………90 2.15 鼠标输入函数(Mouse Input) ……………………………………………………100 2.16 多文档接口函数(Multiple Document Interface) ……………………………103 2.17 资源函数(Resource)………………………………………………………………105 2.18 滚动条函数(Scroll Bar)…………………………………………………………113 2.19 窗口函数(Window)…………………………………………………………………119 2.20 窗口类函数(Window Class)………………………………………………………144 2.21 窗口过程函数(Window Procedure)………………………………………………150 2.22 窗口属性函数(Window Property) ………………………………………………152 第三章 图形设备接口函数(Graphic Device Interface Function) …………………155 3.1 位图函数(Bitmap) …………………………………………………………………155 3.2 笔刷函数(Brush)……………………………………………………………………171 3.3 剪切函数(Clipping) ………………………………………………………………176 3.4 颜色函数(Color)……………………………………………………………………179 3.5 坐标空间与变换函数(Coordinate Space Transformation)……………………186 3.6 设备环境函数(Device Context) …………………………………………………195 3.7 填充形态函数(Filled shape) ……………………………………………………211 3.8 字体和正文函数(Font and Text)…………………………………………………215 3.9 ICM 2.0函数 …………………………………………………………………………238 3.10 线段和曲线函数(Line and Curve)………………………………………………295 3.11 图元文件函数(Metafile)…………………………………………………………300 3.12 多显示器函数(Multiple Display Monitors) …………………………………311 3.13 绘图函数和画图函数(Painting and Drawing)…………………………………313 3.14 路径函数(Path)……………………………………………………………………328 3.15 画笔函数(Pen) ……………………………………………………………………332 3.16 打印及打印假脱机程序函数(Printing and Print Spooler)…………………334 3.17 矩形函数(Rectangle) ……………………………………………………………371 3.18 区域函数(Region)…………………………………………………………………374 第四章 系统服务函数(System Service Function) ……………………………………383 4.1 访问控制函数(Access Control) …………………………………………………383 4.2 原子函数(Atom) ……………………………………………………………………406 4.3 客户/服务器访问控制函数(Client/Server Access Control) ………………409 4.4 剪贴板函数(Clipboard)……………………………………………………………431 4.5 通信函数(Communication)…………………………………………………………436 4.6 控制台函数(Console)………………………………………………………………444 4.7 数据解压库函数(Data Decompression Library) ………………………………463 4.8 调试函数(Debugging)………………………………………………………………466 4.9 设备输入输出函数(Device Input and Output)…………………………………472 4.10 动态数据交换函数(Dynamic Data Exchange) …………………………………474 4.11 动态数据交换管理函数(Dynamic Data Exchange Management)………………476 4.12 动态链接库函数(Dynamic-Link Library)………………………………………489 4.13 错误函数(Error) …………………………………………………………………496 4.14 事件日志函数(Event Logging) …………………………………………………499 4.15 文件函数(File)……………………………………………………………………503 4.16 文件安装库函数(File Installation Library) ………………………………542 4.17 文件映射函数(File Mapping)……………………………………………………546 4.18 文件系统函数 File System)………………………………………………………551 4.19 句柄和对象函数(Handle and Object)………………………………………………556 4.20 挂钩函数(Hook)………………………………………………………………………560 4.21 ImageHlp函数…………………………………………………………………………572 4.22 大整数操作函数(Iarge Integer Operations)……………………………………594 4.23 低层访问控制函数(Low-Level Access Control)………………………………596 4.24 LSAPI函数 …………………………………………………………………………617 4.25 邮槽函数(Mailslot)………………………………………………………………622 4.26 内存管理函数(Memory Management) ……………………………………………623 4.27 管道函数(Pipe) …………………………………………………………………655 4.28 电源管理函数(Power Management) …………………………………………… 663 4.29 进程和线程函数(Process and Thread)…………………………………………666 4.30 注册表函数(Registry)……………………………………………………………700 4.31 字符串操作函数(String Manipulation)……………………………………… 724 4.32 结构化异常处理函数(Structured Exception Handling) ……………………742 4.33 同步函数(Synchronization) ……………………………………………………745 4.34 系统信息函数(System Information)……………………………………………766 4.35 系统消息函数(System Message)…………………………………………………780 4.36 系统关机函数(System Shutdown) ………………………………………………781 4.37 磁带备份函数(Tape Backup) ……………………………………………………783 4.38 时间函数(Time)……………………………………………………………………789 4.39 计时器函数(Timer) ………………………………………………………………795 4.40 工具帮助函数(Tool Help) ………………………………………………………796 4.41 窗口站和桌面函数(Window Station and Desktop)……………………………799 4.42 Windows NT 4.0访问控制函数(Window NT 4.0 Access-Control)……………808 4.43 WinTrust函数(WinTrust)…………………………………………………………814 第五章 国际特性函数(International Peatures Punction)时性…………………………815 5.1 输入方法编辑函数(Input Method Editor)…………………………………………815 5.2 国家语言支持函数(National Language Support)………………………………… 828 5.3 Unicode和字符集函数(Unicode and Character Set)……………………………… 843 第六章 网络服务函数(Networding Service Function)……………………………………849 6.1 数据链路控制函数(DLC)………………………………………………………………849 6.2 网络函数(Net)…………………………………………………………………………849 6.3 NetBIOS函数……………………………………………………………………………896 6.4 网络DDE函数(Networking DDE)……………………………………………………897 6.5 RAS服务器管理函数(RAS Server Administration)………………………………901 6.6 远程访问服务函数(Remote Access Administration)………………………………910 6.7 服务函数(Service)……………………………………………………………………929 6.8 Windows网络函数(Windows Networking)……………………………………………930 附录1 如何在VB中调用DLL API ……………………………………………………………945 1 DLL API的声明……………………………………………………………………………945 2 DLL API的调用……………………………………………………………………………947 附录2 在Delphi中直接调用Windows API…………………………………………………953
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值