在 Windows CE 上实现网络服务1


适用于:
Microsoft? Windows? CE 4.0 和更新版本Windows CE 支持丰富的网络功能。在 Windows CE 中,有 TCP/IP、IrDA 和蓝牙协议栈,另外还有 Winsock、Winsock2、Wininet 和基于 MSXML3 SP1 的 XML 分析程序。在 Windows CE 4.1 中,添加了 IPv6 支持。尽管如此,由于它和 PDA 之间存在重大的联系,因此 Windows CE 通常只被视为网络事务中的客户端。可能令人吃惊的是,Windows CE 随附了许多服务器,包括 Web 服务器、通用即插即用 (UPnP) 和消息队列 (MSMQ)。

Services.exe 概述

尽管有这么多可供 Platform Builder 使用的出色功能(如 Internet Explorer 和 Microsoft DirectX? 支持),但 Windows CE 在本质上是一个嵌入式操作系统。然而,它具有某些限制。一个限制是最多只能有 32 个进程同时运行。如果每个服务都在它自己的进程中运行,那么它们不仅会浪费资源,而且还会使系统更加接近它的 32 个进程最大限制。

在 Windows CE 3.0 和更低版本中,解决方案是将服务实现为在 device.exe 的上下文中运行的设备驱动程序。像其名称所暗示的那样,Device.exe 是 Windows CE 上所有设备驱动程序的主要宿主。(Device.exe 原来名为 horace.exe,但是由于某种原因,大多数人都没有将 horace 与设备驱动程序联系起来。)因此,在 Windows CE 3.0 和更低版本中,只要涉及到操作系统,Web 服务器就显示为设备驱动程序。让 device.exe 加载多个服务可减少同时运行的进程的数量,并且 device.exe 提供了一种方便的进程间通信手段(因为所有驱动程序都在相同的进程地址空间中运行)。

但是,使用 device.exe 也造成了一些问题。由于许多其他组件(包括网络协议栈和真正的设备驱动程序)都在 device.exe 中运行,因此这可能使跟踪资源泄漏变得非常困难。如果 device.exe 泄漏了内存,并且另外有 50 个设备驱动程序正在运行,那么如何找到出错的一方呢?更加糟糕的是,编写质量低下的 DLL 可能会破坏 TCP/IP 协议栈、PCMCIA 驱动程序或其他设备驱动程序中的内存,从而导致无法解释的故障。

创建 Services.exe 的目的就是为了处理这些问题。设计 Services.exe 是为了具有一个类似于 device.exe 的编程模型和应用程序编程接口 (API) 集,以便将实际上不是设备驱动程序的设备驱动程序(考虑一下 Web 服务器)从 device.exe 移动到 services.exe 中。如果您不熟悉 device.exe,请不必担心。本文将从基础知识开始讨论。如果您熟悉 Windows CE 上的设备驱动程序,则本文的很多内容将为您所熟悉。

Services.exe 提供了下列重要功能:

Services.exe 在系统启动时自动加载服务,并且只消耗一个进程。

Services.exe 导出一个 API 并且具有到文件系统 CreateFileDeviceIoControl 函数的挂钩,这极大地简化了服务和设备上的其他应用程序之间的进程间通信。

可以将 services.exe 配置为侦听指定端口上的请求,它随后可以将请求转发给服务器。使用该模型编写服务可以获得循序渐进的学习体验。

Windows CE 上的 services.exe 与 Windows XP 的 services.exe 有哪些共同之处?二者都用来承载其他服务(包括第三方服务)。它们具有相同的名称。但是,在 Windows CE 和 Windows XP 上,用于编写服务的 API 集和编程模型是完全不同的。

系统初始化:解释注册表

服务是在 DLL 中实现的。例如,Web 服务器位于 httpd.dll 中。有关必须导出的确切函数以及它们何时被调用的说明,请参阅实现服务的要素。

Services.exe 在 Windows CE 启动时自动启动。在启动时,它将枚举 HKEY_LOCAL_MACHINE\Services 的每个子项,其中每个子项都对应于一个服务。Services.exe 使用每个注册表项中的信息将服务加载到它的进程空间中,并调用该服务的初始化函数。

为了更具体地阐述该过程,本文包含了一个服务器,它实现了一个轻量级 Finger 服务器。该示例的灵感来自于 UNIX Finger 服务器,该服务器使用户可以查看远程计算机上的用户的状态。客户端可以运行一个命令(如 finger Bob@microsoft.com)来查询主机 microsoft.com,并请求用户 Bob 的状态。Bob 可以指定每当有人查找他时就返回的简单消息,如“I'm out to lunch now”或“Go Reds!”。

本文中的示例使用了一个比真正的 Finger 协议简单得多的协议。当远程客户端打开一个指向端口 79 上基于 Windows CE 的设备的 TCP 连接时,该基于 Windows CE 的设备将返回一个简单的消息。该消息可以由一个简单的 API 调用(也在以下示例中提供)动态更改。但是,单个用户无法创建自定义消息。返回的消息是为整个设备配置的。

因为该协议是如此简单,所以将不会包含任何自定义 Finger 客户端。相反,只需从装有 Windows XP 的计算机中,在命令提示处简单地运行 telnet yourCEDevice 79 以建立一个指向端口 79(而不是常用的 telnet 端口 23)的 TCP 连接。当 Windows CE Finger 服务器收到该 TCP 连接时,它会将设备消息发送给客户端。Telnet 客户端完全按照消息被接收时的内容回送该消息,然后退出。按照这种方式使用 telnet 客户端就可以满足本文的需要了。请记住,本文重点讨论在 Windows CE 上编写服务器,而不是客户端。

保密性如何呢?请考虑一下在设备上创建诸如 Finger 服务器之类的服务。Internet 上的每个人都需要能够获得 Bob 的状态吗?网络安全值得另外撰写一份白皮书(或有关该问题的书籍)加以讨论。至于现在,本文之所以提供该 Finger 服务器,是因为它易于理解,而不是因为它是保护隐私的值得效仿的示例。

您需要做的第一件事情是让 services.exe 知道如何加载您的 DLL。您需要的全部东西只是一个注册表条目:

HKEY_LOCAL_MACHINE\Services\FINGERSERVER "Dll"=" fingerServer.dll" "Order"=dword:10 "Prefix"="FIN" "Index"=dword:0 "Context"=dword:1

有关这些注册表值的详细信息,请参阅 或 。

以下为上述每个注册表值的简短说明:

"DLL" 只提供了实现该服务的 DLL 的名称。如果未提供完整路径,则 DLL 应当位于 \Windows 目录中。

“Order” 告诉 services.exe 按照哪种顺序加载服务(相对于 HKEY_LOCAL_MACHINE\Services 中的其他服务),其中,较低数字表示优先于较高数字。“Order” 值被设置为小于 10 的服务将在 fingerServer.dll 之前加载;“Order” 值大于 10 的服务将在 fingerServer.dll 之后加载。该值不必唯一,但是加载一系列具有相同次序的服务没有定义。

“Prefix” 指定服务的前缀。Services.exe 所关心的所有位于 fingerServer.dll 中的函数都必须具有前缀 FIN_。而且,希望通过使用 CreateFile 调用到服务中的应用程序会将文件名设置为 "FIN X :",其中 X 介于 0 和 9 之间(包括 0 和 9 在内)。

“Index” 是设备的初始文件系统引用的索引("FIN X :" 中的 X)。对于大多数服务(包括 Finger 服务器在内),可以将该值设置为 0。

“Context” 指定要传递给 FIN_Init(这是 fingerServer.dll 导出以处理它的初始化的一个函数)的初始 DWORD 值。值 1 是一个不可思议的值,services.exe 使用它来创建超级服务线程。

Services.exe 在启动过程中很晚才启动。到它被创建的时候,filesys.exe 和 device.exe 都已在运行。但是,这些程序在运行并不意味着,在服务初始化的时候,系统上的所有程序都在运行。设备可能还不具有动态主机配置协议 (DHCP) 地址,或者某个可移动的文件系统可能尚未启动。当您开发服务时,您需要知道这些限制。这些限制不会影响简单的 Finger 服务器。它可能会影响其他更为复杂的服务。例如,如果 Web 服务器在首次尝试打开它的日志文件时无法成功,那么它将休眠,然后重新尝试处理日志文件已经被配置为位于尚未初始化的文件系统上这一情况。

根据注册表值的不同,应用程序还可以通过编程方式来加载服务。要加载服务,请使用 ActivateService(LPWSTR szServiceName , DWORD dwReserved ),其中 szServiceName 是该服务在注册表中的名称。在本文包含的示例中,将使用 ActivateService("FINGERSERVER",0)。如果该服务已经卸载,则 ActivateService 是除重新启动设备以外重新加载该服务的唯一方式。

Services.exe 的应用程序接口

假设您已经编写、编译了 Finger 服务器并将其复制到设备中。当您观察它的时候,假设 serivces.exe 已经加载了它。应用程序如何使用该服务?该上下文中的应用程序意味着另一个程序,它在基于 Windows CE 的设备上运行,需要使用进程间通信来配置/控制/查询正在运行的服务的状态。它不是指想要检索该设备的消息的网络客户端。那已经通过 telnet yourCEDevice 79 解决了。

在 Windows CE 和 Windows XP 上,就像访问设备驱动程序一样,使用文件系统 API(如 CreateFileWriteFileReadFileDeviceIoControl)来访问服务。CreateFile 通过服务的名称调用,并且返回指向其他 API 访问的服务的句柄。DeviceIoControl 发送特定于服务和控制它的应用程序的自定义 I/O 控制调用。Services.exe 还定义了很多您可以根据情况来实现的 I/O 控制码,例如,用于停止或启动服务器的命令。这些 IOCTLS 保存在公共头文件 service.h 中 — 对于版本 4.0 和 4.1,该文件位于 Platform Builder 安装的 public\common\oak\inc 目录中;对于 4.2 和更高版本,该文件位于 public\common\sdk\inc 中。

在 Windows XP 中,可以将设备驱动程序命名为“\Device\TheFingerServer”、“\Device\ThisIsTheFingerServerAndItsReallyCool”或其他名称。在 Windows CE 中,其名称不能如此富于创造性,而这可能不是一件糟糕的事情。每个服务名称都被唯一标识,方法是向该服务的位于 HKLM\Services\{ServiceName} 下的 “Prefix”“Index” 值中添加一个冒号。在 Finger 服务器示例中,服务的名称是 “FIN0:”。与 Windows XP 中不同,服务无需通过使用任何 API 将其自身显式注册为可由 CreateFile 调用。在服务已经由 services.exe 加载之后,应用程序将能够调用它。

那么,在这些 API 的背后发生了什么事情呢?当 CreateFile 获得的字符串具有三个字符、一个数字和一个冒号(如 “FIN0:”)时,它将假设它正在接收访问设备驱动程序或服务(而不是文件)的请求。Filesys.exe 将首先查询 device.exe,以查看它能否识别设备名称。如果它不能识别,则 filesys.exe 会调用到 services.exe 中。Services.exe 将在它的运行服务表中查找匹配项。如果 services.exe 找到了匹配项,它会创建一个句柄并将其返回到 filesys.exe,后者又将其返回到应用程序。当您用该句柄调用 DeviceIoControlReadFile 或类似的 API 时,内核将用该 API 的参数直接调用到 services.exe 中。

对于一些服务器而言,实现流接口(也就是说,它可以支持 ReadFileWriteFile 操作)是有意义的。Telnet 服务器是一个经典示例。Telnet 将其本身设置为 cmd.exe 的 STDIN 和 STDOUT,因此它必须处理由 cmd.exe 生成并最终通过 filesys.exe API 执行的 scanfprintf 调用。

但是,在大多数情况下,使用 DeviceIoControl 是控制服务的最简单(通常也是唯一的)机制。DeviceIoControl 可以支持您定义的 IOCTL,并且同时采用输入和输出参数。ReadFileWriteFile 无法同时采用参数。如果您希望检验流服务,请观察 Platform Builder 中的 %_WINCEROOT%\public\servers\sdk\samples\telnetd。为了防止降低 Finger 服务器的研究价值,没有将其实现为流服务。

在 Finger 服务器示例中,服务定义和实现了 IOCTL 代码,以获得和设置发送到 Finger 客户端的消息。您可以记录您使用的 IOCTL,并且让应用程序程序员直接向您的服务调用 CreateFileDeviceIoControl 写入,但是还有一种更好的方式。您可以将文件系统调用的所有细节包装到一个小型库中,以供应用程序程序员链接。该方法正是 MSMQ 导出其接口的方法。例如,MQOpenQueue 是 msmqrt.lib 中的一个非常简短的函数,它将函数参数封送到对 DeviceIoControl 的调用中。该调用使应用程序程序员的工作变得更容易,并且还使您的代码更易于移植到使用与 Windows CE 不同的进程间通信方法的平台。只需更改存根库的实现,调用应用程序就可以保持不变。下面给出了 Finger 运行时库的实现。

FingerRT.dll 库(RT 代表运行时)导出了两个函数。第一个函数是 SetFingerMessage。该函数告诉 Finger 服务器当客户端连接到它时显示什么消息。导出的另一个函数是 GetFingerMessage,它检索 Finger 服务器将返回给网络客户端的消息。

Fingerrt.dll Code: [fingerService.h] #define IOCTL_FINGER_SET_MESSAGE 1 #define IOCTL_FINGER_GET_MESSAGE 2 [FingerRT.cpp] #include #include #include <..\inc\fingerService.h> static HANDLE hInterface = INVALID_HANDLE_VALUE; static int Initialize (void) { if (hInterface == INVALID_HANDLE_VALUE) { hInterface = CreateFile (L"FIN0:", 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } return (hInterface != INVALID_HANDLE_VALUE); } static void UnInitialize(void) { if (hInterface != INVALID_HANDLE_VALUE) { CloseHandle(hInterface); hInterface = INVALID_HANDLE_VALUE; } } BOOL SetFingerMessage (CHAR *szMessage) { if (!Initialize ()) return FALSE; return DeviceIoControl (hInterface, IOCTL_FINGER_SET_MESSAGE, (void *) szMessage, strlen(szMessage)+1, NULL, 0, NULL, NULL); } BOOL GetFingerMessage (CHAR *szMessage, DWORD *pcchMessage) { if (!Initialize ()) return FALSE; return DeviceIoControl (hInterface, IOCTL_FINGER_GET_MESSAGE,NULL, 0, (void *) szMessage, *pcchMessage, pcchMessage, NULL); }实现服务的要素

服务导出以注册表中指定的 “Prefix” 值开头、并用定义良好的函数字符串终止的函数。以下为一个 fingerServer.dll .def 文件:

fingerServer.def: LIBRARY FINGERSERVER EXPORTS FIN_Init FIN_Deinit FIN_Open FIN_Close FIN_IOControl

当 services.exe 对您的服务调用 LoadLibrary 时,加载程序将调用该服务的 DllMain 函数。DllMain 主要用于执行同步原语的创建(和销毁),并且在任何 Win32 编程方案中(而不仅仅是在服务和 Windows CE 中),DllMain 中都有很多不安全的操作。不要对 COM 或 Winsock 进行调用,不要创建线程或调用任何可能在另一个 DLL 中实现的函数。

在加载之后,services.exe 将调用 xxx _Init。(按照约定,xxx 表示服务的 “Prefix” 值,该值因服务而异;在该示例中,xxx=FIN。)Services.exe 将该服务的 “Context” 注册表值传递到函数中。xxx _Init 是对 COM、Winsock 或其他所有无法从 DllMain 调用的组件进行调用的地方。

在启动时,services.exe 在一个线程上创建服务,并阻塞它们对 xxx _Init 的调用。当您的服务在 xxx _Init 中执行时,没有其他服务可以启动,因此请不要在那里阻塞。相反,如果您需要执行阻塞操作(例如 Winsock 接受调用),请让您自己的线程空转,或者,更好的办法是使用 services.exe 超级服务器功能来侦听传入的连接。

如果 xxx _Init 返回 FALSE,则服务会被卸载。假定它成功,则 services.exe 可能调用 xxx _IOControl 很多次以传入配置信息。Services.exe 生成的 IOCTL 代码(包括简短说明)位于头文件 service.h 中。您不必处理 service.h 中的所有 IOCTL(就该问题而言,您不必处理它们中的任何一个)。通过实现代码来支持服务管理 IOCTL(例如,IOCTL_SERVICE_STOPIOCTL_SERVICE_START),您的服务的配置可以变得容易很多,并且可以使用与现有服务非常类似的接口。

当卸载服务实例时,会调用 xxx _DeInit。在您可以同时具有多个服务实例的情况(例如,telnet 具有“TEL0:”、“TEL1:”和“TEL2:”,每一个都对应于不同的 telnet 会话,并且当该特定实例关闭时,每一个都让 xxx _DeInit 对它们进行调用)下,xxx _DeInit 未必意味着 DLL 将要卸载。因为 Finger 服务器示例只创建“FIN0:”,所以被调用的 xxx _DeInit 告诉它 DLL 将要卸载。Services.exe 会忽略 xxx _DeInit 的返回值。在该函数返回之后,Finger 服务器始终会卸载。至关重要的一点是,当您的服务从 xxx _DeInit 返回时,它不会让任何辅助线程运行。在它返回之后,services.exe 会立即从内存中卸载您的服务 DLL。如果您具有仍在运行的辅助线程,则当它们尝试访问该 DLL 的已卸载代码页时,它们将崩溃。请让 xxx _DeInit 阻塞一秒钟、一个月或者一年,直到 Red Sox 赢得另一届 World Series 为止 — 但是在该服务准备好卸载之前,它无论如何都不应当返回。

只有当您要实现流服务(如 telnet 服务器)时,服务所导出的其余函数才会让您感到有趣。(Telnet 是一个非常有趣的示例,并且具有良好的注释,因此如果您要认真研究 Windows CE 服务,它可能是一个很好的研究主题。)当 services.exe 成功地将对 CreateFile("PrefixX:") 的调用映射到给定服务时,xxx _Open 将被调用。该示例必须返回 TRUE。返回 FALSE 将导致调用应用程序对 CreateFile 的调用失败。同样,xxx _Close 在对服务的句柄执行 CloseHandle 期间调用。只有当您创建流服务时,才需要实现 xxx _Read、xxx _Write 和 xxx _Seek。这些调用对应于 ReadFileWriteFileSetFilePosition

Services.exe 提供最低限度的同步,因此所有同步都需要在您的服务中完成。尽管相关内容超出了本文的范围,但需要说明的是,实际上在某些情况下,可以从多个线程中同时调用 xxx _Init。不要将所有告诉您不要在该函数中阻塞的警告理解为可以忽视它的线程安全性。

以下为我们的 Finger 服务器中与网络无关的部分。稍后将讨论网络部分。

#include #include #include // Use strsafe.h, rather that CRT string manipulation, since these // functions are safer - especially when manipulating external strings // such as those provided in IOCTL calls or those read from across the // network #include #include <..\inc\fingerService.h> // Though not required, using a DWORD and macros defined in service.h // to keep track of state allows for easier configuration. DWORD g_dwServiceState; // Buffer that contains what you'll send to network client on request. CHAR g_szMessage[MAX_PATH]; // Length of string currently held in g_szMessage DWORD g_ccMessage; // Before the initial call to SetFingerMessage, use this as our default // response. CHAR g_cszDefaultMsg[] = "Hello World! I am the sample Finger server!"; // Global critical section CRITICAL_SECTION g_cs; // Function that processes requests extern "C" DWORD WINAPI FingerWorker(LPVOID lpv); // Handle to the worker thread HANDLE g_hWorkerThread; // Use CE's built-in debugging framework. #ifdef DEBUG DBGPARAM dpCurSettings = { TEXT("FingerServer"), { TEXT("Error"),TEXT("Init"),TEXT("Network Client"),TEXT("Interface"), TEXT("API"),TEXT(""),TEXT(""),TEXT(""), TEXT(""),TEXT(""),TEXT(""),TEXT(""), TEXT(""),TEXT(""),TEXT(""),TEXT("") }, 0x0007 // Turn on Error, Init, and Client DEBUGZONE's by default }; #define ZONE_ERROR DEBUGZONE(0) #define ZONE_INIT DEBUGZONE(1) #define ZONE_CLIENT DEBUGZONE(2) #define ZONE_INTRF DEBUGZONE(3) #define ZONE_API DEBUGZONE(4) #define ZONE_NET DEBUGZONE(5) #endif extern "C" BOOL WINAPI DllEntry(HANDLE hInstDll, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: g_dwServiceState = SERVICE_STATE_UNINITIALIZED; InitializeCriticalSection (&g_cs); // This DLL does not require thread attatch/deatch DisableThreadLibraryCalls((HMODULE)hInstDll); // Hook into CE system debugging DEBUGREGISTER((HINSTANCE)hInstDll); break; case DLL_PROCESS_DETACH: DeleteCriticalSection (&g_cs); break; } return TRUE; } extern "C" DWORD FIN_Init (DWORD dwData) { DEBUGMSG(ZONE_INTRF,(L"FINGERD: FIN_Init(0x%08x) called\r\n",dwData)); EnterCriticalSection (&g_cs); if (g_dwServiceState != SERVICE_STATE_UNINITIALIZED) { // Someone is trying to load multiple times (for example, // trying to create "FIN1:"). // Not supported, so fail to load service. DEBUGMSG(ZONE_ERROR,(L"FINGERD: ERROR: Finger service already " L"initialized on FIN_Init() call\r\n")); LeaveCriticalSection (&g_cs); return 0; } StringCchCopyA(g_szMessage,sizeof(g_szMessage),g_cszDefaultMsg); g_ccMessage = strlen(g_cszDefaultMsg); g_dwServiceState = SERVICE_STATE_STARTING_UP; DEBUGMSG(ZONE_INIT,(L"FINGERD: FIN_Init success - service state is in " L"starting up state\r\n")); LeaveCriticalSection (&g_cs); return 1; } extern "C" BOOL FIN_Deinit(DWORD dwData) { DEBUGMSG(ZONE_INTRF,(L"FINGERD: FIN_DeInit(0x%08x) called\r\n",dwData)); EnterCriticalSection (&g_cs); g_dwServiceState = SERVICE_STATE_UNLOADING; // If worker threads are active, you MUST block until they // have shutdown. Real services may take extra action to force // the threads to end (that is, closing all open sockets workers // are using) to speed up the shutdown process. // This function is also the proper place to do // uninitialization of other components that are not safe // to be called from DllMain - that is, COM, Winsock, and so on if (g_hWorkerThread) { DEBUGMSG(ZONE_INIT,(L"FINGERD: Waiting for worker thread to complete " L"before service shutdown\r\n")); HANDLE hWorker = g_hWorkerThread; LeaveCriticalSection (&g_cs); // Block until the worker is through running. WaitForSingleObject(hWorker,INFINITE); } else { LeaveCriticalSection (&g_cs); } // Service is unloaded no matter what is returned. DEBUGMSG(ZONE_INIT,(L"FINGERD: Completed shutdown. Returning to " L"services.exe for unload\r\n")); return 1; } extern "C" DWORD FIN_Open (DWORD dwData, DWORD dwAccess, DWORD dwShareMode) { // Handles calls application made to CreateFile. // Since no client state is maintained, no further work is required // here. DEBUGMSG(ZONE_INTRF,(L"FINGERD: FIN_Open(0x%08x,0x%08x,0x%08x) called\r\n", dwData,dwAccess,dwShareMode)); return 1; } extern "C" BOOL FIN_Close (DWORD dwData) { // Handles calls application made to CloseHandle. // dwData is the value returned during the call to FIN_Open // This is where resources allocated in the dwData handle // (had there been any) would be freed. DEBUGMSG(ZONE_INTRF,(L"FINGERD: FIN_Close(0x%08x) called\r\n",dwData)); return 1; } extern "C" BOOL FIN_IOControl(DWORD dwData, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut) { DWORD dwError = ERROR_INVALID_PARAMETER; DEBUGMSG(ZONE_INTRF,(L"FINGERD: FIN_IOControl(0x%08x,0x%08x,0x%08x,0x%08x," L"0x%08x,0x%08x,0x%08x) called\r\n", dwData, dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, pdwActualOut)); EnterCriticalSection (&g_cs); switch (dwCode) { // Control code sent to start a service. (services start FIN0:) case IOCTL_SERVICE_START: // In real services, you need to be very careful about // state changes and timing issues, and you'll almost // certainly do more work than just changing a var's value. // Finger server will accept connections. if (g_dwServiceState != SERVICE_STATE_OFF) { DEBUGMSG(ZONE_ERROR,(L"FINGERD: ERROR: IOCTL_SERVICE_START fails " L"because service is not off. " L"Current State=<%d>\r\n",g_dwServiceState)); dwError = ERROR_SERVICE_ALREADY_RUNNING; } else { DEBUGMSG(ZONE_INIT,(L"FINGERD: Service state change to on\r\n")); g_dwServiceState = SERVICE_STATE_ON; dwError = ERROR_SUCCESS; } break; // Control code sent to refresh a service. (services refresh FIN0:) case IOCTL_SERVICE_REFRESH: // In real services, you need to be very careful about // state changes and timing issues, and you'll almost // certainly do more work than just changing a var's value. if (g_dwServiceState != SERVICE_STATE_ON) { DEBUGMSG(ZONE_ERROR,(L"FINGERD: ERROR: IOCTL_SERVICE_REFRESH " L"fails because service state is not on. " L"Current State=<%d>\r\n",g_dwServiceState)); dwError = ERROR_SERVICE_NOT_ACTIVE; } else { DEBUGMSG(ZONE_INIT,(L"FINGERD: Service stopping on a refresh\r\n")); g_dwServiceState = SERVICE_STATE_SHUTTING_DOWN; // Shut the service down, re-read configuration (if we have // any), // and then restart. DEBUGMSG(ZONE_INIT,(L"FINGERD: Service restarting on a refresh\r\n")); g_dwServiceState = SERVICE_STATE_ON; dwError = ERROR_SUCCESS; } break; // Control code sent to stop a service. (services stop FIN0:) case IOCTL_SERVICE_STOP: // No longer accept new network connections. Close existing // connections // In real services, you need to be very careful about // state changes and timing issues, and you'll almost // certainly do more work than just changing a var's value. // Finger Server will stop accepting new connections. if (g_dwServiceState != SERVICE_STATE_ON) { DEBUGMSG(ZONE_ERROR,(L"FINGERD: ERROR: IOCTL_SERVICE_STOP fails " L"because service state is not on. " L"Current State=<%d>\r\n",g_dwServiceState)); dwError = ERROR_SERVICE_NOT_ACTIVE; } else { DEBUGMSG(ZONE_INIT,(L"FINGERD: Service stopping\r\n")); g_dwServiceState = SERVICE_STATE_OFF; dwError = ERROR_SUCCESS; } break; // An application (possibly services.exe itself) is // querying for the service's running state. case IOCTL_SERVICE_STATUS: // No need for critical section since this is an atomic read. __try { if (pBufOut && dwLenOut == sizeof(DWORD)) { *(DWORD *)pBufOut = g_dwServiceState; if (pdwActualOut) *pdwActualOut = sizeof(DWORD); dwError = ERROR_SUCCESS; } } __except (EXCEPTION_EXECUTE_HANDLER) { DEBUGMSG(ZONE_ERROR,(L"FINGERD: ERROR Invalid pointer " L"passed on IOCTL_SERVICE_STATUS\r\n")); dwError = ERROR_INVALID_PARAMETER; } break; // Handle API call to SetFingerMessage. case IOCTL_FINGER_SET_MESSAGE: if (pBufIn == NULL) break; __try { // Do NOT trust dwLenIn, since a malicious app may lie // Calculate size of buffer required based on buffer // itself. // Use a try block because you should not trust an // application to have // passed a valid buffer and do not want it to be able to // put // this service into an invalid state. size_t dwStrLen; if (FAILED(StringCchLengthA((CHAR*)pBufIn,sizeof(g_szMessage),&dwStrLen))) break; if (FAILED(StringCchCopyA(g_szMessage,sizeof(g_szMessage),(CHAR*)pBufIn))) break; g_ccMessage = dwStrLen; dwError = ERROR_SUCCESS; DEBUGMSG(ZONE_API,(L"FINGERD: SetFingerMessage() changed message " L"to <%S>\r\n",g_szMessage)); } __except (EXCEPTION_EXECUTE_HANDLER) { // Invalid buffer. Reset message to system default. DEBUGMSG(ZONE_ERROR,(L"FINGERD: ERROR Invalid pointer passed " L"on SetMessage\r\n")); StringCchCopyA(g_szMessage,sizeof(g_szMessage),g_cszDefaultMsg); g_ccMessage = strlen(g_cszDefaultMsg); dwError = ERROR_INVALID_PARAMETER; } break; // Handle API call to GetFingerMessage. case IOCTL_FINGER_GET_MESSAGE: if (pBufOut==NULL) break; if (dwLenOut < (g_ccMessage+1)) { dwError = ERROR_MORE_DATA; break; } __try { if (FAILED(StringCchCopyA((CHAR*)pBufOut,dwLenOut,g_szMessage))) break; if (pdwActualOut) *pdwActualOut = g_ccMessage+1; dwError = ERROR_SUCCESS; DEBUGMSG(ZONE_API,(L"FINGERD: GetFingerMessage() retrieved " L"message <%S>\r\n",g_szMessage)); } __except (EXCEPTION_EXECUTE_HANDLER) { DEBUGMSG(ZONE_ERROR,(L"FINGERD: ERROR Invalid pointer passed " L"on GetMessage\r\n")); dwError = ERROR_INVALID_PARAMETER; } break; } if (dwError != ERROR_SUCCESS) SetLastError(dwError); LeaveCriticalSection(&g_cs); return (dwError==ERROR_SUCCESS); }

以上就是全部内容。哦,除了实际侦听网络以处理请求(这是我们一开始就需要完成的任务)的细枝末节以外。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值