c++python-3.xwinapiwindows-servicesboost-python

How do I initialize python when running a program as a service in Windows?


I am creating a service based on SFL (Service Framework Library). When I work from the console, it is okay, but when I run it as a service, via "sc start MyService", it issues fatal errors when calling Py_Initialize and the like.

When the program crashes, the following line is sent to the debugger:

Fatal Python error: failed to get the Python codec of the filesystem encoding

sc query MyService outputs this:

SERVICE_NAME: MyService 
TYPE : 10
WIN32_OWN_PROCESS STATE : 1
STOPPED WIN32_EXIT_CODE : 1077 (0x435)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
#include "SflBase.h"
namespace nsSFL                                                                             
{                                                                                           
    const CMyService* SflGetServiceApp(void)                                                         
    {                                                                                       
        static T theApp;                                                                    
        return &theApp;                                                                     
    }                                                                                       
}                                                                                           
int _tmain( int argc, LPTSTR* argv )                                                        
{            
    CMyService* pApp = const_cast<CMyService*>( SFL_NS SflGetServiceApp() );                                  
    nsSFL::CServiceRoot* pServiceMap[] = {                                                  
       nsSFL::CServiceProxyT<CMyService, 0>::Construct2((TService*)NULL, L"CMyService")                                                                          
    };                                                                                      
    int retMain = -1;
    if(pApp->PreMain(argc, argv))
        retMain = pApp->Main( argc, argv, pServiceMap, ARRAYSIZE(pServiceMap));
    pApp->PostMain();
    return retMain;
}
BOOL CMyService::PreMain(int argc, LPTSTR* argv)
{   
    if (!InitInstance(argc, argv))
    {
        return FALSE;
    }
    return TRUE;
}
BOOL CMyService::InitInstance(DWORD argc, LPTSTR* lpszArgv)
{
    if (!AfxWinInit(::GetModuleHandle(nullptr), nullptr, ::GetCommandLine(), 0))
    {
        return FALSE;
    }
    {
        PyPreConfig preconfig;
        PyPreConfig_InitIsolatedConfig(&preconfig);
        preconfig.utf8_mode = true;

        status = Py_PreInitialize(&preconfig);
        pystatus_exit_on_error(status);
    }

    // auto pythonPath = L"%localappdata%\\Programs\\Python\\Python38\\python.exe";
    auto pythonPath = L"C:\\Program Files\\Python38\\python.exe";
    Py_SetPythonHome(pythonPath);
    Py_Initialize(); // print "Fatal Python error" only then work as service
}

Project Properties: C++20, SDK Windows 10.0.20348.0, Visual Studio 2022 (v143), python 3.8, all is x64, boost 1.77

According to this question I tried to unset PYTHONPATH and PYTHONHOME, but it was not available in the system, so I set these variables (error remained), and unset them from the environment variables (error remained).

According to that question I tried to assign Py_SetPythonHome, but not the path to "C:\Program Files\Python38\python.exe ", nor "%localappdata%\Programs\Python\Python38\python.exe" did not affect the error in any way. Using PyConfig_InitIsolatedConfig/PyConfig_InitPythonConfig also did not solve the problem.

I think the problem is related to some limitations of LocalSystem, although NetworkService refuses to start at all and LocalService is not suitable since I need to work with ports. Maybe I should set up some kind of access rights but I have no idea how or even where to find information.


Solution

  • I decided to try to fill in all the text variables of the configurator and this turned out to be the answer:

    wchar_t pythonPath[MAX_PATH * 3];
    wchar_t pythonExe[MAX_PATH];
    wchar_t pythonHome[] = L"%localappdata%\\Programs\\Python\\Python38"; // L"C:\\Program Files\\Python38";
    
    (void)swprintf_s(pythonPath, L"%s\\Lib;%s\\DLLs;%s\\Lib\\site-packages", pythonHome, pythonHome, pythonHome);
    PathCombineW(pythonExe, pythonHome, L"python.exe");
    
    PyConfig config;
    PyConfig_InitPythonConfig(&config);
    PyConfig_SetString(&config, &config.executable, pythonExe);
    PyConfig_SetString(&config, &config.home, pythonHome);
    PyConfig_SetString(&config, &config.pythonpath_env, pythonPath);
    
    PyStatus status = Py_InitializeFromConfig(&config);
    if (PyStatus_Exception(status) || !Py_IsInitialized()) 
    {
        CMyService::LogPythonError(L"Python initialization failed");
        CMyService::LogTextEvent(pythonPath);
        PyConfig_Clear(&config);
        return false;
    }
    
    PyConfig_Clear(&config);