سرویس برنامه ای است که که فاقد گرافیک و واسط کاربری میباشد و برنامه ای است که میتواند بصورت لحظه ای یا دائمی ، از قبل از ورود کابر به ویندوز یا در هر لحظه ای فعال و اجرا گردد.

برای شروع چند ساختمان مورد استفاده در آن را بر رسی میکنیم .

ساختمان 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;
}