《Windows核心编程》---Windows服务

5分钟搞定PaddleOCR PP-OCRv5环境搭建:从安装到第一个识别Demo 本文提供了一份详细的PaddleOCR PP-OCRv5环境搭建与快速入门指南。通过清晰的步骤,指导读者在5分钟内完成从创建Python虚拟环境、安装PaddlePaddle框架和PaddleOCR库,到编写并运行第一个OCR识别Demo的全过程,帮助开发者快速体验最新的PP-OCRv5模型的文字识别能力。 阅读详情

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);

       }     

}

 

 

 

打破RK3588 GPIO中断响应延迟的枷锁,开启高效编程 本文针对RK3588芯片在智能安防和工业自动化应用中出现的GPIO中断响应延迟问题展开深入分析。通过硬件原理剖析,发现延迟主要源于中断控制器优先级设置、线路干扰等硬件因素,以及中断处理函数复杂、内核调度延迟等软件问题。提出硬件优化方案包括合理布局布线、电源稳定设计,软件层面则通过精准配置设备树、精简中断处理函数、利用工作队列等内核机制进行优化。测试结果显示,优化后中断响应延迟从50-80ms降至10-20ms,高负载下也能稳定在50ms以内。文章最后展望了AI技术在中断调度中的潜在应用价值。 阅读详情

相关推荐

CarPlay无线连接开发实战:从蓝牙EIR查询到iAP2认证的完整代码逻辑梳理

本文详细解析了无线CarPlay连接开发的完整流程,从蓝牙EIR查询到iAP2认证的代码实现。重点介绍了蓝牙握手、WiFi Beacon帧配置、热点信息准备等关键环节的实战技巧,帮助开发者快速搭建稳定可靠的无线CarPlay连接系统。

weixin_31534091的博客 322

RegisterServiceCtrlHandler

RegisterServiceCtrlHandlerA service calls the RegisterServiceCtrlHandler function to register a function to handle its service control requests.This function has been superseded by the RegisterService

小发猫 6040

新手避坑指南:在MacBook上为PyTorch正确配置MPS环境,跑通第一个YOLOv8训练

本文详细指导新手如何在MacBook上为PyTorch正确配置MPS环境,并成功运行YOLOv8训练。从环境准备、PyTorch安装到YOLOv8配置和性能调优,提供全面的避坑指南和实用技巧,帮助开发者充分利用Mac的GPU加速能力。

weixin_29100399的博客 229

50.服务程序SERVICE_STATUS、SERVICE_STATUS_HANDLE、RegisterServiceCtrlHandler、SetServiceStatus、SERVICE_TABL

开始之前,先了解几个结构体 SERVICE_STATUS 是服务状态机构,用于标记服务的状态 typedef struct _SERVICE_STATUS { DWORD dwServiceType; //服务可执行文件的类型 DWORD dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32E...

单于的学习历程 3050

使用VScode编译AMI代码

我现在了解到的和拿到的code,都是基于AMI的code,一般的编译AMI都需要搭配使用AMI的编译环境Veb,而Veb用到现在,一直都觉得有点卡顿,浏览代码的体验也比VScode差很多。在这之前还要,考虑设置TOOLS_DIR和EWKE_DIR的环境变量,因为在Veb中是可以通过设置自动设置的,但是VScode里面没用设置环境变量的选项。这里可以看出,Veb工具栏中的Build和Rebuild All的命令对应这里的make和make rebuild,也就是说Veb是调用此命令进行编译的。

Dodijd的博客 1883

InTouch历史报警与操作记录高效查询控件V4.0实战指南(一键下载即用)

本文详细介绍了InTouch历史报警与操作记录高效查询控件V4.0的实战应用。该控件针对原生AlmDbViewCtrl控件的查询效率低下、功能单一等问题,提供了智能查询、可视化统计、视图定制和一键导出Excel等强大功能,旨在帮助工程师快速处理海量历史数据,大幅提升工控运维效率。

g9h0i1的博客 960

2-Windows服务之相关API说明

它将等待一个服务完成对服务控制通知的处理,然后再发送下一个服务控制通知。在32位机器上,如果设置了服务依赖(例如:lpDependencies参数设置为Tcpip),则lpLoadOrderGroup参数不能设置为NULL,而需要设置为“”空字符串,否则函数会报错,GetLastError()返回1059(指定了循环服务依赖关系),原因未知。ControlService函数要求SCM发送指定的控制码给服务,如果服务已指定将接受代码,并且处于可以向其发送控制码的状态,则SCM将发送指定的控制码。

weixin_40332490的博客 1215

VTK基础教程(1)- vtkPolyData 介绍

目录 VTK的编译与环境搭建 VTK一般渲染流程 VTK数据类型-vtkPolyData 使用vtk自带的多边形数据 使用自己创建的数据 添加顶点颜色 添加面颜色 VTK的编译与环境搭建 这部分目前已经有了大佬做了成熟的傻瓜式配置,大家可以参考这篇文章:VTK+VS2019(或其他版本均可) 傻瓜式一站式 配置_Nancy_fairy的博客-优快云博客_vs2019 vtk VTK一般渲染流程 这部分的介绍也很多,大家可以参考这篇文章:VTK笔记-了解VTK_黑山老妖的博客.

qq_35769071的博客 9054

Windows服务编程之服务程序

Windows服务程序

Fei_Liu的博客 6246

45、使用 sqlread 函数将数据从数据库表导入 MATLAB

本文详细介绍了如何使用 MATLAB 中的 `sqlread` 函数从不同类型的数据库(如 Microsoft SQL Server 和 SQLite)中导入数据。文章涵盖了 `sqlread` 的基本语法、输入输出参数、使用示例以及注意事项,并通过多个实例演示了自定义导入选项、元信息检索和数据类型转换等内容。此外,还总结了导入数据库数据的通用流程及最佳实践建议,帮助用户更高效地进行数据分析和处理工作。

bash7scripter的博客 134

基于深度确定性策略梯度的永磁同步电机强化学习控制方法

本文提出了一种基于深度确定性策略梯度(DDPG)的永磁同步电机(PMSM)电流内环智能控制方法。首先建立了PMSM在d-q坐标系下的数学模型,分析了传统矢量控制双闭环系统中PI控制器的局限性。针对电流内环控制问题,设计了包含电流跟踪误差、转速信息和历史控制量的状态空间,将电压输出作为动作空间,并构建了兼顾跟踪精度和响应速度的奖励函数。通过DDPG算法训练智能体,使其能够自适应调整控制策略。仿真结果表明,与固定参数的PI控制器相比,DDPG控制器在动态响应速度和抗干扰能力方面具有明显优势,为PMSM高性能控制

huanghm88的专栏 104

Windows 编程之服务-1

一、 编写服务程序 一个服务程序至少包括一个入口函数、服务主函数和控制处理函数。 1. 入口函数  入口函数是可执行程序执行的起点,服务程序的入口函数与一般的可执行程序的入口函数 没有区别。 2. 服务主函数 一个服务程序必须具有服务主函数,服务主函数 是服务启动时执行的入口,也是服务的主线程执行起点。  ① 服务主函数 ServiceMain  服务主函数一般称作ServiceMa

u011529563的专栏 894

滑块逆向及流程分析

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!

pyzzd的博客 8009

基于visual c++之windows核心编程代码分析(9)实现Windows服务并安装,控制

我们进行Windows编程的时候,经常涉及到Windows服务编程,针对Windows服务我们该怎么编程呢,我们先来实现一个Windows服务并实现之,请见注释代码分析。/* 头文件 */ #include /* 全局变量 */ SERVICE_STATUS SplSrvServiceStatus; SERVICE_STATUS_HANDLE SplSrvServiceS

尹成的技术博客 4808

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

windows核心编程,纤程使用---->UNIX服务器应用程序移植到Windows中

Microsoft公司给Windows添加了一种纤程,以便能够非常容易地将现有的 UNIX服务器应用程序移植到Windows中。UNIX服务器应用程序属于单线程应用程序(由Windows定义),但是它能够为多个客户程序提供服务。换句话说,UNIX应用程序的开发人员已经创建了他们自己的线程结构库,他们能够使用这种线程结构库来仿真纯线程。该线程包能够创建多个堆栈保存某些CPU寄存器,并且在它们之间进行切换,以便为客户机请求提供服务。

zhyjhacker的博客 812

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

基于visual c++之windows核心编程代码分析 实现Windows服务并安装,控制

我们进行Windows编程的时候,经常涉及到Windows服务编程,针对Windows服务我们该怎么编程呢, 我们先来实现一个Windows服务并实现之,请见注释代码分析。 [cpp] view plaincopyprint? /* 头文件 */   #include    /* 全局变量 */   SERVICE_STATUS

学习笔记 1918

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

基于visual c++之windows核心编程代码分析(45)利用Windows系统服务启动程序

在Windows 系统中,服务是指执行指定系统功能的程序、例程或进程,以便支持其他程序,尤其是低层(接近硬件)程序。通过网络提供服务时,服务可以在Active Directory(活动目录)中发布,从而促进了以服务为中心的管理和使用。服务是一种应用程序类型,它在后台运行。服务应用程序通常可以在本地和通过网络为用户提供一些功能,例如客户端/服务器应用程序、Web服务器、数据库服务器以及其他基于服务器

尹成的技术博客 4521

基于visual c++之windows核心编程代码分析(66)实现Windows服务的远程控制

Windows服务之前已经进行了讲解,如何在安装Windows服务呢,作为远程控制的服务端。安装Windows服务代码如下#include "stdafx.h" //#include #include "InstallService.h" #include BOOL StartService(LPCTSTR lpService) { SC_HANDLE schSCManage

尹成的技术博客 5897
上一篇: 《Windows核心编程》---内核对象和进程基础
下一篇: 《Windows核心编程》---文件操作
ACE1985
博客等级 码龄17年 1307粉丝 308原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值