سرویس برنامه ای است که که فاقد گرافیک و واسط کاربری میباشد و برنامه ای است که میتواند بصورت لحظه ای یا دائمی ، از قبل از ورود کابر به ویندوز یا در هر لحظه ای فعال و اجرا گردد.
برای شروع چند ساختمان مورد استفاده در آن را بر رسی میکنیم .
ساختمان SERVICE_TABLE_ENTRY:
برای راه اندازی سرویس ، مایکروسافت یک ساختمان دادی ای تشکیل داده که باید آن را پر کنیم الگوی آن به صورت زیر است
typedef struct _SERVICE_TABLE_ENTRY { LPTSTR lpServiceName; LPSERVICE_MAIN_FUNCTION lpServiceProc; } SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;
داده اول یک استرینگ است که شامل نام سرویس میباشد ، داده دوم نیز باید با تابعی مقدار دهی شود که تابع اصلی سرویس میباشد(در مثال ارائه شده با آن بیشتر آشنا میشویم.
ساختمان SERVICE_STATUS :
این ساختمان باید با اطلاعات و وضعیت سرویس پر شود .
typedef struct _SERVICE_STATUS { DWORD dwServiceType; DWORD dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD dwServiceSpecificExitCode; DWORD dwCheckPoint; DWORD dwWaitHint; } SERVICE_STATUS, *LPSERVICE_STATUS;
اولین داده نوع سرویس رو مشخص میکند که فلگ های زیر رو میپذیرد .
آرگمان دوم وضعیت فعلی سرویس را معین میکند که فلگ های زیر را میپذیرد.
ساختمان SERVICE_STATUS_HANDLE :
این نوع ساختمان داده در سایت msdn مستند سازی نشده (اگر غیر از این حالت است ممنون میشم تو کامنت ها اشاره ای بهش بکنید) . از شواهد موجود، مقدار بازگشتی هندل یک سرویس را در خود نگهداری میکند تا با استفاده از آن برای عیب یابی و جلوگیری از خطا سرویس از آن استفاده کنیم .
معرفی مختصر چند api :
StartServiceCtrlDispatcher:
ساختمان داده ای از نوع SERVICE_TABLE_ENTRY رو در آرگمان خود دریافت میکند که شامل اطلاعات نام سرویس و تابع اصلی سرویس میباشد و آن را راه اندازیی میکند.این تابع الگوی زیر را دارد .
BOOL WINAPI StartServiceCtrlDispatcher(
_In_ const SERVICE_TABLE_ENTRY *lpServiceTable
);
درصورت موفقیت مقداری غیر صفر را بازمیگرداند ، و در صورت عدم موفقیت مقدار 0 را برمیگرداند .
RegisterServiceCtrlHandler:
SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandler( _In_ LPCTSTR lpServiceName, _In_ LPHANDLER_FUNCTION lpHandlerProc );
آرگمان اول شامل نام سرویس میباشد.
آرگمان دوم شامل اشاره گری به تابع handler میباشد.
OpenSCManager: این تابع یک هندلی را باز میگرداند که به پایگاه داده مدیر کنترل سرویس متصل میباشد .
SC_HANDLE WINAPI OpenSCManager( _In_opt_ LPCTSTR lpMachineName, _In_opt_ LPCTSTR lpDatabaseName, _In_ DWORD dwDesiredAccess );
آرگمان اول نام کامپیوتر مورد نظر است . اگر نال مقدار دهی شود کامپیوتر محلی مورد نظر قرار میگیرد .
آرگمان دوم نام دیتا بیس سرویس کنترل منیجر میباشد ، که باید با SERVICES_ACTIVE_DATABASE مقدار دهی شود . اگر مقدار آن نال باشد بصورد پسشفرض SERVICES_ACTIVE_DATABASE باز میگردد .
آرگمان سوم سطح دسترسی آن را مشخص میکنم
CreateService: این تابع در صورت موفق بودن هندلی را به یک سرویس باز میگرداند
SC_HANDLE WINAPI CreateService( _In_ SC_HANDLE hSCManager, _In_ LPCTSTR lpServiceName, _In_opt_ LPCTSTR lpDisplayName, _In_ DWORD dwDesiredAccess, _In_ DWORD dwServiceType, _In_ DWORD dwStartType, _In_ DWORD dwErrorControl, _In_opt_ LPCTSTR lpBinaryPathName, _In_opt_ LPCTSTR lpLoadOrderGroup, _Out_opt_ LPDWORD lpdwTagId, _In_opt_ LPCTSTR lpDependencies, _In_opt_ LPCTSTR lpServiceStartName, _In_opt_ LPCTSTR lpPassword );
آرگمان اول شامل هندلی میباشد که به دیتا بیس کنترل سرویس منیجر دسترسی دارد.
آرگمان دوم شامل نام سرویس است که نباید بیش هز 256 کاراکتر باشد . کاراکتر های \ و / در آن غیر مجاز است.
آرگمان سوم نامی است که برنامه های ui آن را میبینند. و حداکثر دارای 256 کاراکتر میباشد.
آرگمان پنجم نوع سرویس را مشخص میکند .
آرگمان ششوم وضعیت استارت سرویس را مشخص میکند .
آرگمان هشتم مسیر فایل روی هارد دیسک سرویس را میگیرد .
OpenService : سرویس های موجود را باز میکند و یک هندل از آن را باز میگرداند
SC_HANDLE WINAPI OpenService( _In_ SC_HANDLE hSCManager, _In_ LPCTSTR lpServiceName, _In_ DWORD dwDesiredAccess );
آرگمان اول هند گرفته شده از دیتابیس سرویس کنتلرل منیجر میگیرد .
آرگمان دوم نام سرویس را میگیرد .
آرگمان سوم سطح دسطرسی را دریافت میکند .
ControlService:این تابع کدی را به سرویس جهت کنترل کردن آن میفرستد .
BOOL WINAPI ControlService( _In_ SC_HANDLE hService, _In_ DWORD dwControl, _Out_ LPSERVICE_STATUS lpServiceStatus );
آرگمان اول یک هند از سرویس مورد نظر مارا میگیرد .
آرگمان دوم میتواند فلگ های زیر را بگیرد .
آرگمان سوم وضعیت فعلی سرویس را دریافت میکند.
در صورت موفقیت مغداری غیر صفر بازمیگرداند و در صورت شکست مقدار صفر را بازمیگرداند
DeleteService: سرویس مورد نظر را از دیتابیس سرویس کنترل منیجر پاک میکند
BOOL WINAPI DeleteService( _In_ SC_HANDLE hService );
آرگمان اول هندل سرویس مورد نظر را دریافت میکند .
برنامه های نمونه :
سرویس های زیر را میتوان با cmd نیز اجرا کرد برای این کار باید از دو دستور sc و net استفاده کرد.
دو مدل نوشتن و اینستال و استارت را نمایش میدهیم . مدل اول خود شامل دو برنامه است . برنامه اول که سرویس مورد نظر ماست و برنامه دوم که وظیفه اینستال و استارت و سپس استاپ و آن اینستال برنامه را بر عهده دارد . برنامه لود کننده باید با دسترسی ادمین باشد .
برای تست کردن اینکه سرویس ران شده یا نه یک فایل تمپ در درایو d شما میسازد .
#include <Windows.h>
#include <Winsvc.h>
#include <time.h>
#include <iostream>
#include <fstream>
using namespace std;
SERVICE_STATUS m_ServiceStatus;
SERVICE_STATUS_HANDLE m_ServiceStatusHandle;
BOOL bRunning = true;
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
void WINAPI ServiceCtrlHandler(DWORD Opcode);
int main(int argc, char* argv[])
{
SERVICE_TABLE_ENTRY DispatchTable[] =
{ { "Service1",ServiceMain },{ NULL,NULL } };
StartServiceCtrlDispatcher(DispatchTable);
return 0;
}
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
DWORD status;
DWORD specificError;
m_ServiceStatus.dwServiceType = SERVICE_WIN32;
m_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
m_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
m_ServiceStatus.dwWin32ExitCode = 0;
m_ServiceStatus.dwServiceSpecificExitCode = 0;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
m_ServiceStatusHandle = RegisterServiceCtrlHandler
("Service1",ServiceCtrlHandler);
if (m_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)0)
{
return;
}
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
if (!SetServiceStatus(m_ServiceStatusHandle, &m_ServiceStatus))
{
}
fstream temp("d:\\tempSrv.txt",ios::out);
temp << "code Tutorial";
temp.close();
bRunning = true;
while (bRunning)
{
Sleep(3000);
//Place Your Code for processing here....
}
return;
}
void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
switch (Opcode)
{
case SERVICE_CONTROL_PAUSE:
m_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
m_ServiceStatus.dwWin32ExitCode = 0;
m_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
SetServiceStatus(m_ServiceStatusHandle, &m_ServiceStatus);
bRunning = false;
break;
case SERVICE_CONTROL_INTERROGATE:
break;
}
return;
}
برنامه دوم وظیفه اینستال ... سرویس را دارد . به اینکه باید توجه داشت که اگر برنامه را با زدن یک کلید کیبورد قطع نکنید سرویس روی سیستم شما نصب میماند (مگر اینکه با دستورات cmd غیر فعال کنیدش)
#include<windows.h>
#include<iostream>
#include<conio.h>
#pragma comment(lib,"Advapi32.lib")
void main()
{
SC_HANDLE hman, hser;
DWORD dw;
SERVICE_STATUS ss;
hman = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hman)
{
hser = CreateService(hman, "Service1", "Name Needed",
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL,
"c:\\services.exe",NULL, NULL, NULL, NULL, NULL);
bool startSrv;
startSrv = StartService(hser, 0, NULL);
if (startSrv)
printf("\nService is runing");
else
printf("\ncant run");
printf("\nService Started");
printf("\nPress a key to end service...");
_getch();
ControlService(hser, SERVICE_CONTROL_STOP, &ss);
DeleteService(hser);
CloseServiceHandle(hser);
CloseServiceHandle(hman);
}
}
____
در مدل دوم عمل اینستال (رجیستر) ، استارت ، استاپ و آن اینستال سرویس درون خود سرویس و با api های سیستم انجام میشود . و برنامه برای اینستال (رجیستر) ، استارت ، استاپ و آن اینستال باید در cmd با دسترسی ادمین اجرا گردد و همراه با آن آرگمان های زیر را به آن داده تا بتواندن وظعیت خود را شناسایی و عمل کند .
services.exe -i برای رجیستر کردن سرویس میباشد .
services.exe -start برای استارت کردن سرویس میباشد .
services.exe -stop برای متوقف کردن سرویس میباشد .
services.exe -u برای آن اینستال کردن سرویس میباشد .
سورس سرویس با نام services :
#include <Windows.h>
#include <Winsvc.h>
#include <iostream>
#include <fstream>
using namespace std;
SERVICE_STATUS m_ServiceStatus;
SERVICE_STATUS_HANDLE m_ServiceStatusHandle;
BOOL bRunning = true;
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
void WINAPI ServiceCtrlHandler(DWORD Opcode);
BOOL InstallService();
BOOL DeleteService();
BOOL StartingService();
BOOL StoppingService();
int main(int argc, char* argv[])
{
if (argc>1)
{
if (strcmp(argv[1], "-i") == 0)
{
if (InstallService())
printf("\n\nService Installed Sucessfully\n");
else
printf("\n\nError Installing Service\n");
}
if (strcmp(argv[1], "-d") == 0)
{
if (DeleteService())
printf("\n\nService UnInstalled Sucessfully\n");
else
printf("\n\nError UnInstalling Service\n");
}
if (strcmp(argv[1], "-start") == 0)
{
if (StartingService())
printf("\n\nService Start Sucessfully\n");
else
printf("\n\nError Start Service\n");
}
if (strcmp(argv[1], "-stop") == 0)
{
if (StoppingService())
printf("\n\nService Stop Sucessfully\n");
else
printf("\n\nError Stop Service\n");
}
else
{
printf("\n\nUnknown Switch Usage\n\nFor Install use services.exe -i
\n\nFor UnInstall use services.exe -d
\n\nFor Start use services.exe -start
\n\nFor Stop use services.exe -stop");
}
}
else
{
SERVICE_TABLE_ENTRY DispatchTable[] =
{ { "Service1",ServiceMain },{ NULL,NULL } };
StartServiceCtrlDispatcher(DispatchTable);
}
return 0;
}
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
DWORD status;
DWORD specificError;
m_ServiceStatus.dwServiceType = SERVICE_WIN32;
m_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
m_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
m_ServiceStatus.dwWin32ExitCode = 0;
m_ServiceStatus.dwServiceSpecificExitCode = 0;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
m_ServiceStatusHandle = RegisterServiceCtrlHandler
("Service1",ServiceCtrlHandler);
if (m_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)0)
{
return;
}
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
if (!SetServiceStatus(m_ServiceStatusHandle, &m_ServiceStatus))
{
}
fstream temp("d:\\tempSrv.txt",ios::out);
temp << "hi";
temp.close();
bRunning = true;
while (bRunning)
{
Sleep(3000);
//Place Your Code for processing here....
}
return;
}
void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
switch (Opcode)
{
case SERVICE_CONTROL_PAUSE:
m_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
m_ServiceStatus.dwWin32ExitCode = 0;
m_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
SetServiceStatus(m_ServiceStatusHandle, &m_ServiceStatus);
bRunning = false;
break;
case SERVICE_CONTROL_INTERROGATE:
break;
}
return;
}
BOOL InstallService()
{
char strDir[1024];
SC_HANDLE schSCManager, schService;
GetCurrentDirectory(1024, strDir);
strcat_s(strDir, "\\services.exe");
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (schSCManager == NULL)
return false;
LPCTSTR lpszBinaryPathName = strDir;
schService = CreateService(schSCManager, "Service1",
"The Display Name Needed", // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_DEMAND_START, // start type
SERVICE_ERROR_NORMAL, // error control type
lpszBinaryPathName, // service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if (schService == NULL)
return false;
CloseServiceHandle(schService);
return true;
}
BOOL StartingService()
{
SERVICE_STATUS_PROCESS ssStatus;
DWORD dwBytesNeeded;
SC_HANDLE schSCManager, schService;
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // servicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
printf("OpenSCManager failed (%d)\n", GetLastError());
return false;
}
// Get a handle to the service.
schService = OpenService(
schSCManager, // SCM database
"Service1", // name of service
SERVICE_ALL_ACCESS); // full access
if (schService == NULL)
{
printf("OpenService failed (%d)\n", GetLastError());
CloseServiceHandle(schSCManager);
return false;
}
// Check the status in case the service is not stopped.
if (!QueryServiceStatusEx(
schService, // handle to service
SC_STATUS_PROCESS_INFO, // information level
(LPBYTE)&ssStatus, // address of structure
sizeof(SERVICE_STATUS_PROCESS), // size of structure
&dwBytesNeeded)) // size needed if buffer is too small
{
printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return false;
}
// Check if the service is already running. It would be possible
// to stop the service here, but for simplicity this example just returns.
if (ssStatus.dwCurrentState != SERVICE_STOPPED
&& ssStatus.dwCurrentState != SERVICE_STOP_PENDING)
{
printf("Cannot start the service because it is already running\n");
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return false;
}
// Attempt to start the service.
if (!StartService(
schService, // handle to service
0, // number of arguments
NULL)) // no arguments
{
printf("StartService failed (%d)\n", GetLastError());
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return false;
}
else printf("Service start pending...\n");
// Check the status until the service is no longer start pending.
if (!QueryServiceStatusEx(
schService, // handle to service
SC_STATUS_PROCESS_INFO, // info level
(LPBYTE)&ssStatus, // address of structure
sizeof(SERVICE_STATUS_PROCESS), // size of structure
&dwBytesNeeded)) // if buffer too small
{
printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return false;
}
// Determine whether the service is running.
if (ssStatus.dwCurrentState == SERVICE_RUNNING)
{
printf("Service started successfully.\n");
}
else
{
printf("Service not started. \n");
printf(" Current State: %d\n", ssStatus.dwCurrentState);
printf(" Exit Code: %d\n", ssStatus.dwWin32ExitCode);
printf(" Check Point: %d\n", ssStatus.dwCheckPoint);
printf(" Wait Hint: %d\n", ssStatus.dwWaitHint);
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return true;
}
BOOL StoppingService()
{
SC_HANDLE schSCManager, schService;
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
printf("OpenSCManager failed (%d)\n", GetLastError());
return true;
}
// Get a handle to the service.
schService = OpenService(
schSCManager, // SCM database
"Service1", // name of service
SERVICE_STOP |
SERVICE_QUERY_STATUS |
SERVICE_ENUMERATE_DEPENDENTS);
if (schService == NULL)
{
printf("OpenService failed (%d)\n", GetLastError());
CloseServiceHandle(schSCManager);
return false;
}
if (m_ServiceStatus.dwCurrentState == SERVICE_STOPPED)
{
printf("Service is already stopped.\n");
CloseServiceHandle(schSCManager);
return true;
}
// Send a stop code to the service.
if (!ControlService(schService,SERVICE_CONTROL_STOP,&m_ServiceStatus))
{
printf("ControlService failed (%d)\n", GetLastError());
CloseServiceHandle(schSCManager);
return false;
}
printf("Service stopped successfully\n");
CloseServiceHandle(schSCManager);
return true;
}
BOOL DeleteService()
{
SC_HANDLE schSCManager;
SC_HANDLE hService;
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (schSCManager == NULL)
return false;
hService = OpenService(schSCManager, "Service1", SERVICE_ALL_ACCESS);
if (hService == NULL)
return false;
if (DeleteService(hService) == 0)
return false;
if (CloseServiceHandle(hService) == 0)
return false;
return true;
}