0.98M
Категория: ПрограммированиеПрограммирование

Работа с сервисами в Windows. Тема 6.2

1.

Тема 6.2. Работа с
сервисами в Windows

2.

Открытие доступа к базе данных сервисов
• Для установки связи с менеджером сервисов и открытия доступа
к базе данных сервисов используется функция OpenSCManager,
которая имеет следующий прототип:
SC_HANDLE OpenSCManager( LPCTSTR lpMachineName, // имя
компьютера
LPCTSTR lpDatabaseName, // имя базы данных сервисов
DWORD dwDesiredAccess // тип доступа );
• В случае успешного завершения функция возвращает дескриптор
базы данных сервисов, а в случае неудачи — null.

3.

Открытие доступа к базе данных сервисов
Параметры функции OpenSCManager:
• Параметр lpMachineName должен содержать адрес строки с
именем компьютера, на котором находится база данных
сервисов.
• Имени компьютера должны предшествовать символы \\.
• Если этот параметр содержит null или указывает на пустую строку,
то предполагается, что база данных расположена на локальном
компьютере.

4.

Открытие доступа к базе данных сервисов
Параметры функции OpenSCManager:
• Параметр lpDatabaseName должен содержать адрес строки с
именем базы данных сервисов.
• Если этот параметр равен null, тo открывается активная база
данных сервисов.

5.

Открытие доступа к базе данных сервисов
Параметры функции OpenSCManager:
• Параметр dwDesiredAccess должен содержать тип доступа к базе
данных.
• Этот параметр может принимать любую комбинацию следующих
флагов:
• sc_manager_all_access — любой из нижеперечисленных доступов;
• sc_manager_connect — связь с менеджером сервисов;
• sc_manager_create_service — установка сервиса;
• sc_manager_enumerate_service — просмотр сервисов в базе данных;
• sc_manager_lock — запирание базы данных сервисов;
• sc_manager_query_lock_status — определение состояния базы данных.
• Флаг sc_manager_connect считается установленным по умолчанию, а
флаг sc_manager_all_access включает в себя флаг
STANDARD_RIGHTS_REQUIRED.

6.

Открытие доступа к базе данных сервисов
• Для использования в этом параметре также определены флаги,
обозначающие следующие родовые права доступа к базе данных
сервисов:
• generic_read — включает следующие флаги: standard_rights_read,
sc_manager_enumerate_service и sc_manager_query_lock_status;
• generic_write — включает флаги standard_rights_write и
sc_manager_create_service;
• generic_execute — включает следующие флаги: standard_rights_execute,
sc_manager_connect и sc_manager_lock.

7.

Открытие доступа к базе данных сервисов
• После окончания работы с менеджером сервисов или сервисом
нужно закрыть его дескриптор.
• Для этой цели предназначена функция CloseServiceHandle,
которая имеет следующий прототип:
BOOL CloseServiceHandle( SC_HANDLE hSCObject // дескриптор
менеджера сервисов или сервиса );
• В случае успешного завершения эта функция возвращает
ненулевое значение, а в случае неудачи — false.
• Единственным параметром этой функции является дескриптор
менеджера сервисов или дескриптор сервиса.

8.

Установка сервиса
• Для установки сервисов в базу данных используется функция createService, которая имеет
следующий прототип:
SC_HANDLE CreateService( SC_HANDLE hSCManager // дескриптор базы данных сервисов
LPCTSTR IpServiceName // внутреннее имя сервиса
LPCTSTR lpDisplayName // внешнее имя сервиса
DWORD dwDesiredAccess // разрешаемый доступ к сервису
DWORD dwServiceType // тип сервиса
DWORD dwStartType // способ запуска сервиса
DWORD dwErrorControl // режим обработки ошибок
LPCTSTR lpBinaryPathName // имя бинарного файла сервиса
LPCTSTR lpLoadOrderGroup // имя группы загрузки сервисов
LPDWORD lpdwTagld // тег сервиса в группе
LPCTSTR lpDependencies // массив имен сервисов
LPCTSTR IpServiceStartName // имя пользователя
LPCTSTR lpPassword // пароль пользователя
);

9.

Установка сервиса
• В случае успешного завершения функция возвращает дескриптор
установленного сервиса, а в случае неудачи — null.
• Параметр hscManager должен содержать дескриптор менеджера
сервисов, который был предварительно открыт функцией
OpenSCManager.

10.

Установка сервиса
• Параметр lpServiceName должен указывать на строку с именем
сервиса, которое используется менеджером сервисов.
• Имена сервисов не чувствительны к верхнему и нижнему
регистрам.
• Длина имени не может превышать 256 символов.
• Имя сервиса не может содержать символы / и \.
• Параметр lpDisplayName должен указывать на имя сервиса,
которое используется в панели управления Windows для ссылок
на сервис.
• В этом имени различаются верхний и нижний регистры, а его
длина не может превышать 256 символов.

11.

Установка сервиса
• В параметре dwDesiredAccess должны быть установлены флаги, которые определяют доступ
к сервису.
• Возможна любая комбинация следующих флагов:
delete — разрешено удаление сервиса;
service_change_config — разрешено изменять конфигурацию сервиса;
service_enumerate_dependents — разрешено просматривать зависимые сервисы;
service_interrogate — разрешено запрашивать состояние сервиса;
service_pause_continue — разрешено приостанавливать и возобновлять сервис;
service_query_config — разрешено запрашивать конфигурацию сервиса;
service_query_status — разрешено запрашивать состояние сервиса;
service_start — разрешено запускать сервис;
service_stop — разрешено останавливать сервис;
service_user_defined_control — разрешено использовать управляющие команды пользователя.
• Для обеспечения возможности работы с атрибутами защиты сервиса нужно установить
комбинированный флаг standard_rights_required — стандартные права доступа, который
также включает флаг delete.
• Также можно использовать флаг service_all_access — полный доступ, который включает
флаг standard_rights_required и все перечисленные выше флаги.

12.

Установка сервиса
• Дополнительно существуют следующие родовые права доступа,
которые можно установить в этом параметре:
• generic_read — включает флаги standard_rights_read, service_
query_config, service_query_status, service_interrogate и service_
enumerate_dependents;
• generic_write — включает флаги standard_rights_write и service_
change_config;
• generic_execute — включает флаги standard_rights_execute, service_start,
service_stop, service_pause_continue и service_user_defined_control.

13.

Установка сервиса
• В параметре dwServiceType задается тип сервиса.
• Этот параметр может принимать одно из следующих значений:
• service_win32_own_process — отдельный процесс;
• service_win32_share_process — разделяет процесс с другими сервисами;
• service_kernel_driver — драйвер ядра;
• service_file_system_driver — драйвер файловой системы.
• В случае если сервис не является драйвером, то может быть
также установлен флаг service_interactive_process —
интерактивный процесс, который разрешает сервису
взаимодействовать с рабочим столом.

14.

Установка сервиса
• В параметре dwstartType должен быть установлен режим запуска
сервиса.
• Этот параметр может принимать одно из следующих значений:
• service_boot_start — запуск драйвера при загрузке системы;
• service_system_start — запуск драйвера функцией ioinitsystem;
• service_auto_start — запуск сервиса менеджером сервисов при загрузке
системы;
• service_demand_start — запуск сервиса функцией startservice;
• service_disabled — сервис не может быть запущен.
• Первые два флага могут быть установлены только для драйверов.

15.

Установка сервиса
• В параметре dwErrorcontroi устанавливается режим обработки
ошибок, произошедших во время запуска сервиса, если этот сервис
запускается во время загрузки системы.
• В этом параметре должно быть установлено одно из следующих
значений:
• service_error_ignore — игнорировать ошибку;
• service_error_normal — игнорировать ошибку и сообщить об этом в окне
ошибок;
• service_error_severe — продолжить или перезагрузиться в безопасной
конфигурации;
• service_error_critical — прервать загрузку и перезагрузиться в безопасной
конфигурации.
• В последнем случае если ошибка произошла при загрузке системы в
безопасной конфигурации, то загрузка прекращается.

16.

Установка сервиса
• Параметр lpBinaryPathName должен указывать на имя исполняемого ехефайла, который содержит сервис.
• Рекомендуется, чтобы это имя заканчивалось символом \.
• Параметр lpLoadOrderGroup должен указывать на имя группы, содержащей
порядок загрузки сервисов и в которую входит сервис.
• Если сервис не зависит от других сервисов, то в этом параметре должно
быть установлено значение NULL.
• В параметре lpdwTagid должен быть задан адрес переменной типа dword, по
которому функция помещает тег сервиса в группе загрузки.
• Этот тег может в дальнейшем использоваться для определения порядка
запуска сервисов.
• Если тег не используется, то этот параметр нужно установить в null.
• При тег может использоваться только для драйверов, для которых задан
режим загрузки service_boot_start или service_system_start.

17.

Установка сервиса
• Параметр lpDependencies должен указывать на массив строк,
каждая из которых содержит имя сервиса или имя группы
сервисов, которые система должна запустить перед запуском
устанавливаемого сервиса.
• Если сервис не зависит от других сервисов, то в этом параметре
должно быть установлено значение null.
• Имя группы должно начинаться с префикса sc_group_identifier,
чтобы отличить это имя от имени сервиса.

18.

Установка сервиса
• Параметр lpServicestartName должен указывать на строку с именем
сервиса.
• Если сервис является процессом, то этот параметр должен указывать
на имя пользователя, под которым будет запущен сервис.
• Если сервис запускается под текущим локальным именем, то этот
параметр должен быть установлен в null.
• Сервис, взаимодействующий с рабочим столом, должен быть запущен
только под текущим именем пользователя.
• Параметр lpPassword должен указывать на строку с паролем
пользователя, под именем которого запускается сервис.
• Если сервис запускается под именем текущего пользователя, то в этом
параметре должно быть установлено значение null.

19.

20.

• После окончания работы с сервисом нужно закрыть его
дескриптор, используя для этого функцию CloseServiceHandle.
• Эта функция не уничтожает сервис, а только разрывает связь
прикладной программы с этим сервисом.
• Для удаления сервиса из базы данных нужно использовать
функцию DeleteService.

21.

Открытие доступа к сервису
• Для открытия доступа к уже установленному сервису
используется функция openservice, которая имеет следующий
прототип:
SC_HANDLE OpenService( SC_HANDLE hSCManager, // дескриптор
базы данных сервисов
LPCTSTR IpServiceName, // имя сервиса
DWORD dwDesiredAccess // требуемый доступ к сервису
);
• В случае успешного завершения функция возвращает дескриптор
сервиса, а в случае неудачи — null.

22.

Открытие доступа к сервису
• Параметр hscManager должен содержать дескриптор базы
данных сервисов, связь с которой была предварительно
установлена вызовом функции OpenSCManager.
• Параметр lpServiceName должен указывать на строку с именем
сервиса.
• В параметре dwDesiredAccess устанавливаются флаги доступа к
сервису.
• Флаги доступа, которые можно установить в этом параметре,
совпадают с флагами, которые можно установить в параметре
dwDesiredAccess функции createservice.

23.

Запуск сервиса
• Для запуска сервиса используется функция startservice, которая
имеет следующий прототип:
BOOL Startservice( SC_HANDLE hService, // дескриптор сервиса
DWORD dwNumServiceArgs, // количество аргументов
LPCTSTR *lpServiceArgVectors // массив аргументов
• В случае удачного завершения функция возвращает ненулевое
значение, а в случае неудачи — false.

24.

Запуск сервиса
• В параметре hservice должен быть установлен дескриптор сервиса,
который был предварительно получен функциями CreateService или
OpenService.
• Параметр dwNumServiceArgs должен содержать количество
аргументов, которые передаются сервису.
• Каждый из аргументов является символьной строкой. Если аргументов
нет, то в параметре должно быть установлено значение ноль.
• Параметр lpServiceArgVectors должен содержать адрес массива,
каждый элемент которого является указателем на строку.
• Эти строки и являются аргументами, которые передаются функции
сервиса serviceMain.
• Если аргументов нет, то этот параметр должен быть установлен в null.

25.

Запуск сервиса
• Во время работы функции startservice менеджер сервисов
устанавливает следующие состояния сервиса в структуре
service_status:
• в поле dwcurrentstate устанавливает значение service_start_pending;
• в поле dwControisAccepted устанавливает значение ноль, что запрещает
обработку управляющих команд;
• в поле dwcheckPoint устанавливает значение ноль;
• в поле dwWaitHint устанавливает значение, равное двум секундам.
• Во время работы функции startservice менеджер запросов
блокирует базу данных сервисов.

26.

Определение и изменение состояния
сервиса
• Для определения состояния сервиса используется функция
QueryServicestatus, которая имеет следующий прототип:
BOOL QueryServicestatus( SC_HANDLE hService, // дескриптор сервиса
LPSERVICE_STATUS IpServiceStatus // адрес структуры
SERVICE_STATUS
);
• В случае успешного завершения функция возвращает ненулевое
значение, а в случае неудачи — false.
• В параметре hservice должен быть установлен дескриптор сервиса,
который предварительно получен функцией CreateService ИЛИ
OpenService.

27.

Определение и изменение состояния
сервиса
• Параметр ipservicestatus должен указывать на структуру типа service_status, которая
содержит информацию о состоянии сервиса.
• Эта структура имеет следующий формат:
typedef struct _SERVICE_STATUS {
DWORD dwServiceType; // тип сервиса
DWORD dwCurrentState; // текущее состояние сервиса
DWORD dwControlsAccepted; // допускаемое управление
DWORD dwWin32ExitCode; // код возврата для Win32
DWORD dwServiceSpecificExitCode; // специфический код возврата сервиса
DWORD dwCheckPoint; // контрольная точка
DWORD dwWaitHint; // период ожидания команды управления
}
SERVICE_STATUS, * LPSERVICE_STATUS;

28.

Определение и изменение конфигурации
сервиса
• Под конфигурацией сервиса понимается информация, которая описывает
внутреннее и внешнее имена сервиса, режимы его запуска и работы.
• Для определения конфигурации сервиса используется функция
QueryServiceConfig, которая имеет следующий прототип:
BOOL QueryServiсеСonfig(
SC_HANDLE hService, // дескриптор сервиса
LPQUERY_SERVICE_CONFIG IpServiceConfig, // указатель на буфер
DWORD dwBufferSize, // размер буфера
LPDWORD lpdwBytesNeeded // необходимая длина буфера
);
• В случае успешного завершения функция возвращает ненулевое значение, а
в случае неудачи — false.

29.

Определение и изменение конфигурации
сервиса
• Параметр hService должен содержать дескриптор сервиса,
который был предварительно открыт при помощи функции
CreateService или OpenService.
• Параметр IpServiceConf ig должен указывать на область памяти, в
которую функция поместит информацию о конфигурации сервиса
в случае своего успешного завершения.

30.

Определение и изменение конфигурации
сервиса
• Эта информация будет храниться в формате, который определяется следующей структурой:
typedef struct _QUERY_SERVICE_CONFIG {
DWORD dwServiceType; // тип сервиса
DWORD dwStartType; // режим запуска сервиса
DWORD dwErrorControl; // режим обработки ошибок при запуске
LPSTR lpBinaryPathName; // имя бинарного файла сервиса
LPSTR lpLoadOrderGroup; // информация о порядке загрузки сервисов
DWORD dwTagld; // тег сервиса в группе загрузки
LPSTR lpDependencies ; // информация о порядке запуска сервисов
LPSTR IpServiceStartName// имя пользователя, который запускает сервис
LPSTR lpDisplayName; // имя сервиса в окне управления сервисами
} QUERY_SERVICE_CONFIG, *LPQUERY_SERVICE_CONFIG;
• В поля этой структуры записываются те значения, которые были установлены в
соответствующих параметрах при вызовах функций CreateService И OpenService.

31.

• Для изменения конфигурации сервиса можно использовать функцию ChangeServiceConfig, которая имеет
следующий прототип:
BOOL ChangeServiceConfig(
SC_HANDLE hService, // дескриптор сервиса
DWORD dwServiceType, // тип сервиса
DWORD dwStartType, // режим запуска сервиса
DWORD dwErrorControl, // режим обработки ошибок при запуске
LPCSTR lpBinaryPathName, // имя бинарного файла сервиса
LPCSTR lpLoadOrderGroup, // информация о порядке загрузки сервисов
LPDWORD lpdwTagld, // тег сервиса в группе загрузки
LPCSTR lpDependencies, // информация о порядке запуска сервисов
LPCSTR IpServiceStartName, // имя пользователя
LPCSTR lpPassword, // пароль пользователя
LPCSTR lpDisplayName // имя сервиса в окне управления сервисами
);
• В случае удачного завершения функция возвращает ненулевое значение, а в случае неудачи — false.

32.

Определение имени сервиса
• Назовем внутренним именем сервиса то имя, под которым сервис хранится в базе
данных сервисов, а также используется менеджером сервисов для ссылок на этот
сервис.
• Узнать внутреннее имя сервиса можно при помощи функции GetServiceKeyName,
которая имеет следующий прототип:
BOOL GetServiceKeyName (
SC_HANDLE hSCManager, // дескриптор базы данных сервисов
LPCSTR lpDisplayName, // указатель на внешнее имя сервиса
LPSTR IpServiceName, // указатель на внутреннее имя сервиса
LPDWORD lpdwBuffer // размер памяти для внутреннего имени
);
• В случае успешного завершения функция возвращает ненулевое значение, а в
случае неудачи — false.

33.

Определение имени сервиса
• В параметре hscManager должен быть установлен дескриптор базы
данных сервисов, которая была предварительно открыта функцией
OpenSCManager.
• Параметр lpDispiayName должен указывать на строку с именем
сервиса, которое используется в окне управления сервисами,
вызываемом из Start | Settings | Control Panel | Administrative Tools |
Services (Пуск | Настройка j Панель управление | Администрирование
| Службы).
• Параметр lpServiceName должен указывать на область памяти, куда
функция запишет внутреннее имя сервиса, т. е. имя, которое
используется для ссылок менеджером сервисов.
• В случае удачного завершения функция записывает в эту память
внутреннее имя сервиса.

34.

Определение имени сервиса
• Параметр lpdwBuffer должен указывать на двойное слово, в
котором установлена длина области памяти, на которую
указывает параметр lpServiceName.
• Функция записывает по этому адресу фактическую длину
внутреннего имени сервиса, исключая завершающий пустой
символ.

35.

• Внешнее имя сервиса - то имя, которое используется для ссылок на сервис в
окне управления сервисами.
• Узнать внешнее имя сервиса можно с помощью функции
GetServiceDisplayName, которая имеет следующий прототип:
BOOL GetServiceDisplayName{
SC_HANDLE hSCManager, // дескриптор базы данных сервисов
LPCSTR IpServiceName, // указатель на внутреннее имя сервиса
LPSTR lpDisplayName, // указатель на внешнее имя сервиса
LPDWORD lpdwBuffer // размер памяти для внешнего имени
);
• В случае успешного завершения функция возвращает ненулевое значение, а
в случае неудачи — false.

36.

• В параметре hscManager должен быть установлен дескриптор базы
данных сервисов, которая была предварительно открыта функцией
OpenSCManager.
• Параметр IpServiceName должен указывать на строку с внутренним
именем сервиса, под которым сервис хранится в базе данных.
• Параметр lpDisplayName должен указывать на область памяти, куда
функция запишет внешнее имя сервиса, т. е. имя, которое
используется для ссылок на сервис в окне управления сервисами.
• Параметр lpdwBuffer должен указывать на переменную типа dword, в
котором установлена длина области памяти, на которую указывает
параметр lpDisplayName.
• Функция записывает по этому адресу фактическую длину внешнего
имени сервиса, исключая завершающий пустой символ.

37.

38.

Управление сервисом
• Приложение может послать сервису управляющую команду, которая будет
передана обработчику управляющих команд сервиса.
• Для этой цели используется функция controiservice, которая имеет
следующий прототип:
BOOL Controiservice(
SC_HANDLE hService, // дескриптор сервиса
DWORD dwControl, // управляющий код
LPSERVICE_STATUS IpServiceStatus // адрес структуры
SERVICE_STATUS
);
• В случае успешного завершения функция controiservice возвращает
ненулевое значение, а в случае неудачи — false.

39.

Управление сервисом
• Параметр hservice должен содержать дескриптор сервиса,
который был предварительно получен вызовом функции
CreateService или OpenService.
• Параметр dwcontroi должен содержать код управляющей
команды, который может принимать одно из следующих
значений:
• service_control_stop — остановить сервис;
• service_control_pause — приостановить сервис;
• SERVICE_CONTROL_CONTINUE — ВОЗОбнОВИТЬ СерВИС,
• SERVICE_CONTROL_INTERROGATE — обновить состояние сервиса.

40.

Управление сервисом
• Сервису можно послать управляющие команды, коды которых
заданы пользователем.
• Для задания таких команд зарезервирован диапазон от 128 до
255.
• Сервис может обрабатывать такие команды только в том случае,
если при создании сервиса в параметре dwDesiredAccess
установлен флаг SERVICE_USER_DEFINED_CONTROL.

41.

42.

Удаление сервисов
• Для удаления сервиса из базы данных сервисов служит функция
DeleteService, которая имеет следующий прототип:
• BOOL DeleteService(
• SC_HANDLE hService // дескриптор сервиса
• В случае успешного завершения функция возвращает ненулевое
значение, а в случае неудачи — false.
• Единственный параметр hservice этой функции должен содержать
дескриптор сервиса, который был предварительно получен вызовом
функции CreateService или OpenService.
• Если сервис работает, то он не удаляется из базы данных до тех пор,
пока не будет остановлен.

43.

Блокирование базы данных сервисов
• Для получения монопольного доступа к базе данных сервисов процесс может
заблокировать доступ других процессов к этой базе данных, используя функцию
LockserviceDatabase, которая имеет следующий прототип:
• SC_LOCK LockserviceDatabase(
• SC_HMIDLE hSCManager // дескриптор базы данных сервисов
• );
• В случае успешного завершения функция возвращает дескриптор блокирования
базы данных сервисов, а в случае неудачи — null.
• Единственный параметр hSCManager этой функции должен содержать дескриптор
базы данных сервисов, доступ к которой блокируется функцией.
• Эта база данных сервисов должна быть предварительно открыта для доступа
вызовом функции openscManager.
• Причем база данных сервисов должна быть открыта С КЛЮЧОМ
SC_MANAGER_LOCK.

44.

Блокирование базы данных сервисов
• Для разблокирования доступа к базе данных сервисов используется
функция unockserviceDatabase, которая имеет следующий прототип:
• BOOL UnlockServiceDatabase(
• SC_LOCK hSCLock // дескриптор блокирования базы данных сервисов );
• В случае успешного завершения функция возвратит ненулевое
значение, а в случае неудачи — false.
• Единственный параметр этой функции должен содержать дескриптор
блокирования базы данных сервисов, который был предварительно
получен вызовом функции LockserviceDatabase.

45.

Блокирование базы данных сервисов
• Для того чтобы узнать заблокирована база данных сервисов или нет, используется
функция QueryServiceLockstatus, которая имеет следующий прототип:
BOOL QueryServiceLockstatus(
SC_HANDLE hSCManager, // дескриптор базы данных сервисов
LPQUERY_SERVICE_LOCK_STATUS IpLockstatus, // информация о блокировке
DWORD dwBufSize, // размер буфера для информации
LPDWORD lpdwBytesNeeded // количество необходимых байтов
);
• В случае успешного завершения функция возвращает ненулевое значение, а в
случае неудачи — false.
• Параметр hscManager должен содержать дескриптор базы данных сервисов,
который был предварительно получен вызовом функции OpenSCManager.
• Причем база данных сервисов должна быть открыта с ключом
SC_MANAGER_QUERY_LOCK_STATUS.

46.

Блокирование базы данных сервисов
• Параметр IpLockstatus должен указывать на область памяти, куда
система поместит информацию о блокировании базы данных
сервисов.
• Эта информация будет записана в структуру типа
query_service_lock_status, которая имеет следующий формат:
typedef Struct _QUERY_SERVICE_LOCK_STATUSA {
DWORD flsLocked; // состояние блокировки
LPSTR lpLockOwner; // владелец блокировки
DWORD dwLockDuration; // время блокировки
}
QUERY_SERVICE_LOCK_STATUS, *LPQUERY_SERVICE_LOCK_STATUS;

47.

Блокирование базы данных сервисов
• В поле flsLocked хранится состояние блокировки базы данных
сервисов.
• Если значение этого поля равно нулю, то база данных не
заблокирована.
• В противном случае база данных заблокирована.
• Если база данных сервисов заблокирована, то в поле lpLockOwner
записывается адрес строки, которая содержит имя пользователя,
заблокировавшего базу данных.
• Если база данных сервисов заблокирована, то в поле dwLockDuration
хранится целое число без знака, которое определяет
продолжительность блокирования базы данных в секундах.

48.

Блокирование базы данных сервисов
• В параметре dwBufsize должен быть установлен размер памяти,
на которую указывает параметр ipLockstatus.
• Параметр lpdwBytesNeeded должен указывать на двойное слово,
в которое функция в случае своего неудачного завершения
поместит необходимый размер буфера для информации о
блокировании базы данных сервисов.
English     Русский Правила