分类: 学习一个
Ubuntu安装Gitlab
1.安装和配置必要的依赖项
sudo apt-get update sudo apt-get install -y curl openssh-server ca-certificates tzdata perl sudo apt-get install -y postfix
2.添加GitLab包仓库并安装包
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
sudo apt-get install gitlab-ce
3.设置浏览器要访问的IP地址及端口号
编辑配置文件
vim /etc/gitlab/gitlab.rb
4.重新载入配置及启动服务
gitlab-ctl reconfigure
gitlab-ctl start
5.查找Gitlab默认root密码
vim /etc/gitlab/initial_root_password
注:该文件为临时文件,将于reconfigure后的24小时后自动删除,注意尽早保存。
静态链接DLL与动态调用DLL
静态链接DLL:
需要设置“附加包含目录”,“附加库目录”以及“附加依赖项”。
然后通过外部引用函数定义即可。
#include <stdio.h> #include <Windows.h> extern int add(int a, int b); int main(int argc, char* argv[]) { MessageBoxW(NULL, L"Test", L"Test", MB_OK); printf("5 + 3 = %d", add(5, 3)); return 0; }
动态调用DLL:
即通过函数调用的方式获得所需函数的地址,然后通过函数指针的方式调用函数。
#include <stdio.h> #include <Windows.h> int main(int argc, char* argv[]) { HMODULE load_lib = NULL; FARPROC get_proc_add = 0; MessageBoxW(NULL, L"Test", L"Test", MB_OK); do { load_lib = LoadLibraryW(L"C:\\Users\\Raiseki\\source\\repos\\dll_learn\\x64\\Debug\\dll_learn.dll"); if (load_lib == NULL) { break; } get_proc_add = GetProcAddress(load_lib, "add"); if (get_proc_add == NULL) { break; } printf("5 + 3 = %lld", get_proc_add(5, 3)); } while (0); return 0; }
注意:
当找不到DLL文件时,采用静态链接方式的程序系统会报错且无法运行。而采用动态调用方式的程序虽然无法调用到所需函数,但系统不会报错。
C语言控制台程序编写Windows服务(二)
在C语言控制台程序编写Windows服务(一)的基础上进行了改动。对比上次,本次用到了Event(事件)和父子进程的概念,解决了服务在系统重启后无法随系统自动启动的问题。
目标回顾:编写一个服务,能够正常添加到Windows服务列表并能随系统启动,可正常手动启停。暂时不需要通过服务实现任何实际的功能。
修改过程中明确了以下几点:
1. 明确原理,仔细阅读文档说明。
以此方式写exe服务在exe文件启动时会存在两个进程。一个控制台程序的进程以及一个服务进程。
2. 明确需要做的工作。同时满足以下两件事。
(1)判断服务进程是否已经存在。如果不存在则通过控制台程序进程创建服务并启动,否则退出,不要让程序运行到StartServiceCtrlDispatcherW。
(2)要避免服务进程重复创建进程导致程序出错。这就需要能够判断当前进程是控制台程序进程还是服务进程。服务进程的特点是它们都是services.exe的子进程,所以可以通过判断当前进程的父进程是否为service.exe的方式来对两种进程进行区分。方法如下:
获取进程pid,ppid以及进程名
3.今后编写代码要注意清晰简洁。大量无意义的代码有时会让自己陷入混乱。
以下为修改后的代码:
#include <stdio.h> #include <Windows.h> #include <tlhelp32.h> SERVICE_STATUS service_status; SERVICE_STATUS_HANDLE service_status_handle; BOOL GetProcessInfoWithPID(DWORD pid, DWORD* ppid, WCHAR* process_name) { BOOL result = FALSE; HANDLE snapshot_handle = NULL; PROCESSENTRY32W process_entry32; BOOL process_return = FALSE; do { snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot_handle == INVALID_HANDLE_VALUE) { break; } process_entry32.dwSize = sizeof(process_entry32); process_return = Process32FirstW(snapshot_handle, &process_entry32); while (process_return) { if (process_entry32.th32ProcessID == pid) { if (ppid != NULL) { *ppid = process_entry32.th32ParentProcessID; } if (process_name != NULL) { lstrcpyW(process_name, process_entry32.szExeFile); } result = TRUE; break; } process_entry32.dwSize = sizeof(process_entry32); process_return = Process32NextW(snapshot_handle, &process_entry32); } } while (0); if (snapshot_handle) { CloseHandle(snapshot_handle); } return result; } BOOL ServiceStart(HANDLE handle) { BOOL result = FALSE; SERVICE_STATUS_PROCESS status; DWORD bytes_need = 0; SecureZeroMemory(&status, sizeof(status)); do { result = QueryServiceStatusEx(handle, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(SERVICE_STATUS_PROCESS), &bytes_need); if (result == FALSE) { break; } else if ((status.dwCurrentState == SERVICE_RUNNING) || (status.dwCurrentState == SERVICE_START_PENDING)) { service_status.dwCurrentState = SERVICE_RUNNING; result = TRUE; break; } result = StartServiceW(handle, 0, NULL); if (result == FALSE) { break; } printf("please wait...\n"); do { result = FALSE; Sleep(500); result = QueryServiceStatusEx(handle, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(SERVICE_STATUS_PROCESS), &bytes_need); if (result == FALSE) { break; } } while (status.dwCurrentState == SERVICE_START_PENDING); result = FALSE; if (status.dwCurrentState == SERVICE_RUNNING) { result = TRUE; break; } } while (0); return result; } BOOL InstallService() { BOOL result = FALSE; SC_HANDLE SCM_handle = NULL; SC_HANDLE service_handle = NULL; DWORD return_value = 0; TCHAR Path[MAX_PATH]; do { return_value = GetModuleFileNameW(NULL, Path, MAX_PATH); if (return_value == 0) { break; } SCM_handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (SCM_handle == NULL) { break; } service_handle = CreateServiceW( SCM_handle, L"TestService", L"TestService", SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, Path, NULL, NULL, NULL, NULL, NULL ); if (service_handle == NULL) { break; } result = ServiceStart(service_handle); if (result == FALSE) { break; } result = TRUE; } while (0); if (SCM_handle != NULL) { CloseServiceHandle(SCM_handle); } if (service_handle != NULL) { CloseServiceHandle(service_handle); } return result; } VOID WINAPI ServiceControlHandler(DWORD parameter) { service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; service_status.dwWin32ExitCode = 0; service_status.dwServiceSpecificExitCode = 0; service_status.dwCheckPoint = 0; service_status.dwWaitHint = 0; if (parameter == SERVICE_CONTROL_STOP) { service_status.dwCurrentState = SERVICE_STOPPED; } SetServiceStatus(service_status_handle, &service_status); return; } VOID WINAPI ServiceMain(DWORD argc, LPWSTR* argv) { service_status_handle = RegisterServiceCtrlHandlerW(L"TestService", (LPHANDLER_FUNCTION)ServiceControlHandler); service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; service_status.dwWin32ExitCode = 0; service_status.dwServiceSpecificExitCode = 0; service_status.dwCheckPoint = 0; service_status.dwWaitHint = 0; SetServiceStatus(service_status_handle, &service_status); while (1) { Sleep(10000); OutputDebugStringA("TestService Running"); } return; } int main(int argc, char* argv[]) { BOOL result = 0; HANDLE event_handle = NULL; DWORD pid = 0; DWORD ppid = 0; WCHAR process_name[MAX_PATH]; int wcscmp_return; SERVICE_TABLE_ENTRY service_table[2]; SecureZeroMemory(process_name, sizeof(process_name)); do { event_handle = CreateEventW(NULL, FALSE, TRUE, L"Global\\Test"); if (event_handle == NULL) { break; } pid = GetCurrentProcessId(); result = GetProcessInfoWithPID(pid, &ppid, process_name); if (result == FALSE) { break; } result = GetProcessInfoWithPID(ppid, 0, process_name); if (result == FALSE) { break; } wcscmp_return = lstrcmpiW(process_name, L"services.exe"); if (wcscmp_return != 0) { if (GetLastError() == ERROR_ALREADY_EXISTS) { break; } result = InstallService(); if (result == 0) { break; } break; } service_table[0].lpServiceName = L"TestService"; service_table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain; service_table[1].lpServiceName = NULL; service_table[1].lpServiceProc = NULL; result = StartServiceCtrlDispatcherW(service_table); if (result == 0) { break; } } while (0); if (event_handle != NULL) { CloseHandle(event_handle); } return 0; }
获取进程pid,ppid以及进程名
获取pid:
pid = GetCurrentProcessId();
获取ppid和进程名:
BOOL GetProcessInfoWithPID(DWORD pid, DWORD* ppid, WCHAR* process_name) { BOOL result = FALSE; HANDLE snapshot_handle = NULL; PROCESSENTRY32W process_entry32; BOOL process_return = FALSE; do { snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot_handle == INVALID_HANDLE_VALUE) { break; } process_entry32.dwSize = sizeof(process_entry32); process_return = Process32FirstW(snapshot_handle, &process_entry32); while (process_return) { if (process_entry32.th32ProcessID == pid) { if (ppid != NULL) { *ppid = process_entry32.th32ParentProcessID; } if (process_name != NULL) { lstrcpyW(process_name, process_entry32.szExeFile); } result = TRUE; break; } process_entry32.dwSize = sizeof(process_entry32); process_return = Process32NextW(snapshot_handle, &process_entry32); } } while (0); if (snapshot_handle) { CloseHandle(snapshot_handle); } return result; }
该函数的第二个或第三个参数如不需要可以填零。
创建事件CreateEventW
函数原型:
CreateEventW( _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, _In_ BOOL bManualReset, _In_ BOOL bInitialState, _In_opt_ LPCWSTR lpName );
参数1:安全描述符
参数2:手动复位
TRUE为手动,FALSE为自动。影响等待线程释放后,线程是手动还是自动的回到复位状态(即无信号状态)。
参数3:初始状态
TRUE为置位(有信号),FALSE为复位(无信号)。
参数4:事件对象名称
可设置一个最长为MAX_PATH的时间对象名称。
可以选择本地或全局(Local或Global),
如:CreateEvent( NULL, FALSE, FALSE, “Global\\CSAPP” );
利用全局内核对象命名空间可以做到进程间的同步。
也可以活用此功能避免运行重复的进程。
返回值:
如果函数成功,则返回值是事件对象的句柄。如果命名事件对象在函数调用之前存在,则该函数返回现有对象的句柄,而 GetLastError返回 ERROR_ALREADY_EXISTS。
如果函数失败,则返回值为NULL。要获取扩展的错误信息,请调用 GetLastError。
事件(Event)
多用于线程同步。
可能用到的函数:
1.CreateEventW
HANDLE CreateEventW( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName );
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventw
2.WaitForSingleObject
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject
3.SetEvent
BOOL SetEvent( HANDLE hEvent );
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setevent
4.ResetEvent
BOOL ResetEvent( HANDLE hEvent );
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-resetevent
5.SetEventWhenCallbackReturns
void SetEventWhenCallbackReturns( PTP_CALLBACK_INSTANCE pci, HANDLE evt );
C语言控制台程序编写Windows服务(一)
服务的概念:
Microsoft Windows 服务(过去称为 NT 服务)允许用户创建可在其自身的 Windows 会话中长时间运行的可执行应用程序。 这些服务可在计算机启动时自动启动,可以暂停和重启,并且不显示任何用户界面。
注:所以一个正常运行的服务程序是不应该执行完就退出的。应该在所有进程中的所有服务均终止时才返回退出。
目标:
编写一个服务,能够正常添加到Windows服务列表并能随系统启动,可正常手动启停。暂时不需要通过服务实现任何实际的功能。
组成部分:
使用C语言通过WindowsAPI编写一个可用的服务至少要包括以下几部分内容:
1. 服务程序的入口点
2. 服务程序的主体ServiceMain函数
3. 服务程序的控制处理功能函数
以上三者缺一不可,否则将无法正常启动。
具体步骤:
1.创建服务
该部分可选,如果忽略创建服务部分可使用手动方式添加服务。
2.编写入口点
第一个重要组成部分,必不可少。
SERVICE_TABLE_ENTRY service_table[2]; //为可以在调用过程中运行的服务指定ServiceMain函数 service_table[0].lpServiceName = L"TestService"; //此服务在进程中运行的名称 service_table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain; //指向ServiceMain函数的指针 service_table[1].lpServiceName = NULL; //表中最后一个条目的成员必须具有NULL值才能指定表的末尾 service_table[1].lpServiceProc = NULL; result = StartServiceCtrlDispatcherW(service_table); //将服务进程的主线程连接到服务控制管理器,该服务控制管理器使该线程成为调用过程的服务控制调度程序线程 if (result == 0) { printf("StartServiceCtrlDispatcherW error: %d\n", GetLastError()); break; }
3.编写ServiceMain函数
第二个重要组成部分,必不可少。
VOID WINAPI ServiceMain(DWORD argc, LPWSTR* argv) { service_status_handle = RegisterServiceCtrlHandlerW(L"TestService", (LPHANDLER_FUNCTION)ServiceControlHandler); //参数2为函数指针,该指针指向的函数定义了如何处理服务控制管理器发来的控制请求 //填充SERVICE_STATUS结构体,该结构体为全局变量 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; //如果有需要初始化的内容,此处可以先填SERVICE_START_PENDING,待初始化完成后将值改为SERVICE_RUNNING service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; service_status.dwWin32ExitCode = 0; service_status.dwServiceSpecificExitCode = 0; service_status.dwCheckPoint = 0; service_status.dwWaitHint = 0; SetServiceStatus(service_status_handle, &service_status); //该函数用以更新服务控制管理器的的状态信息 return;
4.编写控制处理程序功能(即前面代码参数2对应的函数)
第三个重要组成部分,必不可少。该函数用于接收服务控制管理器(SCM)发来的指令,然后根据指令让服务做出相应的状态更新。如下面代码,若不增加对停止服务指令的状态更新,则服务无法正常停止。
VOID WINAPI ServiceControlHandler(DWORD parameter) //该函数的参数即服务控制管理器传入的控制参数 { service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; service_status.dwWin32ExitCode = 0; service_status.dwServiceSpecificExitCode = 0; service_status.dwCheckPoint = 0; service_status.dwWaitHint = 0; if (parameter == SERVICE_CONTROL_STOP) //根据参数内容更新对应的服务状态 { service_status.dwCurrentState = SERVICE_STOPPED; } SetServiceStatus(service_status_handle, &service_status); return; }
包含以上内容就完成了作为一个最基本的Windows服务。
以下为该示例完整代码:
#include <stdio.h> #include <Windows.h> SERVICE_STATUS service_status; SERVICE_STATUS_HANDLE service_status_handle; BOOL InstallService() { BOOL result = FALSE; SC_HANDLE SCM_handle = NULL; SC_HANDLE service_handle = NULL; DWORD return_value = 0; TCHAR Path[MAX_PATH]; HANDLE file_handle = NULL; char error[64]; DWORD bytes_written = 0; DWORD last_error = 0; SecureZeroMemory(error, sizeof(error)); do { return_value = GetModuleFileNameW(NULL, Path, MAX_PATH); if (return_value == 0) { printf("GetModuleFileName error: %d\n", GetLastError()); break; } SCM_handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (SCM_handle == NULL) { printf("OpenSCManagerW error: %d\n", GetLastError()); break; } service_handle = OpenServiceW(SCM_handle, L"TestService", SC_MANAGER_ALL_ACCESS); if (service_handle != NULL) { printf("Test Service Created\n"); } else { service_handle = CreateServiceW( SCM_handle, L"TestService", L"TestService", SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, Path, NULL, NULL, NULL, NULL, NULL ); if (service_handle == NULL) { printf("CreateServiceW error: %d\n", GetLastError()); break; } } result = TRUE; } while (0); if (SCM_handle != NULL) { CloseServiceHandle(SCM_handle); } if (service_handle != NULL) { CloseServiceHandle(service_handle); } return result; } VOID WINAPI ServiceControlHandler(DWORD parameter) { service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; service_status.dwWin32ExitCode = 0; service_status.dwServiceSpecificExitCode = 0; service_status.dwCheckPoint = 0; service_status.dwWaitHint = 0; if (parameter == SERVICE_CONTROL_STOP) { service_status.dwCurrentState = SERVICE_STOPPED; } SetServiceStatus(service_status_handle, &service_status); return; } VOID WINAPI ServiceMain(DWORD argc, LPWSTR* argv) { service_status_handle = RegisterServiceCtrlHandlerW(L"TestService", (LPHANDLER_FUNCTION)ServiceControlHandler); service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; service_status.dwWin32ExitCode = 0; service_status.dwServiceSpecificExitCode = 0; service_status.dwCheckPoint = 0; service_status.dwWaitHint = 0; SetServiceStatus(service_status_handle, &service_status); return; } int main(int argc, char* argv[]) { BOOL result = 0; HANDLE file_handle = NULL; DWORD last_error = 0; do { result = InstallService(); if (result == 0) { printf("InstallService faild.\n"); break; } SERVICE_TABLE_ENTRY service_table[2]; service_table[0].lpServiceName = L"TestService"; service_table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain; service_table[1].lpServiceName = NULL; service_table[1].lpServiceProc = NULL; result = StartServiceCtrlDispatcherW(service_table); if (result == 0) { printf("StartServiceCtrlDispatcherW error: %d\n", GetLastError()); break; } } while (0); return 0; }
ANSI与Unicode
- C语言中用char数据类型来表示一个8位ANSI字符。
- MicroSoft的C/C++编译器定义了一个内建的数据类型wchar_t,它表示一个16位的Unicode(UTF-16)字符。
- 自Windows NT起,Windows的所有版本都完全用Unicode来构建。也就是说,所有核心函数(创建窗口、显示文本、进行字符串处理等等)都需要Unicode字符串。
- 调用Windows函数时,如果向它传入一个ANSI字符串,那么函数首先会把字符串转换为Unicode,再把结果传给操作系统。这会让系统产生时间和内存上的开销。
- Windows API中的一些函数(如WinExee和OpenFile)存在的唯一目的就是为了兼容16位Windows程序,因为其只支持ANSI字符串。所以在开发新程序是应避免使用这些函数。在使用WinExee和OpenFile调用的地方,应该用CreateProcess和CreateFile函数调用代替。
- 在C运行库中,strlen就是一个能返回ANSI字符串长度的函数。与之对应的是wcslen,这个C运行库函数能返回Unicode字符串的长度。
枚举服务状态EnumServicesStatusExW
在指定的服务控制管理器数据库中枚举服务。
函数原型:
BOOL
WINAPI
EnumServicesStatusExW(
_In_ SC_HANDLE hSCManager,
参数1:服务控制管理器数据库的句柄。该句柄由OpenSCManager函数返回 ,并且必须具有SC_MANAGER_ENUMERATE_SERVICE访问权限。
_In_ SC_ENUM_TYPE InfoLevel,
参数2:要返回的服务属性。使用SC_ENUM_PROCESS_INFO检索数据库中每个服务的名称和服务状态信息。所述lpServices参数是一个指向接收数组的缓冲器 ENUM_SERVICE_STATUS_PROCESS结构。缓冲区必须足够大以容纳结构及其成员指向的字符串。
_In_ DWORD dwServiceType,
参数3:
要枚举的服务类型。此参数可以是以下值中的一个或多个。
价值 | 意义 |
---|---|
|
类型为SERVICE_KERNEL_DRIVER和SERVICE_FILE_SYSTEM_DRIVER的服务。 |
|
文件系统驱动程序服务。 |
|
驱动服务。 |
|
类型为SERVICE_WIN32_OWN_PROCESS和SERVICE_WIN32_SHARE_PROCESS的服务。 |
|
在自己的进程中运行的服务。 |
|
与一个或多个其他服务共享一个流程的服务。有关更多信息,请参见服务程序。 |
_In_ DWORD dwServiceState,
参数4:
要枚举的服务状态。此参数可以是以下值之一。
价值 | 意义 |
---|---|
|
枚举处于以下状态的服务:SERVICE_START_PENDING,SERVICE_STOP_PENDING,SERVICE_RUNNING,SERVICE_CONTINUE_PENDING,SERVICE_PAUSE_PENDING和SERVICE_PAUSED。 |
|
枚举处于SERVICE_STOPPED状态的服务。 |
|
合并SERVICE_ACTIVE和SERVICE_INACTIVE状态。 |
_Out_writes_bytes_opt_(cbBufSize) LPBYTE lpServices,
参数5:指向接收状态信息的缓冲区的指针。该数据的格式取决于InfoLevel参数的值。该数组的最大大小为256K字节。若要确定所需的大小,请为此参数指定NULL,为cbBufSize参数指定0 。该函数将失败,并且GetLastError将返回ERROR_MORE_DATA。该pcbBytesNeeded参数将接收到所需的大小。
_In_ DWORD cbBufSize,
参数6:lpServices参数指向的缓冲区大小,以字节为单位。
_Out_ LPDWORD pcbBytesNeeded,
参数7:如果缓冲区太小,则指向变量的指针,该变量接收返回剩余服务条目所需的字节数。
_Out_ LPDWORD lpServicesReturned,
参数8:指向一个变量的指针,该变量接收返回的服务条目数。
_Inout_opt_ LPDWORD lpResumeHandle,
参数9:指向变量的指针,该变量在输入时指定枚举的起点。您必须在第一次调用EnumServicesStatusEx函数时将此值设置为零 。在输出中,如果函数成功,则该值为零。但是,如果函数返回零,并且 GetLastError函数返回ERROR_MORE_DATA,则此值指示调用EnumServicesStatusEx函数以检索其他数据时要读取的下一个服务条目 。
_In_opt_ LPCWSTR pszGroupName
参数10:加载顺序组名称。如果此参数是字符串,则枚举的唯一服务是属于具有由字符串指定的名称的组的服务。如果此参数为空字符串,则仅枚举不属于任何组的服务。如果此参数为NULL,则将忽略组成员身份,并枚举所有服务。
);
示例:
#include <stdio.h> #include <Windows.h> #include <locale.h> int main(int argc, char* argv[]) { HANDLE handle_service_manager = 0; int return_value = 0; unsigned char* service_status_process = NULL; DWORD buffer_size = 0; DWORD bytes_needed = 0; DWORD services_returned = 0; ENUM_SERVICE_STATUS_PROCESS* enmu_service_status_process; setlocale(LC_ALL, "chs"); do { handle_service_manager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (handle_service_manager == NULL) { printf("OpenSCManager Error:%d\n", GetLastError()); break; } return_value = EnumServicesStatusExW( handle_service_manager, //服务控制管理器数据库的句柄。 SC_ENUM_PROCESS_INFO, //要返回的服务属性。 SERVICE_WIN32, //要枚举的服务类型。 SERVICE_STATE_ALL, //要枚举的服务状态。 NULL, //指向接收状态信息的缓冲区的指针。该数据的格式取决于InfoLevel参数的值。 buffer_size, //lpServices参数指向的缓冲区大小,以字节为单位。 &bytes_needed, //如果缓冲区太小,则指向变量的指针,该变量接收返回剩余服务条目所需的字节数。 &services_returned, //指向一个变量的指针,该变量接收返回的服务条目数。 0, //指向变量的指针,该变量在输入时指定枚举的起点。您必须在第一次调用EnumServicesStatusEx函数时将此值设置为零。 NULL //加载顺序组名称。 ); if (return_value == 0 && GetLastError() != ERROR_MORE_DATA) { printf("get bytes needed faild\n"); break; } printf("bytes_needed:%d\n", bytes_needed); buffer_size = bytes_needed; service_status_process = LocalAlloc(LPTR, buffer_size); if (service_status_process == NULL) { break; } SecureZeroMemory(service_status_process, buffer_size); return_value = EnumServicesStatusExW( handle_service_manager, //服务控制管理器数据库的句柄。 SC_ENUM_PROCESS_INFO, //要返回的服务属性。 SERVICE_WIN32, //要枚举的服务类型。 SERVICE_STATE_ALL, //要枚举的服务状态。 service_status_process, //指向接收状态信息的缓冲区的指针。该数据的格式取决于InfoLevel参数的值。 buffer_size, //lpServices参数指向的缓冲区大小,以字节为单位。 &bytes_needed, //如果缓冲区太小,则指向变量的指针,该变量接收返回剩余服务条目所需的字节数。 &services_returned, //指向一个变量的指针,该变量接收返回的服务条目数。 0, //指向变量的指针,该变量在输入时指定枚举的起点。您必须在第一次调用EnumServicesStatusEx函数时将此值设置为零。 NULL //加载顺序组名称。 ); if (return_value == 0) { printf("EnumServicesStatusExW2 faild:%d\n", GetLastError()); break; } printf("services returned:%d\n\n", services_returned); enmu_service_status_process = (ENUM_SERVICE_STATUS_PROCESS*)service_status_process; for (size_t i = 0; i < services_returned; i++) { printf("ServiceName:%ws\n", enmu_service_status_process[i].lpServiceName); printf("DisplayName:%ws\n", enmu_service_status_process[i].lpDisplayName); printf("ProcessId:%d\n", enmu_service_status_process[i].ServiceStatusProcess.dwProcessId); printf("CurrentState:%d\n\n", enmu_service_status_process[i].ServiceStatusProcess.dwCurrentState); } } while (0); return 0; }
注:遍历多个相同的结构体时可以把结构体看做数组,以数组下标的方式表示。
https://docs.microsoft.com/zh-cn/windows/win32/api/winsvc/nf-winsvc-enumservicesstatusexw