Windows服务(Services),是一些运行在WindowsNT、Windows2000和Windows XP等操作系统下用户环境以外的程序。它不同于一般的可执行程序,不需要系统登录便可以运行,以完成某些特定的功能。服务提供了管理能力,可以将后台程序转换成服务,然后就可以用命令或者在系统启动用户登录之前启动,并且也可以暂停、恢复和终止。服务信息在注册表中维护。

为了能够在系统中正确运行,在创建一个服务时必须接受一些特殊的规则,最重要的一点是:必须在目标系统中安装并且注册该Service。此外,基于用户界面的Service是没有多大意义的,当然Service可以有用户界面,不过由于每一个Service都是在自己的Windows Station中创建的,所以用户界面相关的API调用无法进行。

一个Windows服务,至少应该包括两个程序:一个是服务本体程序,一个是服务控制程序。服务本体程序一般是一个DOS控制台程序,而控制程序则是一个普通的Win32应用程序,用于对服务进行启动和停止等操作。

Windows服务在服务控制管理器(SCM)的控制下运行,把一个控制台程序转换成一个Windows服务,需要三个主要的步骤来将程序置于SCM控制下:

1)创建一个新的main()入口点以在SCM中注册服务,它提供逻辑服务入口点和名称;

2)转换旧的main()入口点函数为ServiceMain(),它注册服务控制处理器并向SCM通知其状态;

3)编写服务控制处理器函数以响应SCM命令。

服务控制管理器(Service Control Manager)

SCM的可执行文件镜像是“/Winnt/System32/Services.exe”,Winlogon进程早在系统引导之前就启动它了。SCM运行后,扫描以下注册表键下的内容:

HKEY_LOCAL_MACHINE/System/CurrentControlSet/Services

为它遇到的每个子键在服务数据库中创建一个入口。服务入口包含所有服务相关的参数,也包含了追踪服务状态的域。如果服务或者驱动标识为自动启动,SCM将启动它们,并侦测启动过程中的错误。I/O管理器将在任何用户模式进程执行前加载标识为引导期间启动和系统启动期间启动的驱动程序。

HKEY_LOCAL_MACHINE/System/CurrentControlSet/Services下面有很多子键,一个子键名表示一个服务的内部名称,其下的键值项对应所有与此服务相关的参数。

安装服务所需的最低数量的参数如下:

DisplayName---用户接口程序使用的服务名称。如果没有指定名称,服务注册表键名将作为它的名称。

ErrorControl---如果SCM启动服务时驱动出错,这个值指定SCM的行为。取值如下:

1)SERVICE_ERROR_IGNORE(0)—I/O管理器忽略驱动返回的错误,但是仍然继续启动操作,不做任何记录;

2)SERVICE_ERROR_NORMAL(1)---如果驱动加载或者初始化失败,系统将给用户显示一个警告框,并将错误记录到系统日志中。

ImagePath---指定驱动镜像文件的完整路径。

Start---指定何时启动服务,常用取值如下:

1)SERVICE_BOOT_START(0)---在系统引导期间加载;

2)SERVICE_AUTO_START(1)---在系统启动期间启动

3)SERVICE_DEMAND_START(2)---SCM根据用户的要求显式加载

Type---指定服务类型。若为内核驱动,则设为SERVICE_KERNEL_DRIVER(1)。

服务本体程序设计:

在设计服务程序时必须满足特定函数调用的流程。首先调用main(),然后调用StartServiceCtrlDispatcher()把向ServiceMain()的指针传递给SCM,SCM可以通过该指针启动服务,ServiceMain()产生服务状态句柄并注册Handler()。所以服务主体程序一般由main(),ServiceMain()和Handler()3部分组成。

1、服务控制函数Handler()

服务控制函数Handler()中包含一个switch语句,它用于分发由SCM发送的5个控制通知事件即:SERVICE_CONTROL_STOP, SERVICE_CONTROL_PAUSE, SERVICE_CONTROL_CONTINUE, SERVICE_CONTROL_INTERROGATE, SERVICE_CONTROL_SHUTDOWN,用户分别对这些通知事件进行相应处理,然后将处理后的新服务的最新状态消息发送给SCM:

//分发从SCM获得的控制通知事件,并对控制通知事件进行处理

void WINAPI Handler(DWORD Opcod)

{

switch(Opcod)

{

case SERVICE_CONTROL_STOP: //处理停止服务事件

//Usercode();       //用户加入自己的代码

ss.dwWin32ExitCode = 0;     //设置服务的出错代码

ss.dwCurrentState = SERVICE_STOPPED;

ss.dwCheckPoint = 0;

ss.dwWaitHint = 0;

SetServiceStatus(ssh, &ss); //必须随时更新数据库中Service的状态

break;

case SERVICE_CONTROL_INTERROGATE:

SetServiceStatus(ssh, &ss); //更新数据库中的Service状态

break;

......

}

}

2、ServiceMain()

ServiceMain()是Service的真正入口点,必须在main()中进行正确的定义。它的作用是:

1)创建状态句柄serviceStatusHandle和服务控制函数Handler;

2)向SCM发送服务启动状态信息并创建服务所在的线程;

3)创建终止事件句柄以控制服务的停止。

ServiceMain()的两个参数是由StartService()传递过来的,在ServiceMain()中应该立即调用RegisterServiceCtrlHandler()注册一个Handler去处理控制程序或者控制面板对Service的控制要求:

void WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)

{

//调用RegisterServiceCtrlHandler()获得服务状态句柄并注册生成Handler

ssh = RegisterServiceCtrlHandler(SERVICE_NAME, Handler);

//提供单个服务还是多个服务

ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS;

//等用户程序完成后在当前运行状态设为SERVICE_RUNNING

ss.dwCurrentState = SERVICE_START_PENDING;

//目前能接受的是停止命令

ss.dwControlAccepted = SERVICE_ACCEPT_STOP;

//服务的出错代码

ss.dwWin32ExitCode = NO_ERROR;

//置服务在启动/关闭/运行操作中反映操作进度

ss.dwCheckPoint = 0;

//置服务在启动/关闭/运行操作时将持续的时间

ss.dwWaitHint = 0;

//必须随时更新数据库中的Service的状态信息

SetServiceStatus(ssh, &ss);

//Usercode() here

ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;

ss.dwCurrentState = SERVICE_RUNNING;

ss.dwControlsAccepted = SERVICE_ACCEPT_STOP;

ss.dwWin32ExitCode = NO_ERROR;

ss.dwCheckPoint = 0;

ss.dwWaitHint = 0;

SetServiceStatus(ssh, &ss);

//UserCode() here

}

3、main()

Main()是服务的主线程,先填充服务线程入口表SERVICE_TABLE_ENTRY,当SCM开始一个服务时,它总是等待这个服务去调用StartServiceCtrlDispatcher()函数。Main()作为这个进程的主线程应该在程序开始后尽快调用StartServiceCtrlDispatcher(),该函数被调用后并不立即返回,它把本Service的主线程连接到SCM,从而让SCM通过这个连接发送开始、停止、暂停和继续等控制命令给主线程。StartServiceCtrlDispatcher()在整个Service结束时才返回。

下面是主线程的部分实现代码:

#include <windows.h>

SERVICE_STATUS_HANDLE ssh;   //定义于SCM通讯的服务状态句柄²

char *SERVICE_NAME = "ACEService";      //定义Service线程的名字

SERVICE_STATUS ss; //定义服务状态标志

int main(int argc, char *argv[])

{

SERVICE_TABLE_ENTRY ste[2];    //填充SERVICE_TABLE_ENTRY结构

ste[0].lpServiceName = SERVICE_NAME;  //线程的名字

ste[0].lpServiceProc = ServiceMain;      //线程入口地址

ste[1].lpServiceName = NULL;       //最后必须是NULL

ste[1].lpServiceProc = NULL;

StartServiceCtrlDispatcher(ste); //将新的Service的ServiceMain的指针

//传递给SCM,实现新的Service在SCM中的注册

return 0;

}

新的main()函数由SCM来调用,负责用SCM来注册服务,以及启动服务控制调度程序。这需要使用一个或者多个逻辑服务的名称和入口点来调用函数StartServiceCtrlDispatcher:

BOOL StartServiceCtrlDispatcher(

LPSERVICE_TABLE_ENTRY lpServiceStartTable);

参数lpServiceStartTable是SERVICE_TABLE_ENTRY条目数组的地址,而每个条目时逻辑服务名称和入口点,数组的结尾由一对NULL条目来指示。

如果注册成功则返回TRUE,如果服务已经在运行或者更新注册表(HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services)时有问题,就会出现错误。调用StartServiceCtrlDispatcher服务进程的主线程把线程连接到SCM。而SCM用调用线程作为服务控制调度线程来注册服务。SCM不会返回到调用线程,直到所有的服务都终止了。

另一种类型的带有单个逻辑服务的服务主程序如下:

#include <...>

void WINAPI ServiceMain(DWORD argc, LPTSTR argv[]);

static LPTSTR ServiceName = _T("AceCommandLineService");

VOID _tmain(int argc, LPTSTR argv[])

{

SERVICE_TABLE_ENTRY DispatchTable[] =

{

{ServiceName, ServiceMain},

{NULL, NULL}

};

if(!StartServiceCtrlDispatcher(DispatchTable))

//ServiceMain() will not run until started by the SCM

//Return here only when all services have terminated.

ReportError(_T("Failed"), 1, TRUE);

return;

}

SCM调用的服务控制处理器必须能够控制相关的逻辑服务,每个逻辑服务必须使用RegisterServiceCtrlHandlerEx函数立即注册处理器。函数的调用应该在ServiceMain()的开头处,而且不需再次调用。SCM在接收到服务控制请求后调用处理器:

SERVICE_STATUS_HANDLE RegisterServiceCtrlHandleEx(

LPCTSTR lpServiceName,

LPHADNLE_FUNCTION_EX lpHandlerProc,

LPVOID lpContext);

参数lpServiceName—是用户在服务表条目中提供的逻辑服务名称;

lpHandlerProc—是扩展处理器函数的地址;

lpContext—是传递给控制处理器的用户自定义数据,允许单个控制处理器使用相同的处理器来区分多个服务。

现在处理器已被注册,接着使用SetServiceStatus来把服务状态设置成SERVICE_START_PENDING。服务控制处理器每个调用时必须设置状态,即使状态没有改变:

BOOL SetServiceStatus(

SERVICE_STATUS_HANDKE hServiceStatus,

LPSERVICE_STATUS lpServiceStatus);

参数hServiceStatus—是由RegisterServiceCtrlHandlerEx返回的SERVICE_STATUS_HANDLE。因此,RegisterServiceCtrlHandlerEx调用必须在SetServiceStatus调用之前。

lpServiceStatus—指向SERVICE_STATUS结构,描述了服务属性、状态和特性。

SERVICE_STATUS结构定义如下:

typedef struct _SERVICE_STATUS

{

DWORD dwServiceType;       //服务类型,可能是SERVICE_KERNEL_DRIVER等

DWORD dwCurrentState;       //服务当前状态

DWORD dwControlAccepted;      //服务能够接收的控制代码

DWORD dwWin32ExitCode;       //出错代码,当服务启动或停止时它使用该参数报告一个错误

DWORD dwServiceSpecificExitCode;   //当错误发生时,服务返回的服务相关的错误代码

DWORD dwCheckPoint; //当服务启动时,每完成一步它就增加这个值

DWORD dwWaitHint;    //未决的启动、停止等操作花费的时间

}SERVICE_STATUS, *LPSERVICE_STATUS;

参数dwWin32ExitCode是逻辑服务的线程退出码。而服务在运行或者正常终止时必须将其设为NO_ERROR;

当服务启动或者停止时,dwServiceSpecificExitCode可以用来指示错误,但是该值会被忽略;除非dwWin32ExitCode被设置成ERROR_SERVICE_SPECIFIC_ERROR;

dwCheckPoint应该由服务周期性地增加,以报告含初始化和关闭的所有步骤的过程。如果服务没有启动、停止、暂停或者继续挂起,则该值是非法的,为0;

dwWaitHint是用dwCheckPoint变量的增加值或者dwCurrentState的改变值分别调用SetServiceStatus之间所花费的时间毫秒值;

服务类型dwServiceType取值如下:

SERVICE_WIN32_OWN_PROCESS    表示Windows服务利用自己的资源在自己的进程中运行

SERVICE_WIN32_SHARE_PROCESS       表示Windows服务于其他服务共享进程以便多个服务可以共享资源、环境变量等

SERVICE_KERNEL_DRIVER           表示Windows设备驱动器

SERVICE_FILE_SYSTEM_DRIVER 指定Windows文件系统驱动器

SERVICE_INTERACTIVE_PROCESS      表示某Windows服务进程可以通过桌面与用户交互

参数dwCurrentState表示当前服务的状态:

SERVICE_STOPPED        服务没有运行

SERVICE_START_PENDING 服务正在启动中,但是还没有准备好响应请求

SERVICE_STOP_PENDING   服务正在停止中,但是还没有完成关闭

SERVICE_RUNNING     服务在运行

SERVICE_CONTINUE_PENDING        在服务处于暂停状态后服务继续处于挂起状态

SERVICE_PAUSE_PENDING          服务暂停处于挂起状态,但是服务没有完全处于暂停状态

SERVICE_PAUSED 服务被暂停

参数dwControlsAccept指定了服务接受并由服务控制处理器处理的控制代码:

SERVICE_ACCEPT_STOP      启用SERVICE_CONTROL_STOP

SERVICE_ACCEPT_PAUSE_CONTINUE           启用SERVICE_CONTROL_PAUSE和SERVICE_CONTROL_CONTINUE

SERVICE_ACCEPT_SHUTDOWN                当发生系统关闭时通知服务,这使得系统可以向服务发送SERVICE_CONTROL_SHUTDOWN值

SERVICE_ACCEPT_PARAMCHANGE     该启动参数可以无需重启就可改变,通知是SERVICE_CONTROL_PARAMCHANGE

服务控制处理器,即RegisterServiceCtrlHandlerEx中指定的回调函数具有如下形式:

DWORD WINAPI HandlerEx(

DWORD dwControl,

DWORD dwEventType,

LPVOID lpEventData,

LPVOID lpContext);

参数dwControl表示由SCM发送的应该处理的实际控制信号;它有14中可能值,如SERVICE_CONTROL_STOP、SERVICE_CONTROL_PAUSE...。

参数lpContext是处理器被注册时传递给RegisterServiceCtrlHandlerEx的用户自定义数据。

下面是编写服务的例子:

#include <...>

#define UPDATE_TIME 1000

VOID LogEvent(LPCTSTR, DWORD, BOOL);

void WINAPI ServiceMain(DWORD argc, LPTSTR argv[]);

VOID WINAPI ServerCtrlHandlerEx(DWORD, DWORD, LPVOID, LPVOID);

void UpdateStatus(int ,int);     //Calls SetServiceStatus

int ServiceSpecific(int, LPTSTR *);         //Former main program

volatile static BOOL ShutDown = FALSE;

volatile static BOOL PauseFlag = FALSE;

static SERVICE_STATUS hServStatus;

static SERVICE_STATUS_HANDLE hSStat;         //Handle to set status

static LPTSTR ServiceName = _T("AceCommandLineService");

static LPTSTR LogFileName = _T("CommandLineServiceLog.txt");

//Main routing that starts the service control dispatcher

VOID _tmain(int argc, LPTSTR argv[])

{

SERVICE_TABLE_ENTRY DispatchTable[] =

{

{ServiceName, ServiceMain},

{NULL, NULL}

};

StartServiceCtrlDispatcher(DispatchTable);

return 0;

}

//ServiceMain entry point, called when the service is created

void WINAPI ServiceMain(DWORD argc, LPTSTR argv[])

{

DWROD i;

DWORD Context = 1;

//Set the current directory and open a log file

//appending to an existing file

//Set all server status data members

hServStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;

hServStatus.dwCurrentState = SERVICE_START_PENDING;

hServStatus.dwControlAccepted = SERVICE_ACCEPT_STOP |

SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;

hServStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;

hServStatus.dwServiceSpecificExitCode = 0;

hServStatus.dwCheckPoint = 0;

hServStatus.dwWaitHint = 2*CS_TIMEOUT;

hSStat = RegisterServiceCtrlHandlerEx(ServiceName,

ServiceCtrlHandler, &Context);

SetServiceStatus(hSStat, &hServStatus);

//Start service-specific work;generic work is complete

if(ServiceSpecific(argc, argv) != 0)

{

hServStatus.dwCurrentState = SERVICE_STOPPED;

hServStatus.dwServiceSpecificExitCode = 1;

//Server initialization failed

SetServiceStatus(hSStat, &hServStatus);

return;

}

//We will only return here when the ServiceSpecific function

//completes, indicating system shutdown

UpdateStatus(SERVICE_STOPPED, 0);

return;

}

//Set a new service status and checkpoint

//either specific value or increment

void UpdateStatus(int NewStatus, int Check)

{

if(Check < 0)

hServStatus.dwCheckPoint++;

else

hServStatus.dwCheckPoint = Check;

if(NewStatus >= 0)

hServStatus.dwCurrentState = newStatus;

SetServiceStatus(hSStat, &hServStatus);

return;

}

//Control handler function,invoked by the SCM to run

//in the same thread as the main program

VOID WINAPI ServerCtrlHandlerEx(DWORD Control, DWORD EventType,

LPVOID lpEventData, LPVOID lpContext)

{

switch(Control)

{

case SERVICE_CONTROL_SHUTDOWN:

case SERVICE_CONTROL_STOP:

ShutDown = TRUE;

UpdateStatus(SERVICE_STOP_PENDING, -1);

break;

case SERVICE_CONTROL_PAUSE:

PauseFlag = TRUE;

break;

case SERVICE_CONTROL_CONTINUE:

PauseFlag = FALSE;

break;

case SERVICE_CONTROL_INTERROGATE:

break;

default:

if(Control > 127 && Control < 256)//User defined

break;

}

UpdateStatus(-1, -1);  //Increment checkpoint

return;

}

//This is the service-specific function,or "main",and is

//called from the more generic ServiceMain.

int ServiceSpecific(int argc, LPTSTR argv[])

{

UpdateStatus(-1, -1);

//---Initialize system---

//Be sure to update the checkpoint periodically

return 0;

}

服务控制程序设计

控制程序一般是一个普通的Win32应用程序,用于对服务进行启动和停止等操作。

首先的打开SCM,获取句柄然后允许创建服务:

SC_HANDLE OpenSCManager(

LPCTSTR lpMachineName, //为NULL表示SCM在本地系统,也可以访问网络上的SCM

LPCTSTR lpDatabaseName,    //通常为NULL

DWORD dwDesiredAccess);

注册服务使用函数CreateService:

SC_HANDLE CreateService(

SC_HANDLE hSCManager,     //由OpenSCManager获得的SC_HANDLE

LPCTSTR lpServiceName, //指定要安装的服务的名称。

//此字符串对应服务注册处中一个子键的名称

LPCTSTR lpDisplayName,       //用户界面程序使用的标识该服务的名称

//它对应服务注册处lpServiceName子键下DisplayName的键值

DWORD dwDesiredAccess,     //访问权限

DWORD dwServiceType, //服务类型,对应服务注册处lpServiceName子键下Type的键值

DWORD dwStartType,    //指定何时启动此服务。如果打算通过命令启动,可以传递

//SERVICE_DEMAND_START,如果打算让此服务在系统引导

//时自动启动,传递SERVICE_AUTO_START,它对应

//注册表中的Start键值

DWORD dwErrorControl,      //指定驱动启动失败这个错误的严重性。

//SERVICE_ERROR_IGNORE表示忽略所有错误;

//SERVICE_ERROR_NORMAL表示记录下可能出现的错误。

//此参数对应注册表中的ErrorControl键值

LPCTSTR lpBinaryPathName, //驱动二进制文件(.sys文件)的路径,

//对应注册表中的ImagePath键值

LPCTSTR lpLoadOrderGroup, //指定此服务所属的加载顺序组的名字

LPDWORD lpdwTagId,   //指定一个在lpLoadOrderGroup组中唯一的标签

LPCTSTR lpDependencies,       //指定一组此服务依靠的服务的名字

LPCTSTR lpServiceStartName,       //指定此服务应该运行在哪一个账户下

LPCTSTR lpPassword);     //指定lpServiceStartName账户对应的密码

注册后新的服务位于注册表项中:

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services

服务创建后并不立即执行,我们通过指定CreateService函数获取的句柄,以及服务主函数的argc、argv命令行参数,可以启动ServiceMain()函数:

BOOL StartService(

SC_HANDLE hService,      //指定打开的服务,这是OpenService或CreateService返回的句柄

DWORD argc,    //对设备驱动来说,此参数永远是NULL

LPTSTR argv[]);//对驱动服务来说,此参数应该设为NULL

通过告知SCM使用指定控制选项来调用服务控制处理器,即可控制服务:

BOOL ControlService(

SC_HANDLE hService,      //服务句柄

DWORD dwControlCode,      //向服务发送的控制代码

//要停止服务,应发送SERVICE_CONTROL_STOP

LPSERVICE_STATUS lpServStat);   //返回服务的状态

使用以下方式可以获得SERVICE_STATUS数据结构中服务的当前状态值:

BOOL QueryServiceStatus(

SC_HANDLE hService,      //要查询服务的句柄

LPSERVICE_STATUS lpServiceStatus);   //用于返回状态信息

实例代码如下:

void ACECreate()       //创建服务

{

SC_HANDLE scm;      //打开指定服务

SC_HANDEL svc;

//打开SCM数据库并返回句柄

scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);

if(scm != NULL)

{

svc = CreateService(scm, "aceservice", "ace's Windows Service",

SERVICE_ALL_ACCESS,

SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,

SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,

"C://ServiceFileName.exe",      //Service本体程序路径

NULL, NULL, NULL, NULL, NULL);

if(svc != NULL)

CloseServiceHandle(svc);  //关闭新服务句柄

CloseServiceHandle(scm); //关闭SCM数据库句柄

}

}

void ACEDelete() //删除服务

{

SC_HANDLE scm;

SC_HANDLE svc;

SERVICE_STATUS ServiceStatus;

//指定服务控制管理器建立连接并打开服务管理数据库

scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);

if(scm != NULL)

{

QueryServiceStatus(svc, &ServiceStatus);      //返回服务状态,保存到状态结构变量中

if(ServiceStatus.dwCurrentState == SERVICE_RUNNING)

{

//删除之前先得停止服务

ControlService(svc, SERVICE_CONTROL_STOP, &ServiceStatus);

DeleteService(svc);     //向服务发送控制代码

CloseServiceHandle(svc); //删除Service后,最后再调用CloseServiceHandle()

//以便立即从数据库中移走此条目

}

CloseServiceHandle(scm);

}

}

转载于:https://www.cnblogs.com/android-html5/archive/2010/06/10/2534034.html

《Windows核心编程》---Windows服务相关推荐

  1. Windows程序设计与Windows核心编程(资源)

    Windows程序设计与Windows核心编程 1.Ready <Windows程序设计>(百科) <Windows核心编程>(百科) 2.Resource Windows程序 ...

  2. 《WINDOWS核心编程》的源程序编译不了是什么问题?

    我在网上下了该书的源程序,在本机编译第一个程序ErrorShow,错误如下:         Compiling...     ErrorShow.cpp     You   are   not   ...

  3. 线程基础知识——Windows核心编程学习手札系列之六

    线程基础知识 --Windows核心编程学习手札系列之六 线程与进程一样由两部分构成:一是线程的内核对象,操作系统用它来对线程实施管理,也是系统用来存放线程统计信息的地方:二是线程堆栈,用于维护线程在 ...

  4. [笔记]Windows核心编程《十六》线程栈

    系列文章目录 [笔记]Windows核心编程<一>错误处理.字符编码 [笔记]Windows核心编程<二>内核对象 [笔记]Windows核心编程<三>进程 [笔记 ...

  5. C++Windows核心编程读书笔记(转)

    http://www.makaidong.com/(马开东博客) 这篇笔记是我在读<windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的 ...

  6. [C++]《Windows核心编程》读书笔记

    这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和Windows实际机制可能有出入 ...

  7. 《windows核心编程系列》二谈谈ANSI和Unicode字符集

    第二章:字符和字符串处理 使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是 ...

  8. 《windows核心编程系列》十八谈谈windows钩子

    windows应用程序是基于消息驱动的.各种应用程序对各种消息作出响应从而实现各种功能. windows钩子是windows消息处理机制的一个监视点,通过安装钩子能够达到监视指定窗体某种类型的消息的功 ...

  9. 《Windows核心编程(第5版•英文版)》暨《深入理解.NET(第2版•英文版)》有奖书评/读书笔记征集活动

    <Windows核心编程(第5版•英文版)>暨<深入理解.NET(第2版•英文版)>有奖书评/读书笔记征集活动 图灵公司自成立以来,得到了CSDN的很多专家和朋友的帮助.为了感 ...

  10. chHANDLE_DLGMSG(windows核心编程)讲解

    看完<Windows程序设计>后开始看<windows核心编程>, 结果看第一个案例的时候就很惊人的发现,Jeffery大牛的代码很深奥.乍一看好像没有包含<window ...

最新文章

  1. 下一站,向冠军冲击!
  2. 2016全球教育机器人发展白皮书
  3. CentOS基础命令大全
  4. [转]FINDSTR正则表达式小结
  5. java网络编程(一)
  6. python多线程为啥是假的?(GIL 全局解释器锁)(python多线程不适合并行化的计算密集型代码)
  7. 请把下面的列表转换为html,在python中将列表转换为HTML表的最简单方法是什么?...
  8. LinuX 硬盘分区细节详谈 【 整理至 LinuxSir BY FreeXploiT 】
  9. php获取跳转后url,php获取跳转后真实url的方法
  10. 搭建FTP服务之pure-ftpd
  11. polyval matlab 怎么用,matlab 中polyval的用法 最好能举个例子
  12. 如何快速访问AWS的云服务
  13. 双耳节拍 枕头_枕头的故事
  14. 支付宝二维码可以抓包更改金额_mac下 安卓模拟器抓包推荐 - _恒
  15. 如何使用爬虫采集搜狐汽车新车资讯
  16. 最少操作次数(英雄会)
  17. 尚硅谷大数据项目之电商数仓(4即席查询数据仓库)
  18. 使用pscp命令将Windows和linux中文件互相拷贝
  19. 电信天翼云搭建Halo博客
  20. 软件设计师-系统开发与软件工程

热门文章

  1. 今天看到头条好多新手说摆摊不挣钱
  2. 如何跟成功的男人谈恋爱?
  3. 许多新兴的互联网O2O企业,做的都是一些“无中生有”的事情
  4. CPU为什么不做成圆的而是方的?
  5. 计算机网络————P2 标准化工作及相关组织
  6. Spring_day3
  7. mysql数据库怎么读文件_mysql数据库读写文件
  8. 游标sql server_SQL Server游标教程
  9. ssms只有空白解决方案_SSMS中的解决方案和项目概述
  10. sql azure 语法_如何在SQL 2016中使用Azure Key Vault使用AlwaysOn配置TDE数据库