Python: advapi32.SetServiceStatus () не работает с ошибкой 6

Первоначальный вопрос сейчас решен, большое спасибо eryksun.

Ниже приведен исправленный код. Теперь у меня другая проблема, о которой я спрошу в другом потоке, если я не смогу ее понять.

Ошибка 6 - недопустимый дескриптор, однако дескриптор в порядке, я считаю, что ошибка исходит из второго параметра.

        status = advapi32.SetServiceStatus(g_hServiceStatus, pointer(m_oServiceStatus))
        if 0 == status:
            dwStatus = winKernel.GetLastError()

Примечание: если я сделаю указатель None, то он не откажет (но, очевидно, тоже не сделает ничего полезного).

python -V

Python 3.6.6

Фрагмент большего размера:

from ctypes import *
from ctypes.wintypes import *

winKernel = ctypes.WinDLL('kernel32', use_last_error=True)
advapi32 = ctypes.WinDLL('advapi32', use_last_error=True)

global g_ServiceName
g_ServiceName = "StatusMonitor"

global g_lpcstrServiceName
g_lpcstrServiceName = LPCSTR(b"StatusMonitor")

class _SERVICE_STATUS(Structure):
    _pack_ = 4
    _fields_ = [
        ("dwServiceType", DWORD),
        ("dwCurrentState", DWORD),
        ("dwControlsAccepted", DWORD),
        ("dwWin32ExitCode", DWORD),
        ("dwServiceSpecificExitCode", DWORD),
        ("dwCheckPoint", DWORD),
        ("dwWaitHint", DWORD)
        ]

LPSERVICE_STATUS = POINTER(_SERVICE_STATUS)


global m_oServiceStatus
m_oServiceStatus = _SERVICE_STATUS(0, 0, 0, 0, 0, 0, 0)

global g_hServiceStatus
g_hServiceStatus = SERVICE_STATUS_HANDLE(None)

<lots of code snipped>

def status_report(dwCurrentState, dwWin32ExitCode, dwWaitHint):
    global g_dwCheckPoint
    global g_isService

    try:

        # Fill in the SERVICE_STATUS structure.

        m_oServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS
        m_oServiceStatus.dwCurrentState = dwCurrentState
        m_oServiceStatus.dwWin32ExitCode = dwWin32ExitCode
        m_oServiceStatus.dwWaitHint = dwWaitHint

        if dwCurrentState == SERVICE_START_PENDING:
            m_oServiceStatus.dwControlsAccepted = 0
        else:
            m_oServiceStatus.dwControlsAccepted = 1

        if (dwCurrentState == SERVICE_STOPPED) or (dwCurrentState == SERVICE_RUNNING):
            m_oServiceStatus.dwCheckPoint = 0
        else:
            g_dwCheckPoint += 1
            m_oServiceStatus.dwCheckPoint = g_dwCheckPoint

            status = advapi32.SetServiceStatus(g_hServiceStatus, pointer(m_oServiceStatus))
            if 0 == status:
                dwStatus = winKernel.GetLastError()
                #logging.info("SetServiceStatus(" + str(g_hServiceStatus) + ", status = " + str(dwStatus) + ")")

        logging.info("status_report(" + str(g_hServiceStatus) + ", " + str(dwCurrentState) + ", " + str(dwWin32ExitCode) + ", " + str(dwWaitHint) + ")")

    dwStatus = None
    if g_isService:
        # Report the status of the service to the SCM.
        ptrServiceStatus = LPSERVICE_STATUS(m_oServiceStatus)
        logging.info("m_oServiceStatus struct: " + str(m_oServiceStatus) + ", ref: " + str(byref(m_oServiceStatus)))
        logging.info("                         " + "ptr: " + str(str(pointer(m_oServiceStatus))) + " PTR: " + str(ptrServiceStatus))

        advapi32.SetServiceStatus.restype = BOOL
        advapi32.SetServiceStatus.argtypes = [SERVICE_STATUS_HANDLE, LPSERVICE_STATUS]
        status = advapi32.SetServiceStatus(g_hServiceStatus, ptrServiceStatus)
        if 0 == status:
            dwStatus = ctypes.get_last_error()

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        logging.error("status_report " + str(e) + " line: " + str(exc_tb.tb_lineno))

    return dwStatus


    advapi32.RegisterServiceCtrlHandlerExA.restype = SERVICE_STATUS_HANDLE
    advapi32.RegisterServiceCtrlHandlerExA.argtypes = [LPCSTR, LPHANDLER_FUNCTION_EX, LPVOID]
    g_hServiceStatus = advapi32.RegisterServiceCtrlHandlerExA(g_lpcstrServiceName, LPHANDLER_FUNCTION_EX(svc_control_handler_ex), LPVOID(None))
    logging.info("control handler " + str(g_hServiceStatus))

    logging.info("control handler called count " + str(g_nServiceControlHandlerCalled))

    m_oServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    m_oServiceStatus.dwServiceSpecificExitCode = 0;

    # set the service state as pending
    dwStatus = status_report(SERVICE_START_PENDING, NO_ERROR, 3000);
    logging.info("service_main: status_report(" + str(g_hServiceStatus) + "), status = " + str(dwStatus))
    log_service_status(m_oServiceStatus)

Обновленный результат регистрации:

INFO    service_start
INFO    service_start: StopEventHandle 952
INFO    service_main called JimsStatusMonitor control handler called count 0
INFO    control handler 2787686645712
INFO    control handler called count 0
INFO    status_report(2787686645712, 2, 0, 3000)128
INFO    m_oServiceStatus struct: <__main__._SERVICE_STATUS object at 0x000002890FC666C8>, ref: <cparam 'P' (000002890FCA8A30)>
INFO                             ptr: <__main__.LP__SERVICE_STATUS object at 0x000002890FC66848> PTR: <__main__.LP__SERVICE_STATUS object at 0x000002890FC66648>
INFO    service_main: status_report(2787686645712), status=None
INFO        16, 2, 0, 0, 0, 1, 3000

Должно быть, я упускаю что-то очевидное, но я этого не вижу. Я пробовал использовать другую структуру пакет, но без улучшений.

Я также пробовал использовать byref () вместо pointer () и просто передавать структуру, но ни один из них не работал. Я считаю, что использование pointer () здесь правильно, потому что есть другой API для установки таблицы диспетчеризации, которая работает с помощью pointer ().

Обратите внимание, что я специально использую FFI для этого, так как обнаружил, что существующие пакеты не соответствуют тому, что я пытаюсь сделать. Это решение Python основано на написанном мною решении C++, которое работает, мне просто нужно понять любой нюанс FFI, который вызывает его сбой.

Я должен добавить, что на данный момент служба действительно работает, я просто не могу перевести ее из состояния запуска из-за этой проблемы.

Надеюсь, кто-нибудь скажет мне, что я делаю не так?

Заранее спасибо, -Дэйв

Используйте advapi32 = ctypes.WinDLL('advapi32', use_last_error=True) и ctypes.get_last_error() вместо GetLastError(). Так надежнее.

Eryk Sun 23.11.2018 02:22

Предполагая, что SERVICE_STATUS_HANDLE является типом непрозрачного указателя, вы должны установить advapi32.RegisterServiceCtrlHandlerExA.restype = SERVICE_STATUS_HANDLE. Если это простой указатель ctypes (например, c_void_p), вы должны либо вручную обернуть результат как экземпляр указателя (например, result = c_void_p(result)), чтобы обойти автоматическое целочисленное преобразование, либо установить argtypes для всех функций, использующих указатель. Тем не менее, вы в любом случае должны установить полный прототип для всех функций FFI. Типовая безопасность чрезвычайно важна для кода ctypes.

Eryk Sun 23.11.2018 02:25

Кроме того, бессмысленно устанавливать g_hServiceStatus = SERVICE_STATUS_HANDLE(None) и впоследствии назначать g_hServiceStatus = advapi32.RegisterServiceCtrlHandlerExA(...) в той же области. Также излишне объявлять global g_hServiceStatus в области модуля; у них одна и та же сфера действия.

Eryk Sun 23.11.2018 02:31

SERVICE_STATUS_HANDLE определен в wintypes.py, поэтому я бы не подумал, что мне нужно будет что-то делать с этим? Я только определил то, чего там нет. Пробую некоторые ваши предложения, буду обновлять ...

crusty-dave 23.11.2018 03:26
ctypes.wintypes.SERVICE_STATUS_HANDLE - это псевдоним ctypes.c_void_p. Это простой тип указателя, поэтому, когда он установлен как restype функции, возвращаемое значение автоматически преобразуется в целое число Python. Если вы передаете это целое число в функцию без установки типа в argtypes, по умолчанию выполняется преобразование в 32-битный c_int, который усекает верхнюю 32-битную часть значения указателя, если вы используете 64-битный Python. Точно так же, если вы вообще не устанавливаете restype, тип результата также по умолчанию будет 32-битным c_int.
Eryk Sun 23.11.2018 04:19

Только дескрипторы ядра (например, задание, процесс, поток, раздел, файл, событие, семафор) и пользовательские дескрипторы (например, HWND) гарантированно соответствуют 32-битному значению. Другие «дескрипторы», используемые в ОС, на самом деле являются указателями, такими как HMODULE для базового адреса загруженной DLL, и должны быть 64-битными значениями в 64-битном процессе.

Eryk Sun 23.11.2018 04:23

Спасибо за все комментарии! Я обновлю пример, сейчас он идет намного дальше. Думаю, у меня проблема с настройкой диспетчера, я опишу новую проблему, как только обновлю код.

crusty-dave 23.11.2018 04:50

eryksun, я хочу отметить это как ответ, но сам не хочу отвечать, как мне отдать вам должное?

crusty-dave 23.11.2018 05:21

Я бы написал ответ, если бы у меня было что сказать по этому поводу. Но я и раньше отвечал на подобные вопросы. После того, как вы исправили свой код и получили некоторое представление о проблеме, найдите время для поиска похожих / повторяющихся вопросов.

Eryk Sun 23.11.2018 06:09

Я провел день в поисках, я просто искал не то. Еще раз спасибо!

crusty-dave 23.11.2018 06:37
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
10
218
1

Ответы 1

Благодаря огромной благодарности eryksun, мне удалось решить исходную проблему.

Основная проблема заключалась в том, что я предполагал, что API-интерфейсы Windows полностью определены, потому что казалось, что они работают без определения restype и argstype.

Потребовалось следующее:

advapi32.RegisterServiceCtrlHandlerExA.restype = SERVICE_STATUS_HANDLE
advapi32.RegisterServiceCtrlHandlerExA.argtypes = [LPCSTR, LPHANDLER_FUNCTION_EX, LPVOID]
g_hServiceStatus = advapi32.RegisterServiceCtrlHandlerExA(g_lpcstrServiceName, LPHANDLER_FUNCTION_EX(svc_control_handler_ex), LPVOID(None))

advapi32.SetServiceStatus.restype = BOOL
advapi32.SetServiceStatus.argtypes = [SERVICE_STATUS_HANDLE, LPSERVICE_STATUS]
status = advapi32.SetServiceStatus(g_hServiceStatus, ptrServiceStatus)

После того, как они были определены правильно, остались еще две проблемы, которые я смог выяснить из документации.

Во-первых, я пропустил, что restype является первым аргументом WINFUNCTYPE (), учитывая ответы от eryksun, это было более очевидно для меня, и это объясняло, почему мое определение для моего service_main () не работает должным образом.

Второй был немного более тонким и находится в конце документации обратного вызова здесь:

Important note for callback functions:

Make sure you keep references to CFUNCTYPE objects as long as they are used from C code. ctypes doesn't, and if you don't, they may be garbage collected, crashing your program when a callback is made.

Обратите внимание, что исходный код, который давал сбой, можно найти на форуме Python здесь.

Другие вопросы по теме